Внедрение зависимостей и Рефлексия (Dependency Injection)
ZenithGram поддерживает современный подход к написанию кода через Dependency Injection (DI). Это позволяет писать чистые, типизированные обработчики, избавляя вас от рутинного извлечения данных из объекта $tg.
По умолчанию выключено
Из коробки библиотека работает в стандартном режиме (максимальная производительность). В этом режиме в обработчик роутера передаётся объект ZG и специфичные для каждого аргументы.
Чтобы использовать DI и Type Hinting, вы должны явно включить рефлексию (см. ниже).
Сравнение подходов
Стандартный подход (Без рефлексии)
Вам приходится вручную доставать данные. Аргументы обработчиков для каждого маршрута определены заранее.
$bot->onCommand('start', '/start')->func(function(ZG $tg) {
// Приходится писать лишний код
$user = $tg->getUser();
$chat = $tg->getChat();
$tg->msg("Привет, {$user->firstName}!")->send();
});Подход с Рефлексией (Dependency Injection)
Вы просто указываете в аргументах функции Тип того, что вам нужно. Библиотека сама найдет, создаст и передаст этот объект.
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();
});Как включить рефлексию?
Есть три способа активации, в зависимости от сложности вашего проекта:
- Базовая активация:
$bot->reflection()— включает авто-подстановку встроенных DTO и параметров маршрута. - С контейнером:
$bot->setContainer($container)— автоматически включает рефлексию и позволяет внедрять ваши сервисы (DB, Logger). - С кешем:
$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}
$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
$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 клиентов) прямо в контроллерах бота.
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) — КЕШ ОБЯЗАТЕЛЕН
На боевом сервере код не меняется. Кеш позволяет проанализировать функции один раз и сохранить схему. Это делает работу рефлексии практически такой же быстрой, как и стандартный режим.
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 (замыкание для продолжения цепочки) также нужно указывать, если вы хотите продолжить выполнение.
$bot->middleware(function (UserDto $user, callable $next, ZG $tg) {
// Автоматически получили UserDto
if ($user->id === 12345) {
$next();
} else {
$tg->msg('Доступ запрещен')->send();
}
});