Can I use lazy loading with factories?
08:33 15 Dec 2025

I have some PHP-DI dependency definitions in my application which bind to factories producing PDO instances, for example:

$appConfig = [...];
$config1 = $appConfig['mysql'];
$config2 = $appConfig['mssql'];

$builder = new ContainerBuilder();
$containerBuilder->useAttributes(true);
$builder->addDefinitions([
    'mysql' => factory(fn(ContainerInterface $c) => new PDO('...', $config1['user'], $config1['pass'])),
    'mssql' => factory(fn(ContainerInterface $c) => new PDO('...', $config2['user'], $config2['pass'])),
]);

I want to lazy-load these PDO instances in the classes which use them so that the database connections are only established if the PDOs are used. Consider the following:

class MyClass
{
    private PDO $mysql;
    private PDO $mssql;

    #[Inject(['mysql', 'mssql'])]
    public function __construct(PDO $mysql, PDO $mssql)
    {
        $this->mysql = $mysql;
        $this->mssql = $mssql;
    }

    public function foo()
    {
        $ps = $this->mysql->prepare('...');
        $ps->execute();
        return $ps->fetchAll();
    }

    public function bar()
    {
        $ps = $this->mssql->prepare('...');
        $ps->execute();
        return $ps->fetchAll();
    }
}

In this scenario, I do not want the dependency container to create the $mysql instance if foo() is never called, and likewise it should not create $mssql if bar() is never called.

Now I could do something like this for each connection:

class MySql
{

    private array $config;
    private ?PDO $pdo = null;

    #[Inject('appconfig')]
    public function __construct(array $appConfig)
    {
        $this->config = $appConfig['mysql'];
    }

    public function getPdo(): PDO
    {
        if (is_null($this->pdo))
            $this->pdo = new PDO('...', $this->config['user'], $this->config['pass']);
        return $this->pdo;
    }
}

And then use autowire and lazy for the definition:

$builder->addDefinitions([
    'mysql' => autowire(MySql::class)->lazy(),
    'mssql' => autowire(MsSql::class)->lazy(),
]);

But this would introduce a lot of boilerplate as each database connection would require it's own class.

Is there a way that I can leverage lazy loading in my factories so I can avoid using autowiring?

NOTE: PHP DI lazy loading works by using proxies to the lazy-loaded objects. I wonder if a potential solution to my scenario would involve returning a proxy from the factory method.

php dependency-injection lazy-loading factory php-di