markup/twig-extension-proxy-bundle
Symfony bundle that adds a Twig extension proxy, routing function, filter and test calls through one service so extensions can be decorated, lazy-loaded or overridden without touching your templates.
Architecture fit — poor, and the mismatch is structural rather than cosmetic. markup/twig-extension-proxy-bundle is a Symfony bundle: it registers through Symfony's Bundle class, relies on the DependencyInjection component's compiler passes and service tagging to discover Twig\Extension\ExtensionInterface services, and substitutes lazy proxy objects into the Twig environment so an extension's constructor is deferred until one of its filters/functions/tests is actually invoked. Laravel sits on a different kernel: it borrows Symfony components (HttpFoundation, Routing, Console, VarDumper, ErrorHandler) but does not use symfony/dependency-injection, symfony/http-kernel, or the Bundle system. There is no bundles.php, no compiler-pass pipeline, and no autoconfiguration-by-interface. The package cannot be composer required into a Laravel app and function.
Twig is also not the default templating layer. Laravel ships Blade. Twig only enters the picture via a third-party bridge (historically rcrowe/twigbridge), which has its own extension registration path through a TwigExtension service provider contract — it does not read Symfony bundle configuration. So even in a Twig-on-Laravel stack, this bundle's registration mechanism has nothing to hook into. The underlying idea (proxy the extension so expensive constructors don't run on every request) transfers cleanly; the implementation does not.
Integration feasibility — feasible only as a source-level port, not as a dependency. The proxy logic itself is small and conceptually simple; a TPM should treat this as "read ~a few hundred lines, reimplement against Laravel's container" rather than "evaluate a package for adoption." A DeferrableProvider-style or bind()-with-closure approach in a TwigServiceProvider gets most of the benefit with no third-party surface area.
Technical risk — high on maintenance, low on supply chain, moderate on correctness.
ExtensionInterface but forwards getFilters()/getFunctions() eagerly can invert the intended optimization; proxies that forward getName() incorrectly break Twig's extension registry on newer Twig versions where getName() was deprecated/removed (Twig 3.x). Version drift here is a live hazard.Key questions for the requesting team:
Stack fit: do not adopt the package; harvest the pattern. The realistic decision tree:
getFilters()/getFunctions()/getTests() return closures that resolve the real extension from the container on first call.Migration path. There is no in-place migration because there is no prior integration. The path is: (1) confirm the framework and templating layer; (2) if Laravel, snapshot the proxy classes from the repo at a pinned commit, vendor them under an internal namespace, and rewrite registration against Illuminate\Contracts\Container\Container; (3) add a regression test asserting that an unused extension's constructor is not called on a request that renders a template — this is the whole point of the change and the only meaningful acceptance criterion.
Compatibility. Check the bundle's composer.json for PHP and Symfony version constraints. Symfony's Bundle interface and ExtensionInterface have both changed across 5.x → 6.x → 7.x (return types, getName() removal). Any port must be re-targeted at the Twig version the bridge actually pulls in, and at PHP 8.x typing conventions. Also confirm the license travels with vendored code — MIT does, but keep the attribution header.
Sequencing.
Maintenance. Adoption means permanent ownership. With 0 stars and 0 dependents there is no upstream community to absorb Twig/Symfony version churn, no security triage, and no expectation of a response to issues. Budget a fork-maintenance line item: roughly a few hours per Twig major/minor bump to re-verify the proxy contract, plus the initial review. If the port is done properly (a handful of internal classes), this is cheap and contained; if the bundle is vendored wholesale, you inherit Symfony DI assumptions that will rot.
Support. No vendor, no SLA, no documented upgrade path. Internal escalation is "whoever ported it." That is acceptable for a small internal utility with a named owner and unacceptable as a silent dependency buried in a service provider. Assign an explicit owner and record the pinned source commit in the repo.
Scaling. The performance argument is per-request CPU, not horizontal scale — it reduces work on every request by skipping unused extension construction. The win is real but bounded by how expensive those constructors are; for typical extensions (a few array literals) it is noise. If the constructors do I/O or hit the container for heavy services, the win compounds and the change is worth it. Do not sell this as a scalability improvement; sell it as a latency/CPU micro-optimization with a measurable baseline or not at all.
Failure modes to watch.
getFilters() calls $this->extension->getFilters(), defeating the laziness. Easy to write, hard to spot in review.ExtensionInterface; the proxy doesn't implement it; templates using that construct fail at render. Mitigate with a contract test against the pinned Twig version in CI.services.php for bindings only. Any behavior that depended on compile-time proxy generation will not reproduce; verify in the Octane/queue/CLI contexts separately, since long-lived workers cache resolved instances differently than FPM.Ramp-up. Low for anyone comfortable with Laravel's container; moderate if the team needs to read Symfony DI idioms (compiler passes, service tags, autoconfiguration) to understand the source. The ported code itself is small enough for one engineer to own end-to-end.
Recommendation framing for the review: reject as a dependency; retain as a reference implementation if and only if profiling shows extension construction is a real per-request cost. The default disposition for a 0-star, 0-dependent Symfony bundle in a Laravel intake is "not applicable — confirm framework, then solve natively."
How can I help you explore Laravel packages today?