Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Hook Bundle Laravel Package

scylabs/hook-bundle

scylabs/hook-bundle adds a lightweight hook system to PHP applications, letting you register callbacks at named hook points and trigger them to extend behavior without modifying core code. Ideal for modular, event-driven workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package; there is no Laravel service provider. The registration steps below are Symfony-specific.

composer require scylabs/hook-bundle

Register the bundle in config/bundles.php (or AppKernel.php on older Symfony):

return [
    // ...
    Scylabs\HookBundle\ScylabsHookBundle::class => ['all' => true],
];

Where to look first. Read the bundle's README.md end-to-end before touching code. With only 7 stars and very little community coverage, there is no Stack Overflow safety net, no blog posts, and no large install base to fall back on — the README and the Resources/config/services.xml file are the documentation. Skim the bundle's DependencyInjection/ folder to confirm the exact service-id prefix (scylabs_hook.*, scy_hook.*, etc.) — this is the number-one source of "why is my handler not firing" confusion.

If you are upgrading, inspect the v1.0.6 change first. The release note is only "Fixes Container injection", so the relevant code is almost certainly in DependencyInjection/ and the compiler pass / service wiring. That is the area to smoke-test before assuming your existing handlers still construct the same way.

First use case — a single extension point in a Twig template. The core idea of the bundle is that you expose a named hook inside a template, and other code (yours or a third party's) registers handlers that contribute markup to that hook. Minimal first pass:

  1. In a controller or Twig template, emit a hook point, e.g. {{ hook('product.show.sidebar') }} (check the actual Twig function name in the README — it is likely hook(), scylabs_hook(), or a tag).
  2. Create a handler service that renders a fragment and tag it so the bundle's compiler pass picks it up. On v1.0.6, constructor injection into that handler service should be the first thing you try, since container injection is the release's stated fix.
  3. Clear the cache (bin/console cache:clear) — the compiler pass only runs on a cold cache.
  4. Reload the page: your handler's output should appear at the hook point.

Get this single round-trip working before building anything else. If it renders, you understand the wiring; everything else is priorities and context passing.

Implementation Patterns

1. Tagged-service handlers (the primary pattern). In a Symfony bundle of this vintage, handlers are almost always registered as services with a tag, not as closures. Expect something like:

services:
    App\Hooks\ProductSidebarHook:
        tags:
            - { name: scylabs_hook.handler, hook: product.show.sidebar, priority: 10 }

Treat the tag name and option keys as fixed API. Wrap them in a small App\Hooks\HookTags constant class so that if the bundle's tag ever changes, you patch one file.

v1.0.6 explicitly fixes container injection, so constructor-injected handler services should be the default pattern. If you previously worked around injection failures by pulling dependencies from the container manually, retest after upgrading and remove the workaround if the handler now receives its dependencies normally.

2. Prioritised composition at a single hook. The real value of a hook bundle is that multiple bundles/features can contribute to the same extension point without knowing about each other. Model priorities explicitly:

  • priority: 0 — core contributions
  • priority: -10 — opt-in extras
  • priority: 100 — "replace everything" overrides

Document the priority bands your team uses in the project's docs/hooks.md. Because the bundle gives you no introspection tooling, this convention is the only way to keep ordering sane across teams.

3. Template-level extension points over full template overrides. The strongest day-to-day pattern is to add hook points to a base template (e.g. your layout.html.twig header/footer slots, or a component's action area) instead of forcing downstream projects to override the whole template. For a multi-site or multi-tenant app, this is your cleanest customisation seam: each tenant gets a small Twig file + a tag entry, and the base template never forks.

{# layout.html.twig #}
<header>
    {{ hook('layout.header.brand') }}
    {{ hook('layout.header.nav') }}
</header>

4. Functional hooks around controller/service logic. The "functional hooks" in the description usually means an event-dispatcher-backed hook where handlers receive a mutable context object. The pattern:

$context = $this->hooks->run('checkout.before_charge', ['order' => $order]);
if ($context->isStopped()) { return $this->redirectToRoute('checkout_blocked'); }

Wrap this in one app-wide facade service (App\Hook\Hooks) rather than injecting the bundle's service in 40 places — you keep the upgrade surface tiny and can swap in symfony/event-dispatcher later if the bundle dies.

5. Integration with Twig inheritance and ESI/caching. Hook-rendered fragments are typically not aware of the surrounding page's cache context. If you use Symfony HTTP cache or Varnish, call {{ hook(...) }} only inside fragments that are {{ render_esi(...) }}-wrapped, or mark the containing controller Cache-Control: private. Otherwise one tenant's hook output gets cached and served to another.

6. Migration safety net. Because the package has a very small API surface and a low release cadence, keep all direct references to Scylabs\HookBundle\* classes in a single directory (src/Hook/). If you ever need to fork the bundle, you replace that directory and the Twig function name in one place, and the rest of the app is untouched.

Gotchas and Tips

  • Stale compiler pass = invisible handlers. Service tags are only resolved at container compile time. After adding/editing a tags: entry, cache:clear — in dev, with APP_ENV=dev and a running cache:clear watcher, tags are NOT reloaded. This is the most common "my handler doesn't fire" cause.

  • Upgrade smoke test for v1.0.6. The release is specifically a container-injection fix. After upgrading, clear the cache and run bin/console debug:container --tag=scylabs_hook.handler (adjust the tag name to whatever services.xml actually uses) to confirm handlers are still registered. Then load a page that exercises each hook. Pay particular attention to handlers whose constructors take dependencies — that is the behaviour the release changes.

  • Twig template cache is separate from the container cache. If you edit a hook handler's Twig file and don't see changes, clear var/cache/*/twig. In production with the Twig cache warmers, hook templates are baked into the compiled cache just like any other template.

  • Do not over-read the new release as active maintenance. v1.0.6 fixes container injection, which is a real and useful bug fix, but it is still a narrow release from a bundle with low community coverage. It does not automatically prove Symfony 6/7 compatibility. The last pre-v1.0.6 release was 2020-12-31, and Symfony 6/7 container APIs have changed since then. Verify on your target Symfony version. Either (a) stay on a Symfony version you have tested, (b) vendor the bundle via VCS repository if you need to patch it, or (c) replace the hook mechanism with native Symfony EventDispatcher listeners plus a small Twig extension. If you cannot justify the maintenance risk, do not adopt it.

  • Hook names are strings with no validation. The bundle gives no autocomplete and no compile-time check that a handler's hook: matches an actual {{ hook(...) }} site. Mitigate with a const class of hook names shared between the Twig templates (via a Twig global) and the service tags. A stale handler targeting a removed hook silently does nothing.

  • Priorities collide silently. Two handlers with the same priority have undefined order (depends on the compiler pass sort, which is stable only within a single tag collection). Give each handler a unique priority number within a project, or accept that ordering is arbitrary and make handlers order-independent.

  • Context mutation and stopping propagation. If the functional hook supports stop/setReturn, an early-stopping handler from a third-party bundle can unexpectedly short-circuit the rest of your checkout/payment logic. Always wrap run() in try/finally or check the returned context's flag explicitly; never assume run() returns void.

  • Extension points inside bundles vs apps. If you ship this bundle's hooks from your bundle, your handlers must be registered in that bundle's Configuration/Extension (or Resources/config/services.xml), not the app's services.yaml. Otherwise consumers who disable your bundle still have handlers tagged that reference missing services.

  • Debugging. bin/console debug:container --tag=scylabs_hook.handler (adjust tag name to whatever services.xml actually uses) lists everything registered. Use debug:container <id> to confirm a specific handler's tags survived. Without this, the only debugging signal is "template renders nothing," which tells you nothing.

  • No test tooling. There is no documented test helper; a hook that renders a Twig fragment is best tested via a functional TestCase that hits a route and asserts the fragment's marker text. Don't try to unit-test handlers in isolation — they typically depend on Environment and the container.

  • Fork plan is cheap and worth having. The surface is small. If adoption grows, plan a half-day spike to reimplement: a TwigExtension with a hook() function backed by a tagged iterator plus an EventDispatcher for functional hooks. That removes the abandoned-dependency risk, gives you typed hook names, and costs less than debugging the bundle on newer Symfony versions.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource