Перейти к содержимому

Внедрение зависимостей и Рефлексия (Dependency Injection)

ZenithGram поддерживает современный подход к написанию кода через Dependency Injection (DI). Это позволяет писать чистые, типизированные обработчики, избавляя вас от рутинного извлечения данных из объекта $tg.

По умолчанию выключено

Из коробки библиотека работает в стандартном режиме (максимальная производительность). В этом режиме в обработчик роутера передаётся объект ZG и специфичные для каждого аргументы.

Чтобы использовать DI и Type Hinting, вы должны явно включить рефлексию (см. ниже).

Сравнение подходов

Стандартный подход (Без рефлексии)

Вам приходится вручную доставать данные. Аргументы обработчиков для каждого маршрута определены заранее.

php
$bot->onCommand('start', '/start')->func(function(ZG $tg) {
    // Приходится писать лишний код
    $user = $tg->getUser(); 
    $chat = $tg->getChat();
    
    $tg->msg("Привет, {$user->firstName}!")->send();
});

Подход с Рефлексией (Dependency Injection)

Вы просто указываете в аргументах функции Тип того, что вам нужно. Библиотека сама найдет, создаст и передаст этот объект.

php
use ZenithGram\ZenithGram\Dto\UserDto;
use ZenithGram\ZenithGram\Dto\ChatDto;

// Включаем рефлексию
$bot->reflection(); 

$bot->onCommand('start', '/start')
    ->func(function(UserDto $user, ChatDto $chat, ZG $tg) {
        // $user и $chat уже заполнены данными!
        // Порядок аргументов не важен.
        $tg->msg("Привет, {$user->firstName}!")->send();
    });

Как включить рефлексию?

Есть три способа активации, в зависимости от сложности вашего проекта:

  1. Базовая активация: $bot->reflection() — включает авто-подстановку встроенных DTO и параметров маршрута.
  2. С контейнером: $bot->setContainer($container) — автоматически включает рефлексию и позволяет внедрять ваши сервисы (DB, Logger).
  3. С кешем: $bot->setCache($cache) — автоматически включает рефлексию (для продакшена).

Что можно внедрять автоматически?

Библиотека умеет распознавать и внедрять следующие классы:

Класс / ИнтерфейсОписание
ZenithGram\ZenithGram\ZGОсновной объект библиотеки.
ZenithGram\ZenithGram\Dto\UserDtoОбъект с данными пользователя (из поля from).
ZenithGram\ZenithGram\Dto\ChatDtoОбъект с данными чата.
ZenithGram\ZenithGram\Dto\MessageDtoПолный объект сообщения.
ZenithGram\ZenithGram\UpdateContextСырой контекст текущего обновления.

Аргументы маршрутов (Named Parameters)

Рефлексия позволяет элегантно работать с переменными в командах и кнопках. Имя переменной в функции должно совпадать с именем плейсхолдера в маршруте.

Пример с командой

Маршрут: /gift {username} {amount}

php
$bot->onCommand('gift', '/gift {username} {amount}')
    ->func(function (ZG $tg, string $username, int $amount) {
        // Библиотека распарсит текст команды
        // и передаст значения в переменные $username и $amount
        $tg->msg("Отправлено $amount монет для $username")->send();
    });

Пример с кнопкой (Callback)

Данные кнопки: product_{id}_delete

php
$bot->onCallback('del_prod', 'product_{id}_delete')
    ->func(function (ZG $tg, int $id, UserDto $user) {
        // Мы получили ID товара из кнопки
        // И данные пользователя через DTO
        $tg->msg("Пользователь {$user->firstName} удалил товар #$id")->send();
    });

Работа с внешними сервисами (PSR-11)

Если вы используете PHP-DI, Symfony Container или любой другой PSR-11 совместимый контейнер, вы можете передать его в бота. Это позволит вам запрашивать свои сервисы (базы данных, API клиентов) прямо в контроллерах бота.

php
use DI\ContainerBuilder;

// 1. Настраиваем ваш контейнер (Пример с PHP-DI)
$builder = new ContainerBuilder();
$builder->addDefinitions([
    DatabaseService::class => fn() => new DatabaseService(...),
    LoggerService::class => fn() => new LoggerService(...)
]);
$container = $builder->build();

// 2. Передаем контейнер в бота
// ВНИМАНИЕ: Это автоматически включает режим reflection()
$bot->setContainer($container);

// 3. Используем в коде
$bot->onCommand('balance', '/balance')
    ->func(function (ZG $tg, UserDto $user, DatabaseService $db) {
        // DatabaseService будет автоматически взят из вашего контейнера
        $balance = $db->getUserBalance($user->id);
        $tg->msg("Ваш баланс: $balance")->send();
    });

Кеширование и Производительность

Рефлексия (анализ аргументов функций "на лету") — это тяжелая операция. Она может замедлить бота при высокой нагрузке. Чтобы этого избежать, ZenithGram использует механизм кеширования метаданных функций.

Стратегия использования кеша

В Разработке (Local / Dev) — КЕШ НЕ ИСПОЛЬЗОВАТЬ

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

Правило: На локальной машине просто используйте $bot->reflection() или $bot->setContainer(...) без установки кеша.

В Продакшене (Prod) — КЕШ ОБЯЗАТЕЛЕН

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

php
use ZenithGram\ZenithGram\Utils\SimpleFileCache;

// Пример настройки для Production
if (ENV === 'production') {
    // Встроенный файловый кеш (хранит схему аргументов в файлах)
    $cache = new SimpleFileCache(__DIR__ . '/storage/cache');
    // Установка кеша автоматически включает reflection()
    $bot->setCache($cache);
} else {
    // Для разработки просто включаем рефлексию без кеша
    $bot->reflection();
}

Middleware и Рефлексия

DI работает и в глобальных Middleware, и в Middleware маршрутов. Важно помнить: аргумент $next (замыкание для продолжения цепочки) также нужно указывать, если вы хотите продолжить выполнение.

php
$bot->middleware(function (UserDto $user, callable $next, ZG $tg) {
    // Автоматически получили UserDto
    if ($user->id === 12345) {
        $next();
    } else {
        $tg->msg('Доступ запрещен')->send();
    }
});

Опубликовано под лицензией MIT.