Advertisement
  1. Code
  2. Coding Fundamentals
  3. Design Patterns

Примеры внедрения зависимостей в PHP с помощью компонентов Symfony

Scroll to top
Read Time: 6 min

() translation by (you can also view the original English article)

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

Что такое Symfony-компонент DependencyInjection?

Компонент Symfony DependencyInjection предоставляет стандартный способ для создания объектов и обработки управления зависимостями в PHP-приложениях. Сердце компонента DependencyInjection является контейнер, который содержит все доступные сервисы в приложении.

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

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

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

Установка и настройка

В этом разделе мы сначала установим компонент DependencyInjection. Я предполагаю, что вы уже установили Composer в вашу систему, поскольку он нам понадобится для установки компонента DependencyInjection, доступного в Packagist.

Итак, идем дальше и устанавливаем компонент DependencyInjection, используя следующую команду.

1
$composer require symfony/dependency-injection

Эта команда создаст файл composer.json, содержимое которого должно быть таким:

1
{
2
    "require": {
3
        "symfony/dependency-injection": "^4.1",
4
    }
5
}

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

Если вы хотите загрузить сервисы из YAML-файла вместо его определения в PHP-коде, то для этого нужен компонент Yaml, поскольку он помогает вам конвертировать YAML-строки в PHP-совместимые типы данных и наоборот.

1
$composer require symfony/yaml

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

1
$composer require symfony/config

Изменим файл composer.json, чтобы он выглядеть как показано ниже.

1
{
2
    "require": {
3
        "symfony/dependency-injection": "^4.1",
4
        "symfony/config": "^4.1",
5
        "symfony/yaml": "^4.1"
6
    },
7
    "autoload": {
8
         "psr-4": {
9
             "Services\\": "src"
10
         },
11
         "classmap": ["src"]
12
    }
13
}

Поскольку мы добавили новое поле classmap, давайте обновим автозагрузчик Composer, выполнив следующую команду.

1
$composer dump -o

Теперь вы можете использовать пространство имен Services для автоматической загрузки классов в каталоге src.

Итак, это была часть установки, но как вы предполагаете использовать этот компонент? На самом деле нужно просто включить файл autoload.php, созданный Composer в вашем приложении, как показано в следующем фрагменте.

1
<?php
2
require_once './vendor/autoload.php';
3
4
// application code

5
?>

Как работать с Container

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

Для начала, давайте определим довольно простой сервис src/DemoService.php со следующим содержимым.

1
<?php
2
// src/DemoService.php

3
namespace Services;
4
5
class DemoService
6
{
7
  public function helloWorld()
8
  {
9
    return "Hello World!\n";
10
  }
11
}

Это очень простой сервис, который просто реализует метод helloWorld на данный момент.

Далее теперь создадим файл basic_container.php со следующим содержимым в корневом каталоге вашего приложения.

1
<?php
2
// basic_container.php

3
require_once './vendor/autoload.php';
4
use Symfony\Component\DependencyInjection\ContainerBuilder;
5
6
// init service container

7
$containerBuilder = new ContainerBuilder();
8
9
// add service into the service container

10
$containerBuilder->register('demo.service', '\Services\DemoService');
11
12
// fetch service from the service container

13
$demoService = $containerBuilder->get('demo.service');
14
echo $demoService->helloWorld();

Для начала мы создали объект ContainerBuilder с помощью конструктора new ContainerBuilder(). Затем мы использовали метод register объекта ContainerBuilder для внедрения нашего пользовательского сервиса \Services\DemoService в контейнер. demo.service действует как псевдоним для нашего сервиса.

И наконец, мы использовали метод get в объекте ContainerBuilder для получения нашего сервиса из контейнера и использовали его для вызова метода helloWorld.

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

Реальный пример

В этом разделе мы напишем пример, демонстрирующий, как разрешаются зависимости класса с помощью компонента DependencyInjection.

Чтобы продемонстрировать это, мы создадим новый сервис DependentService, который требует сервис DemoService, созданный в предыдущем разделе, в качестве зависимости. Таким образом, мы увидим, как сервис DemoService автоматически внедряется в качестве зависимости при создании экземпляра сервиса DependentService.

Идем дальше и создаем файл src/DependentService.php со следующим содержимым для определения сервиса, DependentService.

1
<?php
2
// src/DependentService.php

3
namespace Services;
4
5
class DependentService
6
{
7
  private $demo_service;
8
9
  public function __construct(\Services\DemoService $demoService)
10
  {
11
    $this->demo_service = $demoService;
12
  }
13
14
  public function helloWorld()
15
  {
16
    return $this->demo_service->helloWorld();
17
  }
18
}

Как вы можете видеть, сервис \Services\DemoService требует для своей инициализации сервис DependentService.

Не останавливаемся и создаем файл di_container.php с приведенным ниже содержимым.

1
<?php
2
// di_container.php

3
require_once './vendor/autoload.php';
4
use Symfony\Component\DependencyInjection\ContainerBuilder;
5
use Symfony\Component\DependencyInjection\Reference;
6
7
// init service container

8
$containerBuilder = new ContainerBuilder();
9
10
// add demo service into the service container

11
$containerBuilder->register('demo.service', '\Services\DemoService');
12
13
// add dependent service into the service container

14
$containerBuilder->register('dependent.service', '\Services\DependentService')
15
                 ->addArgument(new Reference('demo.service'));
16
17
// fetch service from the service container

18
$dependentService = $containerBuilder->get('dependent.service');
19
echo $dependentService->helloWorld();

Мы используем один и тот же метод register для внедрения нашего пользовательского сервиса \Services\DependentService в контейнер.

В дополнение к этому, мы также использовали метод addArgument, чтобы указать контейнеру о зависимости сервиса DependentService. Мы использовали класс Reference, чтобы сообщить контейнеру, что ему нужно внедрить сервис demo.service при инициализации сервиса dependent.service. Таким образом, зависимость автоматически внедряется по мере необходимости!

И последнее, мы использовали метод get объекта ContainerBuilder для извлечения сервиса dependent.service из объекта ContainerBuilder и использовали его для вызова метода helloWorld.

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

Как динамически загружать сервисы, используя YAML-файл

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

Наряду с компонентом DependencyInjection нам также понадобится еще два Symfony-компонента для реализации примера — Config и Yaml. Напоминаем, что мы уже установили эти два компонента в разделе "Установка и настройка" вместе с компонентом DependencyInjection. Поэтому мы готовы начать!

Продолжим дальше и создадим файл services.yaml со следующим содержимым в корневом каталоге вашего приложения.

1
services:
2
    demo.service:
3
        class:     \Services\DemoService
4
    dependent.service:
5
        class:     \Services\DependentService
6
        arguments: ["@demo.service"]

Как вы можете видеть, довольно просто определить сервисы, используя синтаксис YAML. Для определения зависимостей сервисов, вам нужно будет использовать ключ arguments.

Далее создаем файл di_yaml_container.php со следующим содержимым.

1
<?php
2
// di_yaml_container.php

3
require_once './vendor/autoload.php';
4
use Symfony\Component\DependencyInjection\ContainerBuilder;
5
use Symfony\Component\Config\FileLocator;
6
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
7
8
// init service container

9
$containerBuilder = new ContainerBuilder();
10
11
// init yaml file loader

12
$loader = new YamlFileLoader($containerBuilder, new FileLocator(__DIR__));
13
14
// load services from the yaml file

15
$loader->load('services.yaml');
16
17
// fetch service from the service container

18
$serviceOne = $containerBuilder->get('dependent.service');
19
echo $serviceOne->helloWorld();

Все почти то же самое, за исключением того, что мы загружаем сервисы из services.yaml, а не определяем их в самом коде PHP. Это позволяет динамически определять зависимости приложения.

Заключение

Компонент Symfony DependencyInjection занял центральное место в этой обучающей статье. Мы увидели, как установить и настроить DependencyInjection, а также некоторые реальные примеры того, как его использовать.

Я действительно очарован и рад несвязанным друг с другом компонентам фреймворка Symfony, которые вы можете просто найти и использовать в вашем приложении. Включите их к ваш код, и они просто работают! В целом, я могу только увидеть преимущества этого нового подхода фреймворка для нашего сообщества PHP!

Поделитесь своими мыслями и предложениями, используя канал ниже. Мне хотелось бы обсудить эту статью с вами!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.