Что такое внедрение зависимостей в PHP и как его использовать
Одна популярная и хорошо известная методология разработки программного обеспечения называется внедрением зависимостей, которая помогает облегчить поток, гарантируя, что ваше программное обеспечение всегда имеет доступ к необходимым инструментам. Многие пытаются представить эту методологию достаточно сложной, но на самом деле это не так.
Давайте углубимся в то, что такое внедрение зависимостей, как оно работает и как оно поможет вашему программному обеспечению.
Что такое внедрение зависимостей?
Отличная аналогия внедрения зависимостей – это работник с набором инструментов, который путешествует вместе с программным обеспечением во время его обработки, обеспечивая бесперебойную работу. Набор инструментов может содержать всевозможные вещи, включая переменные, массивы, объекты , замыкания и все остальное, что необходимо для выполнения поставленной задачи.
Когда рабочий запускает новую задачу (например, класс или метод), он рассматривает необходимые требования и, не задумываясь, извлекает различные инструменты, необходимые для выполнения работы. В двух словах, это инъекция зависимости.
Вы можете наполнить свой инструментарий всем, что вам нужно, а затем в классах и методах программного обеспечения укажите необходимые инструменты, и они автоматически появятся для вас.
Установить контейнер Apex
Существует множество различных реализаций, но все они в основном работают одинаково, и мы будем использовать контейнер Apex, поскольку он прост и понятен. Предполагается, что у вас уже установлен PHP, и вы можете проверить, установлен ли Composer, с помощью команды:
composer --version
Если вы получаете сообщение об ошибке «команда не найдена», вы можете установить Composer с помощью следующей команды:
sudo curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer
Теперь создайте пустой каталог и в нем выполните следующие команды:
composer require apex/container
composer require twig/twig
Это загрузит как контейнер Apex, так и популярный механизм шаблонов Twig, который будет использоваться в приведенных ниже примерах. Оба могут быть найдены в подкаталоге / vendor /.
Внедрите свои инструменты
Давайте создадим быстрый класс под названием Car со следующим кодом:
<?php
use TwigLoaderArrayLoader;
class Car
{
public function __construct(
public string $model,
public string $color,
public ArrayLoader $db
) {
echo "I'm a $color $model and have a " . $db::class . "
";
}
}
Это простой класс с двумя свойствами, маркой и цветом автомобиля, и загружает класс ArrayLoader из Twig. Сохраните его как car.php и приготовьтесь использовать магию внедрения зависимостей. Откройте еще один пустой файл и добавьте следующий код:
<?php
use ApexContainerContainer;
// Load Composer packages, and the car.php file
require_once(__DIR__ . '/vendor/autoload.php');
require_once(__DIR__ . '/car.php');
// Create container, and add a couple tools
$cntr = new Container(use_attributes: true);
$cntr->set('model', 'Jaguar');
$cntr->set('color', 'silver');
// Create our car object
$car = $cntr->make('Car');
$car2 = $cntr->make('car', ['color' => 'red']);
Сохраните и запустите этот код в терминале, и результат будет:
I'm a silver Jaguar and have a TwigLoaderArrayLoader
I'm a red Jaguar and have a TwigLoaderArrayLoader
В приведенном выше коде вы создали контейнер (то есть набор инструментов) и добавили пару инструментов, цвет и марку автомобиля. Вместо создания объекта автомобиля с помощью обычного new Car (); , он был создан с помощью метода контейнера make () . Сначала он прошел через класс, чтобы проверить требования, затем посмотрел, какие элементы (то есть инструменты) у нас есть в наличии, и внедрил их в класс, прежде чем передать его обратно.
Вы заметите объявление использования в верхней части файла для ArrayLoader, а третий аргумент в конструкторе также запрашивает ArrayLoader . Когда контейнер посмотрел на требования класса, он заметил оба этих аспекта, автоматически создал экземпляр ArrayLoader `и внедрил его в конструктор. Это называется автоматическим подключением.
Расширение с помощью внедрения атрибутов
Сделав еще один шаг, мы можем внедрять не только в конструктор, но и непосредственно в свойства через атрибуты. Измените файл автомобиля и измените его на:
<?php
use TwigLoaderArrayLoader;
use ApexContainerContainer;
class Car
{
#[Inject(Container::class)]
public Container $cntr;
public function __construct(
public string $model,
public string $color,
public ArrayLoader $db
) {
echo "I'm a $color $model and have a " . $db::class . "
";
}
function getCost()
{
echo "Class is " . $this->cntr::class . "
";
}
}
Только изменения были новое применение декларация была добавлена, было добавлено свойство с атрибутом класса Container, и мы добавили новый getCost () функцию . В нижней части ранее запущенного тестового кода добавьте строку:
$car->getCost();
Теперь запустите код еще раз, и результат будет:
I'm a silver Jaguar and have a TwigLoaderArrayLoader
I'm a red Jaguar and have a TwigLoaderArrayLoader
Class is ApexContainerContainer
На этот раз, когда был загружен класс car.php , контейнер также посмотрел на его свойства, заметил атрибут Inject, который вызвал класс контейнера, и внедрил его экземпляр. Внедрение через атрибуты таким способом иногда предпочтительнее, так как это помогает сделать вещи более чистыми и читаемыми.
Получите свои собственные инструменты
Что произойдет, если вы захотите извлечь элемент из контейнера самостоятельно, вместо того, чтобы всегда вводить его? Это легко сделать с помощью метода контейнера get () . В файле car.php измените ранее добавленную функцию getCost () с помощью:
function getCost()
{
$price = $this->cntr->get('car_price');
echo "The price is $price
";
}
Теперь в тестовом коде, который вы выполняли, в любом месте перед строкой, вызывающей getCost (), добавьте строку, например:
$cntr->set('car_price', 24995);
Теперь запустите код, и результат будет:
I'm a silver Jaguar and have a TwigLoaderArrayLoader
I'm a red Jaguar and have a TwigLoaderArrayLoader
Price is 24999
Вам не обязательно вводить свои элементы, и вы всегда можете легко получить то, что вам нужно, с помощью метода get (), как показано выше.
Движение вперед с внедрением зависимостей
Теперь у вас есть хорошее представление о том, что такое внедрение зависимостей и как оно работает. Опять же, приведенное выше является лишь одной из многих реализаций, но все реализации работают одинаково с методами get () / set () / make () .
Но есть еще кое-что о внедрении зависимостей, например, о внедрении файла определений и метода. Если интересно, ознакомьтесь с руководством по контейнеру Apex или другими реализациями.