PHP 8.4 is out now for approximately 2 month and there are a lot of new features – including Lazy Objects. One significant new feature is “Lazy Objects”, which is described as follows:
A lazy object is an object whose initialization is deferred until its state is observed or modified.
The past few weeks, I read a couple of blogs addressing Lazy Objects in the context of Dependency Injection and how it works together to boost up performance. I would like to discuss a few confusions and clarify some terminology.
This topic will also be published as a video on my YouTube channel: https://www.youtube.com/@doganoo
I will go into the topic in more detail there. So don’t forget to follow so you don’t miss anything!

Brief Recap: What is Dependency Injection
Dependency Injection (DI) is a design pattern that promotes loose coupling between components in software systems. Instead of a class creating its own dependencies, these are injected from an external source. This means a class simply declares what it needs, and another part of the application (e.g., a Dependency Injection container) provides those dependencies.
By separating the creation of dependencies from their usage, DI offers several advantages:
- Improved Testability: Dependencies can be easily mocked or replaced during unit testing.
- Flexibility: Dependencies can be swapped out or configured differently without modifying the class itself.
- Maintainability: Changes in dependency implementation don’t require changes in dependent classes.
There are several ways to implement DI:
- Constructor Injection: Dependencies are passed into a class via its constructor.
- Setter Injection: Dependencies are assigned using setter methods.
- Method Injection: Dependencies are passed directly to methods that require them.
Lazy Loading vs. Lazy Initialization: Similar Terms, Different Concepts
While lazy loading and lazy initialization are often mentioned together, they refer to distinct concepts with different scopes and use cases. Understanding their differences is crucial to applying them effectively in your software design.
Lazy Initialization
Lazy initialization focuses on delaying the creation of an object until it is explicitly needed. It’s typically implemented at the object level.
- Use Case: Reducing resource usage for dependencies that might never be needed during the application’s lifecycle.
- Scope: Individual objects.
- Mechanism: Usually implemented with a simple null check or similar logic.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Container { private $services; public function get(string $name) { $service = $this->services[$name] ?? null; if ($service === null) { $service = new MyService(); $this->services[$name] = $service; } return $service; } } |
Here, the container is initializing MyService only when it is called the first time.
Lazy Loading
Lazy loading is a broader concept where entire resources or services are loaded or retrieved only when necessary. This is often implemented at the framework or system level, using techniques like proxies to defer the initialization of resources.
- Scope: Larger resources or dependencies, often managed externally.
- Mechanism: Proxies or intermediaries that abstract the actual resource and load it when accessed.
- Use Case: Optimizing performance in systems with heavy or rarely accessed dependencies.
In Dependency Injection, a service might be defined as “lazy,” meaning the DI container creates a proxy for the service and initializes the actual object only when you call a method on it.
1 2 | $lazyService = $container->get('HeavyService'); // Proxy created, not the actual service. $lazyService->performTask(); // The real service is initialized here. |
Key Differences
- Granularity: Lazy initialization applies to specific properties or objects, while lazy loading applies to entire resources or systems.
- Implementation: Lazy initialization often involves basic checks in code, while lazy loading leverages proxies or more advanced mechanisms.
- Responsibility: Lazy initialization is implemented by the class itself, while lazy loading is typically managed externally (e.g., by a framework or library).
By understanding these distinctions, developers can choose the right strategy for optimizing performance and managing resources in their applications. Whether you need lazy initialization for lightweight, local optimizations or lazy loading for system-wide efficiency, both are invaluable tools in modern software engineering.
Lazy Objects in PHP 8.4 introduce a native implementation of lazy initialization for entire objects, but their focus is fundamentally different from how DI containers handle lazy loading or property-level lazy initialization. Lazy Objects in PHP defer the initialization of the object itself until it is accessed, creating a lightweight “ghost” placeholder. This design allows for on-demand instantiation, aligning closely with the concept of lazy initialization at the object level but without addressing internal property-level delays or system-wide lazy loading.
In the context of Dependency Injection, PHP’s Lazy Objects can be used to optimize resource management by ensuring that injected services are not fully initialized until required. This is particularly useful for heavy dependencies that might not always be utilized. However, Lazy Objects are limited to this specific role of delaying object creation. They do not act as proxies for managing external resources or broader lazy loading across an application. DI containers often rely on more sophisticated proxy mechanisms to enable lazy loading, which can manage not just the initialization of services but also their interaction with external systems, databases, or APIs.
This distinction is critical: while PHP’s Lazy Objects excel in simplifying lazy initialization for complete objects, they are not designed to replace the complex lazy loading mechanisms provided by DI container libraries. Developers can combine the two approaches, using DI containers for managing system-wide lazy loading while leveraging Lazy Objects for targeted, native lazy initialization where appropriate. This combination can enhance the efficiency and flexibility of modern PHP applications.
Conclusion
Ok, this was a bit long, to be honest. But it was important to me to outline the differences, as many terms in the context of Dependency Injection is used intercharged. With the introduction of Lazy Objects in PHP 8.4, I feel like the confusion has only grown.
To summarize: Dependency Injection is a technique to loosely couple services and is an implementation of the Dependency Inversion Principle of SOLID. Lazy Initialization describes the way how services of a dependency container are initialized — namely up on request and Lazy Loading is the way how heavy services or operations are only loaded when needed, often using proxies.
If you liked what you read, please do not forget to leave a follow here and on YouTube, subscribe to my newsletter on my personal blog and see you next time.