alizharb/laravel-hooks
Laravel Hooks provides a simple action/filter system for Laravel apps, letting you register and run hooks to extend packages and application code without modifying the original source. Ideal for modular, maintainable customizations.
Assessment scope: AlizHarb/laravel-hooks v1.2.0 (upgrade from v1.1.0 baseline; no prior internal assessment exists, so this establishes the initial position). Release is versioned as a minor bump but self-described as a "major update" — treat semver signalling as unreliable and pin explicitly.
Verdict: Conditional adopt. Strong fit for decoupled UI/commerce extensibility; do not enable Dynamic Eloquent Extensions in core domain models during the first cycle.
Architecture fit — Good, with a caveat on scope creep
#[DynamicRelation], #[DynamicCast], #[DynamicScope], Hook::model(User::class), HasDynamicHooks) shift the package from decoration to runtime model mutation. This is architecturally invasive: model behaviour would no longer be fully derivable from source. Acceptable for plugin/module ecosystems; risky for a system of record.Integration feasibility — High
laravel-modular discovery is neutral-to-positive: it costs us nothing if we don't run that ecosystem, and it means the maintainer's roadmap is oriented toward multi-module/plugin architectures rather than single-app convenience.HasDynamicHooks) is precisely what static analysis struggles to prove safe.@hook('name', 'fallback.view') is a clean, low-risk entry point; adoptable in a single sprint with no schema or API surface change.Technical risk — Moderate overall; High if dynamic extensions are used on core models
| Risk | Severity | Notes |
|---|---|---|
| Runtime model mutation obscures behaviour | High | #[DynamicRelation]/#[DynamicScope] make relations non-discoverable via static reading; onboarding and debugging cost rises. |
| Circuit breaker masks silent failures | High | Automatic silencing is a resilience win and an observability liability — a muted listener returns "success" with no effect. Must be paired with telemetry/alerting before enabling in production paths. |
| Metadata caching across deploys | Medium | "Refined caching logic for dynamic model metadata" is unverifiable from release notes; stale caches after deploy are a classic failure mode for discovery-based engines. |
| Long-running process state (Octane/Swoole/queue workers) | Medium | Per-request dynamic extension of models raises state-leak questions. Unconfirmed in notes — blocking question. |
| Bus factor / maintenance continuity | Medium–High | Single-maintainer OSS repo; enterprise-critical paths depending on it need a fork or vendoring contingency. |
| Semver discipline | Low–Medium | "Major update" shipped as 1.2.0. Pin exact versions; do not float ^1.2. |
| Static analysis / IDE ergonomics | Low–Medium | Dynamic methods and attributes degrade autocomplete and PHPStan coverage at the call site; team-level discipline required. |
Key questions to raise with the maintainer (blocking before Phase 2)
Stack fit — Strong for the hook bus, conditional for model extension
@hook(..., 'fallback.view') maps directly onto our existing concern of "optional widget renders nothing / renders broken". Adopt early — lowest risk, immediate UX benefit.Migration path — Incremental, four phases, each independently reversible
1.2.0 exactly in composer.json. Add to the dependency register with an owner and a fork contingency note. No production usage.Hook::model() / #[DynamicRelation] to a single non-critical, low-traffic model. Require a written internal ADR and code-owner review for any addition to core domain models. Re-evaluate at the next major.Compatibility considerations
laravel-modular, laravel-themer, filament-modular-luncher, modular-js are ecosystem companions we do not currently run. Confirm they are optional (no transitive hard dependency) before install; if they are pulled in, re-run the dependency review.Sequencing rationale Value is front-loaded in the low-risk features (Blade fallbacks, DTOs, circuit breaker) and back-loaded in the high-risk one (dynamic model extensions). Ship in that order so the team builds operational muscle — and a telemetry baseline — before touching anything that reshapes Eloquent.
Maintenance
Support
Scaling
Failure modes and mitigations
| Failure mode | Impact | Mitigation |
|---|---|---|
| Circuit breaker silences a listener silently | Business logic skipped with no error — worst-case "silent wrong answer" | Alert on open/silence events; fail-loud in staging; periodic reconciliation checks |
| Third-party listener throws in request path | 5xx on critical paths | Circuit breaker + async/queued listeners for non-blocking work |
| Stale dynamic-model metadata after deploy | Missing relations/attributes → runtime errors | Explicit cache-warm/clear step in deploy runbook; verify on first post-deploy smoke test |
| Dynamic scope shadows a security-relevant query constraint | Data exposure | Ban dynamic extensions on models with authorization scopes; code-owner review gate |
| Type-hinted DTO mismatch on listener signature | Hook dispatch errors at runtime, not build time | Contract tests per hook; register hooks in boot with smoke assertions |
| Upstream abandonment or breaking 1.x change | Rework on critical paths | Exact pin + vendored fork at Phase 2 |
| Octane/queue state bleed across requests | Cross-tenant leakage — severe | Block Phase 3 until proven request-scoped with a test |
Ramp-up
Recommendation: Approve Phases 0–2 now (bounded, reversible, clear value). Hold Phase 3 pending answers to the state-scoping, cache-invalidation, and circuit-breaker-observability questions, and re-assess at v1.3 or the next genuinely breaking release.
How can I help you explore Laravel packages today?