Dependency Injection — один из самых важных паттернов в современном PHP. Он помогает писать код, который легко тестировать, расширять и поддерживать. Вместо того чтобы объект сам создавал свои зависимости внутри конструктора или методов, он получает их извне. Здесь мы разберём основные идеи DI: от простого ручного внедрения до использования контейнера и автоподвязки. Пройди задания по порядку — от базовых примеров до более сложных случаев с интерфейсами и контейнерами.
- Модуль 1: Основы синтаксиса PHP
- Модуль 2: Переменные и типы данных
- Модуль 3: Операторы
- Модуль 4: Условные конструкции
- Модуль 5: Циклы
- Модуль 6: Массивы
- Индексированные массивы.
- Ассоциативные массивы.
- Многомерные массивы.
- Добавление элементов в массив.
- Удаление элементов из массива.
- Функция count().
- Функция array_push() и array_pop().
- Функция array_shift() и array_unshift().
- Функция array_merge().
- Функция array_slice().
- Функция array_splice().
- Функция in_array().
- Функция array_search().
- Функция array_keys() и array_values().
- Функция array_unique().
- Функция array_reverse().
- Сортировка: sort(), rsort().
- Сортировка: asort(), arsort().
- Сортировка: ksort(), krsort().
- Функция array_map().
- Функция array_filter().
- Функция array_reduce().
- Функция array_walk().
- Функция array_column().
- Деструктуризация массивов.
- Iterator интерфейс
- Модуль 7: Функции
- Объявление функций.
- Вызов функций.
- Параметры функций.
- Возврат значений return.
- Значения параметров по умолчанию.
- Передача по значению.
- Передача по ссылке.
- Переменное количество аргументов.
- Оператор распаковки ....
- Типизация параметров.
- Типизация возвращаемых значений.
- Nullable типы.
- Union типы (PHP 8).
- Именованные аргументы (PHP 8).
- Анонимные функции.
- Стрелочные функции.
- Замыкания и use.
- Рекурсивные функции.
- Глобальные переменные в функциях.
- Статические переменные в функциях.
- IteratorAggregate интерфейс
- Модуль 8: Работа со строками
- Одинарные и двойные кавычки.
- Экранирование символов.
- Heredoc и Nowdoc синтаксис.
- Функция strlen().
- Функция substr().
- Функция str_replace().
- Функция strpos() и strrpos().
- Функция explode() и implode().
- Функция trim(), ltrim(), rtrim().
- Функция strtolower() и strtoupper().
- Функция ucfirst() и ucwords().
- Функция str_repeat().
- Функция htmlspecialchars().
- Модуль 9: Суперглобальные переменные
- Модуль 10: Работа с формами
- Модуль 11: Работа с файлами
- Модуль 12: Дата и время
- Модуль 13: Регулярные выражения
- Модуль 14: Сессии и Cookie
- Модуль 15: Include и Require
- Модуль 16: Объектно-ориентированное программирование
- Модуль 17: Пространства имен
- Модуль 18: Обработка ошибок и исключений
- Модуль 19: Работа с JSON и XML
- Модуль 21: PDO - PHP Data Objects
- Модуль 22: Composer и зависимости
- Модуль 23: cURL и HTTP запросы
- Модуль 24: REST API
- Модуль 25: Безопасность
- Модуль 28: Работа с изображениями
- Модуль 29: Отправка email
- Модуль 30: Паттерны проектирования
- Singleton паттерн.
- Factory паттерн.
- Dependency Injection.
- MVC паттерн.
- Модуль 31: Тестирование
- Модуль 32: Продвинутые возможности PHP 8+
1. Простое внедрение зависимости через конструктор
Допишите код класса UserService так, чтобы он получал объект Logger через конструктор и сохранял его в свойство. Это классический пример Dependency Injection.
class UserService
{
private input1S $logger;
public function __construct(input2S $logger)
{
$this->logger = input3S;
}
public function register(string $name)
{
// регистрация пользователя...
$this->logger->log("Зарегистрирован пользователь: $name");
}
}
$logger = new FileLogger();
$service = new UserService(input4S);
$service->register('Anna');2. Найдите нарушение Dependency Injection
В коде есть типичная ошибка — объект создаёт свою зависимость внутри себя. Исправьте это, чтобы зависимость передавалась через конструктор.
<?phpclass UserRepository{ private $db; public function __construct() { $this->db = new Database(); } public function find($id) { return $this->db->query("SELECT * FROM users WHERE id = $id"); }}3. Что выведет код без DI?
Посмотрите на код и определите, что будет выведено. Здесь зависимость создаётся внутри класса — это нарушение DI.
class Mailer
{
public function send($to, $message)
{
$transport = new SmtpTransport();
$transport->send($to, $message);
echo "Отправлено через SMTP";
}
}
$mailer = new Mailer();
$mailer->send('test@example.com', 'Привет');4. Внедрение через интерфейс
Заполните пропуски, чтобы класс PaymentProcessor мог работать с любым платёжным шлюзом, реализующим интерфейс PaymentGateway.
interface PaymentGateway
{
public function charge(float $amount): bool;
}
class PaymentProcessor
{
private input1S $gateway;
public function __construct(input2S $gateway)
{
$this->gateway = input3S;
}
public function pay(float $amount): bool
{
return $this->gateway->input4S($amount);
}
}
$paypal = new PaypalGateway();
$processor = new PaymentProcessor(input5S);
$processor->pay(99.90);5. Плюсы и минусы DI
Сопоставьте утверждения с тем, относятся ли они к преимуществам или недостаткам Dependency Injection.
6. Соберите класс с DI из фрагментов
Перетащите строки в правильном порядке, чтобы получился рабочий класс OrderProcessor с внедрением зависимостей Logger и PaymentGateway через конструктор. Лишние строки тоже есть.
class OrderProcessor private Logger $logger; private PaymentGateway $gateway; public function __construct(Logger $logger, PaymentGateway $gateway) { $this->logger = $logger; $this->gateway = $gateway; } public function process(Order $order) { $this->gateway->charge($order->total()); $this->logger->info('Order processed: ' . $order->id()); }} private $db; // лишняя $logger = new FileLogger(); // лишняя7. Заполните контейнер зависимостей
Используя готовые токены из банка, заполните простейший DI-контейнер. Перетащите нужные куски в пропуски. В банке есть лишние элементы.
$container = [
LoggerInterface::class => input1S,
input2S => PaymentGatewayLive::class,
'mailer.transport' => input3S,
];
$logger = $container[LoggerInterface::class];8. Автоподвязка в Pimple-контейнере
Определите, что выведет код при использовании Pimple (простого DI-контейнера).
$c = new Pimple\Container();
$c['logger'] = function ($c) {
return new FileLogger();
};
$log1 = $c['logger'];
$log2 = $c['logger'];
var_dump($log1 === $log2); // что будет?9. Setter Injection
Реализуйте внедрение зависимости через сеттер (Setter Injection). Заполните пропуски.
class NewsletterManager
{
private ?input1S $notifier = null;
public function input2S(input3S $notifier): void
{
$this->notifier = input4S;
}
public function subscribe(string $email)
{
// ... подписка
if ($this->notifier) {
$this->notifier->send("Добро пожаловать, $email!");
}
}
}10. Финальный тест: что выведет полностью DI-код?
Внимательно посмотрите на код с полным внедрением зависимостей через конструктор и контейнер. Что будет выведено в результате?
$container = new class implements Psr\Container\ContainerInterface {
public function get($id) {
return match($id) {
LoggerInterface::class => new class implements LoggerInterface {
public function log($message) { echo "LOG: $message\n"; }
// остальные методы опущены
},
PaymentGateway::class => new StripeGateway(),
};
}
public function has($id) { return true; }
};
$processor = new PaymentProcessor(
$container->get(LoggerInterface::class),
$container->get(PaymentGateway::class)
);
$processor->process(1500.00);