PHP 8.4 introduces lazy objects, a feature designed to defer an object's initialization until it is accessed. This approach minimizes resource usage, especially for objects with heavy initialization logic that may only sometimes be used during execution.
What is the difference between a default object and a lazy one?
Lazy objects use a dynamically created proxy object to defer the execution of the target object’s constructor. Reflection is the key to making this proxy, which mimics the original class, allowing the actual object to remain uninitialized until it is accessed.
<?php
class MyClass
{
public function __construct(private int $foo)
{
echo 'Accessing a method triggers initialization'.PHP_EOL;
}
public function getFoo(): int
{
return $this->foo;
}
}
$object = new MyClass(1);
echo 'lazy object created'.PHP_EOL;
echo 'call lazy object '. $object->getFoo(). PHP_EOL;
Output:
Accessing a method triggers initialization
lazy object created
call lazy object 1
In this example, when an object is created, the MyClass constructor is immediately called, which displays “Access to a method triggers initialization”. The class has a private property $foo that is initialized at runtime.
Use a Lazy Object:
<?php
class MyClass
{
public function __construct(private int $foo)
{
echo 'Accessing a method triggers initialization'.PHP_EOL;
}
public function getFoo(): int
{
return $this->foo;
}
}
$initializer = static function (MyClass $ghost): void {
$ghost->__construct(123);
};
$reflector = new ReflectionClass(MyClass::class);
$object = $reflector->newLazyGhost($initializer);
echo 'lazy object created'.PHP_EOL;
echo 'call lazy object '. $object->getFoo(). PHP_EOL;
Output:
lazy object created
Accessing a method triggers initialization
call lazy object 123
In this code, the newLazyGhost method creates a lazy object. The initializer function is defined to initialize the object only when the method is called. The ReflectionClass object is responsible for creating a proxy object that is not a full instance of MyClass before calling getFoo().
When getFoo() is called, the constructor is launched (which displays “Accessing a method triggers initialization”), and the object is initialized to 123, as shown in the output.
Advantages of Lazy Objects
Improved Performance:
Delayed instantiation reduces memory usage and improves performance, especially when objects are accessed conditionally.
Transparent Proxying:
Developers interact with lazy objects in the same way as with regular objects, without any additional efforts to work with proxies.
Optimized Resource Management:
Useful in scenarios such as dependency injection containers, ORM systems, or API integrations where initialization can be deferred until needed.
Limitations
Serialization:
Lazy objects can make serialization and deserialization difficult due to their deferred state.
Debugging Complexity:
Stack traces can include proxy layers, making debugging slightly more complex.
Conclusion
Lazy objects in PHP 8.4 are a powerful tool for improving performance and resource efficiency in modern applications. By deferring initialization until necessary, they provide a cleaner and more scalable approach to managing complex object lifecycles.
To learn more about lazy objects, visit the official PHP RFC page.