Внедрение зависимостей (также Dependency injection или DI) — механизм подстановки фреймворком зависимостей для конструктора или других методов у создаваемых объектов.
При создании объектов фреймворком, таких, как контроллеры, middlewares, команды и другие, внедрение зависимостей уже назначено при вызове целевого метода (в том числе конструктора).
Согласно механизму DI предполагается, что если указать в зависимостях (аргументах) метода нужные в нём классы или интерфейсы, то фреймворк попытается найти такие соответствия в контейнере, получит из контейнера или самостоятельно создаст объект и подставит его в необходимый аргумент.
При этом, если в контейнере такой сервис найден не будет, то будет произведена попытка создать объект из обычного подходящего класса в проекте, а если у последнего есть свои зависимости в конструкторе, то фреймворк попробует их наполнить аналогичным образом.
При отсутствии подстановочного значения для аргументов, которые имеют значения по умолчанию, будет использовано дефолтное.
Иначе фреймворк вернёт ошибку с информацией, что успешно использовать DI для указанных зависимостей не удалось.
Когда объект контроллера или middleware создается на стороне фреймворка, то сначала разрешаются зависимости конструктора, затем вызываемого метода.
Также, когда запрос обрабатывается фреймворком, то в совпавшем контроллере вызовется только один метод, в таком случае нет разницы, откуда получать зависимость, из конструктора или метода, хотя из конструктора в некоторых случаях удобнее.
На следующем примере показаны два метода контроллера с различным присвоением $logger из контейнера через DI.
Аналогичным образом устанавливаются зависимости для middleware.
В командах фреймворка и в событиях (Events) реализовано похожим образом, но только через конструктор:
Внедрение зависимостей удобно тем, что при тестировании мы можем создать нужные значения для зависимостей класса. Однако, при создании объекта вручную, было бы неудобно инициализировать самим все его зависимости. Чтобы автоматизировать этот процес, существует класс Hleb\Static\DI фреймворка.
Здесь показано, как создать объект класса, в конструкторе которого есть зависимость, а также вызвать нужный метод объекта, в котором также нужно автоматически подставить значение. На примере также есть зависимость не из контейнера (класс Insert), объект которой создается и подстанавливается в метод.
Довольно часто используемый вариант DI c Request и Response (в данном случае получаемых из контейнера):
Из-за существования различных подходов в именовании интерфейсов, получение стандартных сервисов из контейнера может быть как по интерфейсу с окончанием Interface, так и без. Например, Hleb\Reference\RequestInterface аналогичен Hleb\Reference\Interface\Request.
Как уже было упомянуто выше, если в процессе разрешения зависимостей фреймворк не находит зависимость в контейнере, то он попытается самостоятельно создать объект такого класса и разрешить зависимости последнего, если они указаны в конструкторе класса.
Существуют способы для указания, каким путём нужно следовать в таком случае. Через параметр конфигурации `system`.`autowiring.mode` устанавливается режим управления такими зависимостями. Существует режим, в котором можно полностью отключить автоподстановку не найденных в контейнере зависимостей и режим аналогичный этому, но при наличии атрибута AllowAutowire разрешающий использовать объект класса, а также атрибут NoAutowire, запрещающий автопостановку текущего класса, если включен разрешающий режим с поддержкой этого атрибута.
При помощи специального атрибута DI можно указать в конкретном месте (методе класса), какую именно зависимость с указанным интерфейсом нужно использовать. Если такая зависимость из атрибута будет найдена в контейнере, то будет использована из контейнера. Если нет, то здесь действуют те же правила автоподстановки не найденных в контейнере зависимостей, как если бы она была указана напрямую в методе. Примеры:
Показаны варианты, как можно указать конкретный класс для требуемого интерфейса в параметре, а также создание нужного класса в атрибуте.
← Получение сервиса Request →