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

Lazy Services Bundle Laravel Package

matthiasnoback/lazy-services-bundle

A Symfony bundle for declaring lazy services that are only instantiated when first used. Lightweight proxies stand in for real services, cutting boot-time overhead and memory use in apps with heavy dependencies, without changing how services are wired or consumed.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first. This bundle predates — and largely duplicates — native Symfony lazy services. Before installing anything, try lazy: true on the service definition (Symfony ≥2.3) or #[Autowire(lazy: true)] on a constructor argument (Symfony ≥6.2). Install this bundle only if you are on an old Symfony (2.x/3.x) codebase and specifically need to make constructor arguments of services you don't own lazy. That is the one gap the bundle fills.

Minimal steps (legacy Symfony 2/3 project):

composer require matthiasnoback/lazy-services-bundle
// app/AppKernel.php
$bundles = [
    // ...
    new MatthiasNoback\LazyServicesBundle\LazyServicesBundle(),
];
# app/config/config.yml  (key names per the bundle README)
lazy_services:
    services:
        - app.mailer          # whole service becomes a proxy
    arguments:
        app.report_generator: # only this service's argument is proxied
            - pdf_renderer

Then clear and warm the cache — proxies are generated at container compile time:

php app/console cache:clear --env=prod

Where to look first:

  1. The bundle's DependencyInjection/CompilerPass — it is the whole product; it rewrites Definition classes and/or wraps referenced arguments with generated ProxyManager value holders.
  2. The cache directory where generated proxy classes land (app/cache/.../lazy_services or similar) — this tells you what actually got proxied.
  3. php app/console container:debug <service_id> to confirm the service exists and what its class originally was.

First use case to prove it out: a controller or hot listener that injects a pdf_renderer/mailer/http_client which boots a subprocess or opens a socket in its constructor, but which is used on maybe 1 in 20 requests. Proxy it, hit a route that doesn't use it, and confirm (via XHProf/Blackfire or a var_dump in the real service's constructor) that the constructor no longer runs.

Implementation Patterns

Proxy the heavy collaborator, not the hot service. The classic win is a cheap service (ReportGenerator, NotificationListener) that is instantiated on every request and holds an expensive dependency (PdfRenderer, Swift_Mailer, Doctrine\DBAL\Connection). Mark the dependency lazy; the cheap service still gets built normally.

Lazy arguments are the escape hatch for third-party definitions. You cannot edit Definition objects registered by another bundle, but the bundle's compiler pass runs on the finished container, so you can proxy an argument of a vendor service from your own config. This is also the cleanest fix for a circular dependency that would otherwise require setter injection:

lazy_services:
    arguments:
        app.a:
            - app.b

Whole-service proxying for listeners and subscribers. Event listeners are constructed eagerly by EventDispatcher even if the event never fires. Proxying the listener class defers its constructor (and its own dependency graph) until handle() is actually called.

Console commands. Long-running commands that build a full stack of services on bin/console startup see a real wall-clock improvement from proxying the DB/HTTP layer.

Testing laziness explicitly. Don't trust the config. Add a test that asserts on construction:

class ReportGeneratorTest extends \PHPUnit_Framework_TestCase
{
    public function testPdfRendererIsNotInstantiatedEagerly()
    {
        $kernel = new \AppKernel('test', true);
        $kernel->boot();
        $container = $kernel->getContainer();

        // container must not have already built it
        $this->assertFalse($container->get('app.pdf_renderer', Container::NULL_ON_INVALID_REFERENCE) instanceof PdfRenderer
            && PdfRenderer::$constructed);
    }
}

A simpler production-side check: give the real service a public static $constructed = false; set to true in its constructor, and log it.

Migration posture. Treat this bundle as a stopgap. Every pattern above has a native equivalent today:

Bundle pattern Modern replacement
lazy_services.services: [...] lazy: true on the definition
lazy_services.arguments: [...] #[Autowire(lazy: true)] / lazy argument in services.yaml
ProxyManager value holders Symfony's LazyServiceInstantiator + ProxyManagerBridge
Circular dep via lazy Setter injection, service locator, or ServiceSubscriber

If you inherit a project using it, write an ADR and schedule removal.

Gotchas and Tips

  • Final classes and final public methods cannot be proxied. ProxyManager generates a subclass; final kills it. The same applies to classes with private constructors or static-only APIs.
  • get_class() lies. Once proxied, get_class($service) returns a generated ProxyManagerGeneratedProxy\__PM__\... name. Code doing get_class($x) === 'App\Service\Foo' breaks. instanceof and type hints are fine because the proxy extends the real class.
  • Generated proxies are container artifacts. They are produced during compilation and live in the cache dir. Change the config, then always cache:clear (or cache:warmup on deploy). Symptoms of forgetting: your newly-marked service is still eager, or after a class rename you get "class not found" from a stale proxy file.
  • Compiler pass ordering matters. The pass must run after all bundles have registered their definitions. If a service registered late is not proxied, check the pass registration priority in the bundle's extension (beforeOptimization vs optimization).
  • Factories and synthetic services. If a definition uses a factory that returns a final class, or is synthetic (e.g. request, doctrine.orm.entity_manager in some setups), proxying either silently no-ops or blows up at instantiation. Verify with container:debug --show-private.
  • Serialization surprises. Serializing a proxy serializes the generated class name and wrapped state. Anything that round-trips services through the cache (session storage of a service, messenger payloads, var_export'd config) will break on the proxy class.
  • Overhead per call. Every proxied method call goes through the initializer check. It is cheap but non-zero — don't lazy-load cheap value objects; you'll waste more on proxies than you save.
  • Debugging: container:debug shows the original class, not the proxy. That mismatch is the number one source of "why isn't it lazy?" confusion. Cross-reference against the generated files in the cache directory.
  • Unmaintained. No commits in many years, zero dependents. It targets the Symfony 2 ContainerBuilder API; the generated proxies use ProxyManager patterns that are deprecation-noisy on PHP 8 (dynamic properties, return types) and will not run unmodified on Symfony 5+. Pin your PHP/Symfony versions or vendor a fork.
  • Extension point is the compiler pass itself — if you need finer control (e.g. proxy only specific methods, or use interface-based proxies instead of class proxies) you are better off writing your own small pass against ocramius/proxy-manager than trying to configure this bundle.
  • Don't use it to paper over a design problem. If half your container is marked lazy to get boot time down, the real issue is a service doing work in its constructor or a god-object controller.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings