Dependency Injection

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

Тренажер PHP

Dependency Injection — один из самых важных паттернов в современном PHP. Он помогает писать код, который легко тестировать, расширять и поддерживать. Вместо того чтобы объект сам создавал свои зависимости внутри конструктора или методов, он получает их извне. Здесь мы разберём основные идеи DI: от простого ручного внедрения до использования контейнера и автоподвязки. Пройди задания по порядку — от базовых примеров до более сложных случаев с интерфейсами и контейнерами.

Список тем

1. Простое внедрение зависимости через конструктор

id: 39371_task1

Допишите код класса 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');
Сообщения
Проверить
Показать решение на 3 сек.
Показать подсказку

2. Найдите нарушение Dependency Injection

id: 39371_task2

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

Найдите ошибку и исправьте
<?php
class 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 сек.
Показать подсказку

3. Что выведет код без DI?

id: 39371_task3

Посмотрите на код и определите, что будет выведено. Здесь зависимость создаётся внутри класса — это нарушение 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. Внедрение через интерфейс

id: 39371_task4

Заполните пропуски, чтобы класс 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);
Сообщения
Проверить
Показать решение на 3 сек.
Показать подсказку

5. Плюсы и минусы DI

id: 39371_task5

Сопоставьте утверждения с тем, относятся ли они к преимуществам или недостаткам Dependency Injection.

Сопоставьте строки в правой(нижней) части с соответствующими строками в левой(верхней) по порядковому номеру
Упрощает модульное тестирование
Увеличивает связанность классов
Позволяет легко менять реализации
Требует больше «котельного» кода
Делает зависимости явными
Усложняет понимание новичкам
Преимущество
Недостаток
Преимущество
Преимущество
Недостаток
Недостаток
Сообщения
Проверить
Показать подсказку

6. Соберите класс с DI из фрагментов

id: 39371_task6

Перетащите строки в правильном порядке, чтобы получился рабочий класс 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(); // лишняя
Сообщения
Проверить
Показать решение на 3 сек.
Показать подсказку

7. Заполните контейнер зависимостей

id: 39371_task7

Используя готовые токены из банка, заполните простейший DI-контейнер. Перетащите нужные куски в пропуски. В банке есть лишние элементы.

Нужно правильно расставить в пропуски предложенные варианты
$container = [
    LoggerInterface::class => input1S,
    input2S => PaymentGatewayLive::class,
    'mailer.transport' => input3S,
];

$logger = $container[LoggerInterface::class];
FileLogger::class
PaymentGatewayInterface::class
SmtpTransport::class
PaymentGatewayTest::class
Database::class
Сообщения
Проверить
Показать решение на 3 сек.
Показать подсказку

8. Автоподвязка в Pimple-контейнере

id: 39371_task8

Определите, что выведет код при использовании 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

id: 39371_task9

Реализуйте внедрение зависимости через сеттер (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!");
        }
    }
}
Сообщения
Проверить
Показать решение на 3 сек.
Показать подсказку

10. Финальный тест: что выведет полностью DI-код?

id: 39371_task10

Внимательно посмотрите на код с полным внедрением зависимостей через конструктор и контейнер. Что будет выведено в результате?

Что должно получиться?
$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);
Сообщения
Проверить
Показать подсказку

PHP: запуск кода в браузере

id: 39371_compiler
🐘
Запустить тренажёр (PHP)
НайтиКурс.Ру