Гид по технологиям

Внедрение зависимостей в PHP с Apex Container

5 min read PHP Обновлено 31 Dec 2025
Внедрение зависимостей в PHP с Apex Container
Внедрение зависимостей в PHP с Apex Container

Изображение: концепция инъекции зависимостей в PHP

Кратко: внедрение зависимостей (Dependency Injection, DI) — это способ обеспечить класс всеми «инструментами» (зависимостями), которые он требует, без ручного создания этих инструментов внутри класса. В статье показано, как установить Apex Container, сделать авто‑внедрение (autowiring), использовать атрибутную инъекцию и выбирать зависимости вручную через get()/set(). Подключены практические советы, модели принятия решений и чек‑листы для команд.

Что такое внедрение зависимостей?

В одной строке: внедрение зависимостей — это передача объекту внешних ресурсов и настроек вместо их создания внутри объекта.

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

Почему это полезно:

  • Уменьшает связность между классами.
  • Улучшает тестируемость (можно легко подменять зависимости моками).
  • Позволяет централизовать конфигурацию — менять реализации в одном месте.

Ключевые термины:

  • Контейнер — реестр или фабрика, который хранит и создаёт зависимости (инструменты).
  • Авто‑внедрение (autowiring) — контейнер автоматически создаёт и связывает требуемые типы.
  • Атрибутная инъекция — внедрение в свойства класса через атрибуты (PHP 8+).

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

Установка Apex Container

Мы будем использовать Apex Container — простая и понятная реализация контейнера. Предполагается, что PHP установлен. Проверьте Composer:

composer --version

Если Composer не установлен, его можно поставить, выполнив (в Linux/macOS):

sudo curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer

Создайте пустую папку проекта и выполните внутри:

composer require apex/container
composer require twig/twig

Обе библиотеки будут загружены в каталог /vendor/.

Пример: инъекция инструментов (Car)

Создадим класс Car. Смысл—показать, как контейнер передаёт настройки и автоматически создаёт экземпляры классов.

Сохраните этот код как car.php:

Теперь файл теста (index.php или run.php):

set('model', 'Jaguar');
$cntr->set('color', 'silver');

$car = $cntr->make('Car');
$car2 = $cntr->make('car', ['color' => 'red']);

При запуске вы увидите:

I'm a silver Jaguar and have a Twig\Loader\ArrayLoader
I'm a red Jaguar and have a Twig\Loader\ArrayLoader

Объяснение: контейнер просматривает конструктор класса Car, находит требуемые аргументы, подставляет зарегистрированные значения (model, color) и автоматически создаёт ArrayLoader из Twig — это автосвязывание.

Расширение: атрибутная инъекция

Можно не только внедрять через конструктор, но и в свойства класса через атрибуты. Измените car.php так:

cntr::class . "\n";
    }
}

В тестовом коде добавьте вызов:

$car->getCost();

Результат:

I'm a silver Jaguar and have a Twig\Loader\ArrayLoader
I'm a red Jaguar and have a Twig\Loader\ArrayLoader
Class is Apex\Container\Container

Контейнер заметил атрибут Inject и поместил в свойство экземпляр самого контейнера — это полезно, если объекту нужен доступ к фабрике/реестру.

Получение инструментов вручную (get/set)

Иногда удобнее не инжектить всё заранее, а запросить нужные данные из контейнера в момент выполнения.

Пример изменения getCost():

function getCost()
{
    $price = $this->cntr->get('car_price');
    echo "The price is $price\n";
}

В тесте перед вызовом getCost() установите цену:

$cntr->set('car_price', 24995);

Вывод будет примерно:

I'm a silver Jaguar and have a Twig\Loader\ArrayLoader
I'm a red Jaguar and have a Twig\Loader\ArrayLoader
The price is 24995

Примечание: код хранит только число; валюту и формат вывода вы определяете сами (в примере — условно в долларах).

Модели мышления и эвристики при использовании DI

  • Разделяйте ответственность: контейнер управляет созданием/конфигурацией, объекты — бизнес‑логикой.
  • Предпочитайте инъекцию через конструктор для обязательных зависимостей и через свойства/атрибуты для опциональных.
  • Если объект требует много аргументов, подумайте о фасаде или фабричном методе.

Хорошая эвристика: «Если хочется new внутри класса — это повод подумать о DI». Это делает зависимости видимыми и подменяемыми.

Когда DI не подходит (контрпримеры)

  • Простые скрипты/утилиты: DI может добавить лишний уровень абстракции в одноразовых скриптах.
  • Очень мелкие классы с одной‑двумя зависимостями — иногда проще вручную передать зависимости.
  • Когда архитектура строго модульна и внедрение приводит к циклическим зависимостям — лучше пересмотреть дизайн.

Важно: DI помогает, но не заменяет рефакторинг плохих архитектур.

Альтернативные подходы

  • Service Locator: объект, из которого классы сами запрашивают зависимости. Проще, но скрывает зависимости и ухудшает тестируемость.
  • Ручная фабрика: централизованный набор фабрик, создающих сложные объекты по мере необходимости.
  • Контейнеры с конфигурационными файлами (YAML, PHP arrays) — дают явную карту зависимостей.

Выбор зависит от размера проекта, команды и требований к тестируемости.

Мини‑методология внедрения DI в проект (шаги)

  1. Выделить сервисы и зависимости в небольшом модуле.
  2. Добавить контейнер (например, Apex) и зарегистрировать базовые сервисы.
  3. Поменять создание объектов на контейнер.make()/container.get().
  4. Добавить тесты с моками через injection точек.
  5. Постепенно рефакторить остальные модули.

Дерево решений (Mermaid)

flowchart TD
  A[Нужно ли DI?] -->|Проект > 1 разработчика| B{Сложность зависимостей}
  B -->|Много| C[Использовать DI контейнер]
  B -->|Мало| D[Ручная передача через конструктор]
  A -->|Нет, одноразовый скрипт| E[Не использовать DI]
  C --> F{Требуются автосвязывание и атрибуты?}
  F -->|Да| G[Выбрать контейнер с autowiring]
  F -->|Нет| H[Рассмотреть фабрики/Service Locator]

Чек‑лист по ролям

Разработчик:

  • Заменить new на container.make() там, где появляются зависимости.
  • Делать зависимости явными в конструкторе.
  • Писать модульные тесты с подменой зависимостей.

Архитектор:

  • Определить корневой набор сервисов и политики жизненного цикла.
  • Выбрать стратегию конфигурации контейнера (код или файлы).

QA/DevOps:

  • Убедиться, что контейнер корректно работает в средах (dev/staging/prod).
  • Проверить время старта приложения при создании большого числа сервисов.

Критерии приёмки

  • Сервисы создаются контейнером, а не через new внутри бизнес‑логики.
  • Модули покрыты тестами, где зависимости подменены моками.
  • Нет скрытых зависимостей (все обязательные зависимости видны в конструкторе).

Краткий словарь (1‑строчная справка)

  • Контейнер — центр управления зависимостями; создаёт и хранит сервисы.
  • Autowiring — автоматическое сопоставление типов и создание экземпляров.
  • Атрибутная инъекция — пометка свойства атрибутом для автоматического внедрения.

Edge‑case gallery (загрраничные случаи и подсказки)

  • Циклическая зависимость: распознать и разорвать через фабрику или ленивую загрузку.
  • Конфигурация, зависящая от окружения: регистрируйте разные реализации в зависимости от env.
  • Долгая инициализация сервисов: использовать ленивую загрузку (lazy) или отложенную инициализацию.

Безопасность и приватность

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

Краткое резюме

  • DI делает зависимости явными и управляемыми — это улучшает тестируемость и поддерживаемость.
  • Apex Container показывает базовую работу DI: set(), get(), make(), автосвязывание и атрибутная инъекция.
  • Не применять DI там, где он усложняет простые сценарии; выбирать инструменты под размер проекта.

Важно: начните с малого — извлеките несколько сервисов в контейнер, добавьте тесты, и постепенно расширяйте использование DI.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Лучшие онлайн‑конвертеры файлов — выбор и советы
Инструменты

Лучшие онлайн‑конвертеры файлов — выбор и советы

Индексные карточки в Word
Productivity

Индексные карточки в Word

Автоматизация поиска и замены в Word
Office automation

Автоматизация поиска и замены в Word

Интеграция Excel и Word через VBA
Office automation

Интеграция Excel и Word через VBA

32‑бит против 64‑бит Windows: что выбрать
Windows

32‑бит против 64‑бит Windows: что выбрать

Как скрывать данные в Excel — полное руководство
Productivity

Как скрывать данные в Excel — полное руководство