deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Symfony 8 leans on PHP 8.4 native lazy objects to defer service construction

A dev.to walkthrough explains how PHP 8.4's native lazy objects let Symfony 8 register container services as empty shells, deferring constructors and reportedly cutting boot time 15–25% in service-heavy apps.

Symfony 8 leans on PHP 8.4 native lazy objects to defer service construction

Lazy objects become a PHP 8.4 language primitive

According to a post on dev.to, the core change is that PHP 8.4 ships lazy objects as a native language feature. A lazy object exists from the moment it is created, but its constructor does not run and its dependencies are not resolved until the first real interaction — a property read, or a method call that touches the object's state.

The pattern itself is not new to PHP. As the post notes, Doctrine has used dynamically generated proxies for entities for years, and Symfony previously implemented lazy loading through the LazyGhostTrait and proxy classes generated at runtime by its dependency injection container. What changes with 8.4 is who does the work: the PHP runtime now handles lazy initialization itself, with no code generation or introspection overhead.

How Symfony 8 rewires its container

Symfony's dependency injection container is the heart of the framework, registering hundreds of services — router, template engine, security, repositories — while a single HTTP request uses only a fraction of them. Constructing all of them at boot is wasted work, the post argues.

With Symfony 8, services in the container are created as native PHP 8.4 lazy objects. The container registers each service, but its constructor runs only when a method or property of that service is actually called. Boot produces empty shells, and the real initialization cost is spread across the request lifecycle and paid only for services that are genuinely used.

A second consequence is the removal of generated proxies. Before PHP 8.4, Symfony generated proxy classes at runtime, stored them in the cache directory and loaded them through the autoloader. That approach worked but carried measurable costs: code generation on first execution, cache I/O, and autoloader overhead. With native lazy objects, the post says, that overhead disappears entirely — no additional classes, no proxy cache files, no autoloader.

The reported performance numbers

The figures come from the post's author rather than an official benchmark suite. A typical Symfony application with more than 200 registered services is said to see boot-time improvements in the 15–25% range, with potentially larger gains for applications built on heavy bundles such as Sonata or EasyAdmin. The author also points to serverless environments and frequently scaled containers, where cold start time is critical, as the cases where the improvement translates most directly into operational savings.

Two other PHP 8.4 features in the mix

The post also describes two related features it says Symfony 8 adopts.

Property hooks attach logic to property reads and writes directly in the property declaration. Unlike the __get and __set magic methods, they are typed, declarative and handled natively by the runtime. According to the post, Symfony 8 integrates them across several components: Form data transformers can be declared as hooks on the properties of the DTO behind a form, Serializer normalization logic can be co-located with object properties, Validator set-hooks can apply inline checks that complement the constraint system, and Doctrine entities can pre-process property values without separate lifecycle events or listeners.

Asymmetric visibility lets a property be publicly readable but privately writable — for instance public private(set) string $name — which removes boilerplate getters for properties that still need internal mutability. Symfony 8 reportedly uses this in value objects and DTOs.

Why it matters

The lazy-object change is the practical core of this story: it moves a widely used pattern from userland code generation into the language runtime, and Symfony's container — the component every request passes through — is the highest-leverage place to apply it. For service-heavy applications, faster boot means lower per-request overhead, and in serverless or autoscaling deployments it shortens cold starts, which feeds directly into infrastructure cost.

The caveat is sourcing. The 15–25% figure, the serverless framing and the component-level property hook integrations all come from a single dev.to blog post. The direction of the change is consistent with what PHP 8.4's lazy objects were designed to do, but teams weighing an upgrade should treat the numbers as indicative and measure boot time on their own service graphs.

  • #php
  • #symfony
  • #dependency-injection
  • #performance
  • #lazy-loading

Related posts