Автоматическое связывание зависимостей (autowiring) с помощью symfony/dependency-injection

Чтобы объекты, указанные в конструкторе класса создавались и подгружались автоматически можно использовать autowiring в контейнере зависимостей от symfony.

Устанавливаем DI компонент от symfony:

composer require symfony/dependency-injection

Описываем зависимости:

// создаём строитель контейнера
$container = new \Symfony\Component\DependencyInjection\ContainerBuilder();

// добавляем публичные зависимости с автозагрузкой
$container->autowire(Config::class)->setPublic(true);
$container->autowire(Database::class)->setPublic(true);

// компилируем контейнер
$container->compile();

Опишем простой класс Config, который берёт настройки из внешнего файла:

class Config
{
    private $config = [];

    /**
     * Config constructor.
     * @throws Exception
     */
    public function __construct()
    {
        /** @noinspection PhpIncludeInspection */
        $this->config = require './pathToConfig/config.php';

        if (!is_array($this->config)) {
            throw new Exception('Unable to get config');
        }
    }

    public function get(string $name)
    {
        return $this->config[$name] ?? null;
    }
}

А вот и класс Database, в котором сработает автоматическое связывание, объект $config, создастся и придёт в класс автоматически:

class Database
{
    /**
     * @var \PDO
     */
    public $pdo;

    public function __construct(Config $config)
    {
        $databaseConfig = $config->get('database');

        $this->pdo = new \PDO(
            sprintf(
                'mysql:dbname=%s;host=%s;port=%s',
                $databaseConfig['dbname'],
                $databaseConfig['host'],
                $databaseConfig['port']
            ),
            $databaseConfig['username'],
            $databaseConfig['password']
        );
    }

    /**
     * @param string $query
     * @return \PDOStatement
     * @throws Exception
     */
    public function query(string $query): \PDOStatement
    {
        $result = $this->pdo->query($query);

        if ($result === false) {
            throw new Exception(serialize($this->pdo->errorInfo()));
        }

        return $result;
    }
}

Получаем объект $database и пользуемся:

/** @var Database $database */
$database = $container->get(Database::class);

$database->query('
    CREATE TABLE IF NOT EXISTS users (
        `id` int(11) NOT NULL AUTO_INCREMENT,
        `login` varchar(30) NOT NULL UNIQUE,
        `password_hash` varchar(255) NOT NULL,
        `auth_hash` varchar(255) UNIQUE,
        PRIMARY KEY (`id`),
        INDEX (`login`)
    ) COLLATE="utf8_general_ci" ENGINE=InnoDB;
');