New Implement lazy loading objects in PHP 8.4

Video Lesson: Implement lazy loading objects in PHP 8.4

Lesson Content

PHP 8.4 introduces native lazy loading for objects. This feature delays the instantiation of an object until you actually use it. This can dramatically improve both the loading performance and memory utilization of expensive objects.

Lazy loading objects really shines in dependency injection containers. Your app may define a few dozen services, but a request may only use a few of them. By lazy-loading services, you can avoid initializing dependencies you don't actually use.

Let's look at a practical example. Here's a class with an intentionally expensive constructor:

<?php

declare(strict_types=1);

readonly class Product
{
    public function __construct(
        private int $id,
        private string $name,
        private float $price,
        private array $details = [],
    ) {
        // Simulating an expensive operation
        // Like fetching product details from a database
        sleep(2);
    }

    public function getName(): string
    {
        return $this->name;
    }
}

The 2-second delay simulates expensive operations like database queries or complex calculations. Without lazy loading, you'd pay this cost immediately when creating the object.

With PHP 8.4's lazy loading, we can defer this cost using the Reflection API:

<?php

require_once 'Product.php';

// Create a reflection of our class
$reflector = new ReflectionClass(Product::class);

// Create a lazy "ghost" object
$product = $reflector->newLazyGhost(function(Product $product) {
    // This initializer runs only when first accessed
    $product->__construct(123, 'Widget', 19.99);
});

// The Product exists but hasn't been initialized yet

// Now when we access a method, initialization happens automatically
echo $product->getName().PHP_EOL; // Now we wait for the delay then get the name

The magic happens in the initializer function. It only executes when you first access a property or method. If you never use the object, you never pay the initialization cost. This pattern works exceptionally well in data-heavy applications.

You can check if an object is still uninitialized by calling it’s isUninitializedLazyObject() method:

if ($reflector->isUninitializedLazyObject($product)) {
    echo "Product is not initialized yet".PHP_EOL;
} else {
    echo "Product is initialized".PHP_EOL;
}

The benefits of these ghost objects are multiplied in large applications. You'll see reduced memory usage, faster startup times, and better overall performance β€” especially when dealing with complex objects or expensive database ops.