Внедрение Зависимостей / Служба Размещения

Объяснение DI

Следующий пример немного длинноват, но он пытается объяснить, почему Phalcon использует службы размещения и внедрение зависимостей. Во-первых, предположим, что мы разрабатываем компонент, называемый SomeComponent. Он выполняет некоторую задачу. Наш компонент имеет зависимость, то есть соединение с базой данных.

В этом первом примере, соединение создается внутри компонента. Несмотря на то, что это совершенно допустимая реализация, она не оптимальная, из-за того, что мы не можем изменить параметры соединения или тип системы баз данных, потому что компонент работает только как создано.

<?php

class SomeComponent
{
    /**
     * Экземпляр соединения жестко закодирован внутри компонента, 
     * 
     * поэтому его трудно заменить снаружи или изменить его поведение
     */
    public function someDbTask()
    {
        $connection = new Connection(
            [
                'host'     => 'localhost',
                'username' => 'root',
                'password' => 'secret',
                'dbname'   => 'invo',
            ]
        );

        // ...
    }
}

$some = new SomeComponent();

$some->someDbTask();

Чтобы устранить этот недостаток, мы создали сеттер, который вводит зависимость извне перед ее использованием. Это также допустимая реализация, но имеет свои недостатки:

<?php

class SomeComponent
{
    private $connection;

    /**
     * Установка внешнего подключения
     *
     * @param Connection $connection
     */
    public function setConnection(Connection $connection)
    {
        $this->connection = $connection;
    }

    public function someDbTask()
    {
        $connection = $this->connection;

        // ...
    }
}

$some = new SomeComponent();

// Создание соединения
$connection = new Connection(
    [
        'host'     => 'localhost',
        'username' => 'root',
        'password' => 'secret',
        'dbname'   => 'invo',
    ]
);

// Внедрение соединения в компонент
$some->setConnection($connection);

$some->someDbTask();

Теперь рассмотрим, что мы используем этот компонент в разных частях приложения, а затем нам нужно будет создать соединение несколько раз, прежде чем передать его компоненту. Используя глобальный шаблон реестра, мы можем хранить объект связь и использовать его всякий раз, когда нам это нужно.

<?php

class Registry
{
    /**
     * Возвращает соединение
     */
    public static function getConnection()
    {
        return new Connection(
            [
                'host'     => 'localhost',
                'username' => 'root',
                'password' => 'secret',
                'dbname'   => 'invo',
            ]
        );
    }
}

class SomeComponent
{
    protected $connection;

    /**
     * Установка внешнего подключения
     *
     * @param Connection $connection
     */
    public function setConnection(Connection $connection)
    {
        $this->connection = $connection;
    }

    public function someDbTask()
    {
        $connection = $this->connection;

        // ...
    }
}

$some = new SomeComponent();

// Передать соединение, определенное в реестре
$some->setConnection(Registry::getConnection());

$some->someDbTask();

Теперь давайте представим, что мы должны реализовать два метода в компоненте, первый всегда должен создать новое соединение, а второй всегда должен использовать общее соединение:

<?php

class Registry
{
    protected static $connection;

    /**
     * Создает соединение
     *
     * @return Connection
     */
    protected static function createConnection(): Connection
    {
        return new Connection(
            [
                'host'     => 'localhost',
                'username' => 'root',
                'password' => 'secret',
                'dbname'   => 'invo',
            ]
        );
    }

    /**
     * Создает соединение только один раз и возвращает его
     *
     * @return Connection
     */
    public static function getSharedConnection(): Connection
    {
        if (self::$connection === null) {
            self::$connection = self::createConnection();
        }

        return self::$connection;
    }

    /**
     * Всегда возвращает новое соединение
     *
     * @return Connection
     */
    public static function getNewConnection(): Connection
    {
        return self::createConnection();
    }
}

class SomeComponent
{
    protected $connection;

    /**
     * Установка внешнего подключения
     *
     * @param Connection $connection
     */
    public function setConnection(Connection $connection)
    {
        $this->connection = $connection;
    }

    /**
     * Этот метод всегда требует общего подключения
     */
    public function someDbTask()
    {
        $connection = $this->connection;

        // ...
    }

    /**
     * Этот метод всегда требует нового подключения
     *
     * @param Connection $connection
     */
    public function someOtherDbTask(Connection $connection)
    {

    }
}

$some = new SomeComponent();

// Это вставляет общее соединение
$some->setConnection(
    Registry::getSharedConnection()
);

$some->someDbTask();

// Здесь мы всегда передаем новое соединение в качестве параметра
$some->someOtherDbTask(
    Registry::getNewConnection()
);

До сих пор мы видели, как инъекция зависимости решала наши проблемы. Передача зависимостей в качестве аргументов вместо их внутреннего создания в коде делает наше приложение более поддерживаемым и отделенным. Однако в долгосрочной перспективе такая форма внедрения зависимостей имеет ряд недостатков.

Например, если компонент имеет много зависимостей, нам нужно будет создать несколько аргументов setter для передачи зависимостей или создать конструктор, который передает их с большим количеством аргументов, кроме того, создание зависимостей перед использованием компонента, каждый раз, делает наш код не таким ремонтопригодным, как хотелось бы:

<?php

// Создание зависимостей или их извлечение из реестра
$connection = new Connection();
$session    = new Session();
$fileSystem = new FileSystem();
$filter     = new Filter();
$selector   = new Selector();

// Передаёт их как параметры конструктора
$some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector);

// ... Или с помощью сеттеров
$some->setConnection($connection);
$some->setSession($session);
$some->setFileSystem($fileSystem);
$some->setFilter($filter);
$some->setSelector($selector);

Подумайте, если бы нам пришлось создать этот объект во многих частях нашего приложения. В будущем, если нам не потребуются какие-либо зависимости, мы должны пройти через всю базу кода, чтобы удалить параметр в любом конструкторе или сеттере, где мы вводили код. Чтобы решить эту проблему, мы снова возвращаемся к Глобальному реестру для создания компонента. Однако перед созданием объекта добавляется новый уровень абстракции:

<?php

class SomeComponent
{
    // ...

    /**
     * Определите  метод factory для создания экземпляров SomeComponent, вводящих его зависимости
     */
    public static function factory()
    {
        $connection = new Connection();
        $session    = new Session();
        $fileSystem = new FileSystem();
        $filter     = new Filter();
        $selector   = new Selector();

        return new self($connection, $session, $fileSystem, $filter, $selector);
    }
}

Теперь мы возвращаемся к тому, с чего начали, и снова строим зависимости внутри компонента! Мы должны найти решение, которое не позволит нам постоянно впадать в плохие практики.

Практичным и элегантным способом решения этих проблем является использование контейнера для зависимостей. Контейнеры выступают в качестве глобального реестра, который мы видели ранее. Использование контейнера зависимостей в качестве моста для получения зависимостей позволяет снизить сложность компонента:

<?php

use Phalcon\Di;
use Phalcon\DiInterface;

class SomeComponent
{
    protected $di;

    public function __construct(DiInterface $di)
    {
        $this->di = $di;
    }

    public function someDbTask()
    {
        // Получение услуги связи
        // Всегда возвращает новое соединение
        $connection = $this->di->get('db');
    }

    public function someOtherDbTask()
    {
        // Получить службу общего подключения, 
        // она будет возвращать одно и то же подключение каждый раз
        $connection = $this->di->getShared('db');

        // Этот метод также требует службы фильтрации входных данных
        $filter = $this->di->get('filter');
    }
}

$di = new Di();

// Регистрация службы "db" в контейнере
$di->set(
    'db',
    function () {
        return new Connection(
            [
                'host'     => 'localhost',
                'username' => 'root',
                'password' => 'secret',
                'dbname'   => 'invo',
            ]
        );
    }
);

// Регистрация службы "filter" в контейнере
$di->set(
    'filter',
    function () {
        return new Filter();
    }
);

// Регистрация службы "session" в контейнере
$di->set(
    'session',
    function () {
        return new Session();
    }
);

// Передает контейнер службы как уникальный параметр
$some = new SomeComponent($di);

$some->someDbTask();

Теперь компонент может просто получить доступ к службе, которая ему требуется, когда он в ней нуждается, если он не требует службы, он даже не инициализируется, экономя ресурсы. Компонент теперь сильно разъединен. Например, мы можем заменить способ создания соединений, их поведение или любой другой аспект, и это не повлияет на компонент.

Phalcon\Di является компонентом, реализующим внедрение зависимостей и расположение служб, и сам является контейнером для них.

Поскольку Phalcon сильно развязан, Phalcon\Di имеет важное значение для интеграции различных компонентов платформы. Разработчик также может использовать этот компонент для внедрения зависимостей и управления глобальными экземплярами различных классов, используемых в приложении.

В основном, этот компонент реализует шаблон инверсии  управления. Применяя это, объекты не получают свои зависимости с помощью установщиков или конструкторов, но запрашивают службу инжектор зависимостей. Это уменьшает общую сложность, так как есть только один способ получить необходимые зависимости в компоненте.

Кроме того, этот шаблон повышает тестируемость кода, делая его менее подверженным ошибкам.

Регистрация услуги в контейнере.

Зарегистрировать сервисы может сам фреймворк или разработчик. Когда компоненту A требуется компонент B (или экземпляр его класса) для работы, он может запросить компонент B из контейнера, а не создавать новый экземпляр компонента B.

Такой способ работы дает нам много преимуществ:

  • Мы можем легко заменить компонент на созданный нами или третьей стороной.
  • У нас есть полный контроль над инициализацией объектов, что позволяет нам устанавливать эти объекты по мере необходимости, прежде чем доставлять их компонентам.
  • Мы можем получить глобальные экземпляры компонентов структурированным и унифицированным способом.

Сервисы могут быть зарегистрированы с использованием нескольких типов определений:

Простая регистрация.

Как было замечено ранее, существует несколько способов регистрации сервисов. Эти мы вызываем просто:

Строка.

Этот тип ожидает имя допустимого класса, возвращая объект указанного класса, если класс не загружен, он будет создан с помощью автоматического загрузчика. Этот тип определения не позволяет указывать аргументы для конструктора класса или параметров:

<?php

// Вернуть новый Phalcon\Http\Request();
$di->set(
    'request',
    'Phalcon\Http\Request'
);

Экземпляр класса.

Этот тип объекта. В связи с тем, что объект не нужно разрешать, так как он уже является объектом, можно сказать, что на самом деле это не инъекция зависимости, однако это полезно, если вы хотите, чтобы возвращаемая зависимость всегда была одним и тем же объектом / значением:

<?php

use Phalcon\Http\Request;

// Вернуть новый Phalcon\Http\Request();
$di->set(
    'request',
    new Request()
);

Замыкания / анонимные функции.

Этот метод предоставляет большую свободу для построения зависимости по желанию, однако, трудно изменить некоторые параметры извне без необходимости полностью изменить определение зависимости:

<?php

use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;

$di->set(
    'db',
    function () {
        return new PdoMysql(
            [
                'host'     => 'localhost',
                'username' => 'root',
                'password' => 'secret',
                'dbname'   => 'blog',
            ]
        );
    }
);

Некоторые из ограничений могут быть преодолены путем передачи дополнительных переменных в среду закрытия:

<?php

use Phalcon\Config;
use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;

$config = new Config(
    [
        'host'     => '127.0.0.1',
        'username' => 'user',
        'password' => 'pass',
        'dbname'   => 'my_database',
    ]
);

// Использование переменной $config в текущей области
$di->set(
    'db',
    function () use ($config) {
        return new PdoMysql(
            [
                'host'     => $config->host,
                'username' => $config->username,
                'password' => $config->password,
                'dbname'   => $config->name,
            ]
        );
    }
);

Вы также можете получить доступ к другим службам DI с помощью метода get():

<?php

use Phalcon\Config;
use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;

$di->set(
    'config',
    function () {
        return new Config(
            [
                'host'     => '127.0.0.1',
                'username' => 'user',
                'password' => 'pass',
                'dbname'   => 'my_database',
            ]
        );
    }
);

// Использование сервиса 'config' из DI
$di->set(
    'db',
    function () {
        $config = $this->get('config');

        return new PdoMysql(
            [
                'host'     => $config->host,
                'username' => $config->username,
                'password' => $config->password,
                'dbname'   => $config->name,
            ]
        );
    }
);

Комплексная Регистрация.

Если требуется изменить определение сервиса без создания/разрешения сервиса, то нам нужно определить сервисы, используя синтаксис массива. Определение службы с помощью определения массива может быть немного более подробным:

<?php

use Phalcon\Logger\Adapter\File as LoggerFile;

// Регистрация службы "logger" с именем класса и его параметрами
$di->set(
    'logger',
    [
        'className' => 'Phalcon\Logger\Adapter\File',
        'arguments' => [
            [
                'type'  => 'parameter',
                'value' => '../apps/logs/error.log',
            ]
        ]
    ]
);

// Использование анонимной функции
$di->set(
    'logger',
    function () {
        return new LoggerFile('../apps/logs/error.log');
    }
);

Обе регистрации служб выше дают одинаковый результат. Однако определение массива позволяет при необходимости изменять параметры службы:

<?php

// Изменение имени класса службы
$di
    ->getService('logger')
    ->setClassName('MyCustomLogger');

// Измените первый параметр без создания экземпляра logger
$di
    ->getService('logger')
    ->setParameter(
        0,
        [
            'type'  => 'parameter',
            'value' => '../apps/logs/error.log',
        ]
    );

Кроме того, с помощью синтаксиса массива можно использовать три типа внедрения зависимостей:

Инъекция через Конструктор.

Этот тип внедрения передает зависимости / аргументы конструктору класса. Представим, что у нас есть следующий компонент:

<?php

namespace SomeApp;

use Phalcon\Http\Response;

class SomeComponent
{
    /**
     * @var Response
     */
    protected $response;

    protected $someFlag;

    public function __construct(Response $response, $someFlag)
    {
        $this->response = $response;
        $this->someFlag = $someFlag;
    }
}

Службу можно зарегистрировать следующим образом:

<?php

$di->set(
    'response',
    [
        'className' => 'Phalcon\Http\Response'
    ]
);

$di->set(
    'someComponent',
    [
        'className' => 'SomeApp\SomeComponent',
        'arguments' => [
            [
                'type' => 'service',
                'name' => 'response',
            ],
            [
                'type'  => 'parameter',
                'value' => true,
            ],
        ]
    ]
);

Службу 'response' (Phalcon\Http\Response) разрешается передавать в качестве первого аргумента конструктора, в то время как второй-логическое значение (true), которое передается как есть.

Инъекция через Сеттер.

Классы могут иметь сеттеры для введения необязательных зависимостей, наш предыдущий класс может быть изменен, чтобы принять зависимости с сеттерами:

<?php

namespace SomeApp;

use Phalcon\Http\Response;

class SomeComponent
{
    /**
     * @var Response
     */
    protected $response;

    protected $someFlag;

    public function setResponse(Response $response)
    {
        $this->response = $response;
    }

    public function setFlag($someFlag)
    {
        $this->someFlag = $someFlag;
    }
}

 Сервис с инъекцией через сеттер может быть зарегистрирован следующим образом:

<?php

$di->set(
    'response',
    [
        'className' => 'Phalcon\Http\Response',
    ]
);

$di->set(
    'someComponent',
    [
        'className' => 'SomeApp\SomeComponent',
        'calls'     => [
            [
                'method'    => 'setResponse',
                'arguments' => [
                    [
                        'type' => 'service',
                        'name' => 'response',
                    ]
                ]
            ],
            [
                'method'    => 'setFlag',
                'arguments' => [
                    [
                        'type'  => 'parameter',
                        'value' => true,
                    ]
                ]
            ]
        ]
    ]
);

Инъекции через Свойства .

Менее распространенная стратегия заключается во внедрении зависимостей или параметров непосредственно в открытые атрибуты класса:

<?php

namespace SomeApp;

use Phalcon\Http\Response;

class SomeComponent
{
    /**
     * @var Response
     */
    public $response;

    public $someFlag;
}

Сервис с инжекцией через свойств может быть зарегистрирован следующим образом:

<?php

$di->set(
    'response',
    [
        'className' => 'Phalcon\Http\Response',
    ]
);

$di->set(
    'someComponent',
    [
        'className'  => 'SomeApp\SomeComponent',
        'properties' => [
            [
                'name'  => 'response',
                'value' => [
                    'type' => 'service',
                    'name' => 'response',
                ],
            ],
            [
                'name'  => 'someFlag',
                'value' => [
                    'type'  => 'parameter',
                    'value' => true,
                ],
            ]
        ]
    ]
);

Поддерживаемые типы параметров:

TypeDescriptionExample
parameter Представляет литеральное значение, передаваемое в качестве параметра
php['type' => 'parameter', 'value' => 1234]
service Представляет другую службу в контейнере служб
php['type' => 'service', 'name' => 'request']
instance Представляет объект, который должен быть построен динамически
php['type' => 'instance', 'className' => 'DateTime', 'arguments' => ['now']]

Разрешение сервиса, определение которого является сложным, может быть немного медленнее, чем простые определения, замеченные ранее. Однако они обеспечивают более надежный подход к определению и внедрению служб.

Смешивание различных типов определений допускается, каждый может решить, что является наиболее подходящим способом регистрации услуг в соответствии с потребностями приложения.

Синтаксис Массива.

Синтаксис массива также позволяет регистрировать службы:

<?php

use Phalcon\Di;
use Phalcon\Http\Request;

// Создание контейнера инжектора зависимостей
$di = new Di();

// По имени класса
$di['request'] = 'Phalcon\Http\Request';

// При использовании анонимной функции экземпляр будет загружаться с задержкой
$di['request'] = function () {
    return new Request();
};

// Регистрация экземпляра напрямую
$di['request'] = new Request();

// Использование определения массива
$di['request'] = [
    'className' => 'Phalcon\Http\Request',
];

В приведенных выше примерах, когда платформе требуется доступ к данным запроса, она запрашивает службу, определенную в контейнере как "request". Контейнер, в свою очередь, вернет экземпляр необходимой службы. Разработчик может в конечном итоге заменить компонент, когда он/она в нем нуждается.

Каждый из методов (продемонстрированных в примерах выше), используемых для установки/регистрации сервиса, имеет свои преимущества и недостатки. Это зависит от разработчика и конкретных требований, которые будут определять, какой из них используется.

Настройка службы по строке проста, но не обладает гибкостью. Настройка служб с помощью массива обеспечивает большую гибкость, но усложняет код. Функция lambda является хорошим балансом между ними, но может привести к большему обслуживанию, чем можно было бы ожидать.

Phalcon\Di предлагает отложенную загрузку для каждой службы, которую он хранит. Если разработчик не решит создать экземпляр объекта напрямую и сохранить его в контейнере, любой объект, хранящийся в нем (через массив, строку и т. д.) будет лениво загружен, т. е. инстанцирован только при запросе.

Загрузка сервисов из YAML файлов.

Эта функция позволит вам установить ваши сервисы в файлах yaml или просто в простом php. Например, вы можете загрузить службы, используя файл yaml, как показанно тут:

config:
  className: \Phalcon\Config
  shared: true
<?php

use Phalcon\Di;

$di = new Di();
$di->loadFromYaml('services.yml');
$di->get('config'); // правильно вернет сервис config

Разрешение Служб.

Получение службы из контейнера-это простой вызов метода get. Будет возвращен новый экземпляр службы:

$request = $di->get('request');

Или вызывая через волшебный метод:

$request = $di->getRequest();

Или используя синтаксис доступа к массиву:

$request = $di['request'];

Аргументы могут быть переданы конструктору путем добавления параметра массива в метод get:

<?php

// new MyComponent('some-parameter', 'other')
$component = $di->get(
    'MyComponent',
    [
        'some-parameter',
        'other',
    ]
);

События.

Phalcon\Di может отправлять события в EventsManager , если он присутствует. События запускаются с помощью типа 'di'. Некоторые события при возврате boolean false могут остановить активную операцию. Поддерживаются следующие события:

имя событияВызываетсяМожет остановить работу?Срабатывает на
beforeServiceResolve Срабатывает до решения. Слушатели получают имя службы и передаваемые ей параметры. Нет Слушатели
afterServiceResolve Срабатывает после разрешения службы. Слушатели получают имя службы, экземпляр и передаваемые ему параметры. Нет Слушатели

Коллективное обслуживание.

Сервисы могут быть зарегистрированы как "общие" сервисы это означает, что они всегда будут действовать как одиночка. После первого разрешения Службы один и тот же экземпляр возвращается каждый раз, когда потребитель извлекает службу из контейнера:

<?php

use Phalcon\Session\Adapter\Files as SessionFiles;

// Зарегистрируйте службу сеансов как "всегда общую"
$di->setShared(
    'session',
    function () {
        $session = new SessionFiles();

        $session->start();

        return $session;
    }
);

// Находит службу впервые
$session = $di->get('session');

// Возвращает первый экземпляр объекта
$session = $di->getSession();

Альтернативным способом регистрации общих служб является передача 'true' в качестве третьего параметра 'set':

<?php

// Зарегистрировать службу 'session' как "всегда общую"
$di->set(
    'session',
    function () {
        // ...
    },
    true
);

Если служба не зарегистрирована как общая и вы хотите быть уверены, что общий экземпляр будет доступен каждый раз, когда служба будет получена из DI, можно использовать метод getShared:

$request = $di->getShared('request');

Манипулирование службами индивидуально.

После регистрации службы в контейнере служб ее можно извлечь, чтобы управлять ею по отдельности:

    <?php

    use Phalcon\Http\Request;

    // Регистрация службы 'request'
    $di->set('request', 'Phalcon\Http\Request');

    // Получить службу
    $requestService = $di->getService('request');

    // Изменить ее определение
    $requestService->setDefinition(
        function () {
            return new Request();
        }
    );

    // Изменить ее на общий
    $requestService->setShared(true);

    // Разрешение Службы (возврат экземпляра Phalcon\Http\Request)
    $request = $requestService->resolve();

Создание экземпляров классов с помощью контейнера служб.

Когда вы запрашиваете службу в контейнере служб, если она не может найти службу с тем же именем, она попытается загрузить класс с тем же именем. С помощью этого поведения мы можем заменить любой класс другим, просто зарегистрировав сервис с его именем:

<?php

// Регистрация контроллера в качестве службы
$di->set(
    'IndexController',
    function () {
        $component = new Component();

        return $component;
    },
    true
);

// Регистрация контроллера в качестве службы
$di->set(
    'MyOtherComponent',
    function () {
        // Фактически возвращает другой компонент
        $component = new AnotherComponent();

        return $component;
    }
);

// Создание экземпляра с помощью контейнера служб
$myComponent = $di->get('MyOtherComponent');

Этим можно воспользоваться, всегда создавая экземпляры классов с помощью контейнера служб (даже если они не зарегистрированы в качестве служб). DI вернется к допустимому автозагрузчику, чтобы наконец загрузить класс. Таким образом, можно легко заменить любой класс в будущем, реализовав для него определение.

Автоматическая самоиньекция DI .

Если класс или компонент требует, чтобы DI сам определил местонахождение сервисов, DI может автоматически внедрить себя в экземпляры, которые он создает, чтобы сделать это, необходимо реализовать Phalcon\Di\InjectionAwareInterface в ваших классах:

<?php

use Phalcon\DiInterface;
use Phalcon\Di\InjectionAwareInterface;

class MyClass implements InjectionAwareInterface
{
    /**
     * @var DiInterface
     */
    protected $di;

    public function setDi(DiInterface $di)
    {
        $this->di = $di;
    }

    public function getDi()
    {
        return $this->di;
    }
}

Затем, как только сервис будет разрешен, $di будет автоматически передан setDi():

<?php

// Регистрация сервиса
$di->set('myClass', 'MyClass');

// Разрешения (прим.: $myClass->setDi($di) вызывается автоматически)
$myClass = $di->get('myClass');

Организация служб в файлах.

Вы можете лучше организовать свое приложение, переместив регистрацию службы в отдельные файлы вместо того, чтобы делать все в начальной загрузке приложения:

<?php

$di->set(
    'router',
    function () {
        return include '../app/config/routes.php';
    }
);

Затем в файле ('../app/config/routes.php') вернуть разрешенный объект:

<?php

$router = new MyRouter();

$router->post('/login');

return $router;

Доступ к DI статическим способом.

При необходимости вы можете получить доступ к последней DI, созданной в статической функции следующим образом:

<?php

use Phalcon\Di;

class SomeComponent
{
    public static function someMethod()
    {
        // Get the session service
        $session = Di::getDefault()->getSession();
    }
}

Поставщики сервисов.

Используя ServiceProviderInterface Вы теперь регистрируете  службы контекстом. Вы можете переместить весь свой вызовы $di->set() в классы как здесь:

<?php

use Phalcon\Di\ServiceProviderInterface;
use Phalcon\DiInterface;
use Phalcon\Di;
use Phalcon\Config\Adapter\Ini;

class SomeServiceProvider implements ServiceProviderInterface
{
    public function register(DiInterface $di)
    {
        $di->set(
            'config', 
            function () {
                return new Ini('config.ini');
            }
        );
    }
}

$di = new Di();
$di->register(new SomeServiceProvider());
var_dump($di->get('config')); // вернем правильно наш конфиг

Предопределенная настройка DI.

Хотя несвязанный характер Phalcon предлагает нам большую свободу и гибкость, возможно, мы просто хотим использовать его в качестве фреймворка с полным стеком. Чтобы добиться этого, платформа предоставляет вариант в Phalcon\Di вызова Phalcon\Di\FactoryDefault. Этот класс автоматически регистрирует соответствующие службы в комплекте с платформой для работы в качестве полного стека.

<?php

use Phalcon\Di\FactoryDefault;

$di = new FactoryDefault();

Соглашения Об Именах Служб.

Несмотря на то, что вы можете зарегистрировать службы с нужными именами, Phalcon имеет несколько соглашений об именах, которые позволяют ему получить правильный (встроенный) сервис, когда вам это нужно.

Название сервисаОписаниеПо умолчаниюОбщий
assets Менеджер по активам Phalcon\Assets\Manager Да
annotations Синтаксический Анализатор Аннотаций Phalcon\Annotations\Adapter\Memory Да
cookies Служба Управления http куками Phalcon\Http\Response\Cookies Да
crypt Шифрование/дешифрование данных Phalcon\Crypt Да
db Низкоуровневая Служба Подключения К Базе Данных Phalcon\Db Да
dispatcher Диспетчеры Диспетчерской Службы Phalcon\Mvc\Dispatcher Да
eventsManager Служба Управления Событиями Phalcon\Events\Manager Да
escaper Контекстное Экранирование Phalcon\Escaper Да
flash Служба Flash-Сообщений Phalcon\Flash\Direct Да
flashSession Служба Обмена Сообщениями Flash-Сеанса Phalcon\Flash\Session Да
filter Служба Фильтрации Входных Данных Phalcon\Filter Да
modelsCache Серверная часть кэша для кэша моделей Нет Нет
modelsManager Служба Управления Моделями Phalcon\Mvc\Model\Manager Да
modelsMetadata Службы Метаданных Моделей Phalcon\Mvc\Model\MetaData\Memory Да
request Служба среды HTTP-запросов Phalcon\Http\Request Да
response Служба среды HTTP-ответа Phalcon\Http\Response Да
router Служба маршрутизации Phalcon\Mvc\Router Да
security Помощники по безопасности Phalcon\Security Да
session Служба сессий Phalcon\Session\Adapter\Files Да
sessionBag Служба мешка сессии Phalcon\Session\Bag Да
tag Помощники по генерации HTML Phalcon\Tag Да
transactionManager Модели Службы Диспетчера Транзакций Phalcon\Mvc\Model\Transaction\Manager Да
url Служба генератора URL-адресов Phalcon\Mvc\Url Да
viewsCache Серверная часть кэша для фрагментов представлений Нет Нет

Реализация собственного DI.

Интерфейс Phalcon\DiInterface должен быть реализован для создания собственного DI, заменяющего предоставленный Phalcon или расширяющего текущий.