In 2019, I wrote an article about using Dotenv to configure a Zend Expressive application. Since then, the project has been renamed to Laminas and the microframework to Mezzio. Plus, vlucas/phpdotenv has been updated and the recommended way to use it has changed. Time for an update.


Starting point

Any laminas or mezzio application serves as a starting point. For this article, a minimal setup is used with a container.php and container.php as it is provided by mezzio-skeleton.

In this example, the .env file is used to configure the database connection. The database connection is created using PDO. The database connection is created in a factory and the factory is registered in the container.

Install phpdotenv

Install it as recommended.

composer require vlucas/phpdotenv

Create a dotenv file

.env

DB_DSN=sqlite::memory:
DB_USERNAME=null
DB_PASSWORD=null

Connect dotenv to the application

To make the .env file available to the application, the following code is added to container.php. It also ensures that the required environment variables are set.

config/container.php

use Dotenv\Dotenv;

$dotenv = Dotenv::createImmutable(dirname(__DIR__));
$dotenv->load();
$dotenv->required(['DB_DSN', 'DB_USERNAME', 'DB_PASSWORD']);

Application configuration

As the .env file is environment-specific, and the application configuration is making usage of it, the configuration file can be global.

config/autoload/db.global.php

<?php

declare(strict_types=1);

return [
    'db' => [
        'dsn'      => $_ENV['DB_DSN'],
        'username' => $_ENV['DB_USERNAME'],
        'password' => $_ENV['DB_PASSWORD'],
        'options'  => [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ],
    ],
];

Factory

The PDO instance is created in a factory.

src/LaminasDotEnv/PdoFactory.php

<?php

declare(strict_types=1);

namespace LaminasDotEnv;

use PDO;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;

final class PdoFactory
{
    /**
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function __invoke(ContainerInterface $container): PDO
    {
        $config = $container->get('config');

        return new PDO(
            $config['db']['dsn'],
            $config['db']['username'],
            $config['db']['password'],
        );
    }
}

Add a service definition as you wish. The PDO instance can then be retrieved from the container.

bin/db.php

<?php

declare(strict_types=1);

use Psr\Container\ContainerInterface;

require_once dirname(__DIR__) . '/vendor/autoload.php';

(static function (): void {
    $container = require dirname(__DIR__) . '/config/container.php';
    assert($container instanceof ContainerInterface);

    $pdo = $container->get(PDO::class);
    assert($pdo instanceof PDO);

    echo 'Create table' . PHP_EOL;
    $pdo->exec('CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, name TEXT NOT NULL)');

    echo 'Insert data' . PHP_EOL;
    $pdo->exec('INSERT INTO test (name) VALUES ("Hello, World!")');

    echo 'Fetch data' . PHP_EOL;
    $stmt = $pdo->query('SELECT * FROM test');
    $row  = $stmt->fetch(PDO::FETCH_ASSOC);

    echo 'Data: ';
    echo $row['name'] . PHP_EOL;

    echo 'Done' . PHP_EOL;
})();

Run the application

C:\source\laminas-dot-env [main ]> php bin/db.php
Create table
Insert data
Fetch data
Data: Hello, World!
Done

Conclusion

Using Dotenv to configure a Laminas or Mezzio application is straightforward. The .env file is loaded in the container configuration and the required environment variables are checked. The PDO instance is created using the configuration via a factory registered to the container. The PDO instance is retrieved from the container.

Resources