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

Middleware (Промежуточное ПО)

Middleware (промежуточное ПО) — это мощный механизм, который позволяет вам выполнять код до того, как будет обработан основной обработчик маршрута. Это функции, которые встраиваются в цепочку обработки запроса и могут модифицировать его, проверять данные или прерывать выполнение, если определённые условия не выполнены.

Основные сценарии использования Middleware:

  • Аутентификация и авторизация: проверка, имеет ли пользователь доступ к определённой команде или функции.
  • Логирование: запись информации о входящих запросах.
  • Подготовка данных: загрузка данных пользователя из базы данных и передача их в основной обработчик.
  • Ограничение частоты запросов (Rate Limiting): предотвращение спама от пользователей.

В библиотеке существует два типа Middleware: глобальное и для конкретного маршрута.

Глобальное Middleware

Глобальное Middleware устанавливается на весь экземпляр бота с помощью метода $bot->middleware() и выполняется для каждого входящего обновления, прежде чем библиотека начнёт поиск подходящего маршрута.

Сигнатура

php
public function middleware(callable $handler): void

Обработчик Middleware принимает два аргумента:

  1. $tg (ZhenyaGR\TGZ\TGZ): Экземпляр основного класса TGZ, предоставляющий доступ ко всему контексту и методам API.
  2. $next (Closure): Функция обратного вызова. Вы должны вызвать $next(), чтобы передать управление дальше по цепочке (к поиску маршрута). Если $next() не будет вызван, обработка обновления прекратится.

Пример: Проверка регистрации пользователя

Представим, что у вас есть бот, которым могут пользоваться только зарегистрированные пользователи. Глобальное Middleware — идеальное место для такой проверки.

php
<?php
require 'vendor/autoload.php';

use ZhenyaGR\TGZ\TGZ;

// Предположим, у нас есть функция для проверки пользователя в БД
function isUserRegistered(int $user_id): bool {
    // ... ваша логика проверки в базе данных
    // Для примера, разрешим доступ только пользователю с ID 12345
    return $user_id === 12345;
}

$bot = new TGZ('YOUR_BOT_TOKEN');

// Устанавливаем глобальное Middleware
$bot->middleware(function (TGZ $tg, Closure $next) {
    $user_id = $tg->getUserID();

    if ($user_id && !isUserRegistered($user_id)) {
        // Если пользователь не зарегистрирован, отправляем ему сообщение
        // и НЕ вызываем $next(), прерывая дальнейшую обработку.
        $tg->msg('Извините, для использования этого бота требуется регистрация.')->send();
        return;
    }

    // Если проверка пройдена, передаем управление дальше
    $next();
});


$bot->onBotCommand('/start')->text('Привет, зарегистрированный пользователь!');
$bot->onBotCommand('/help')->text('Это раздел помощи.');

$bot->run();

В этом примере, если пользователь с ID, отличным от 12345, напишет боту /start или любую другую команду, он получит сообщение о необходимости регистрации, а сам обработчик команды не выполнится.

Middleware для маршрута

Иногда проверку нужно выполнить только для определённой команды или группы команд. Для этого можно использовать Middleware на уровне конкретного маршрута, применив метод middleware() к объекту Action.

Такое Middleware выполняется после того, как маршрут был успешно сопоставлен с запросом, но до вызова основного обработчика, заданного методом func().

Сигнатура

php
public function middleware(callable $handler): Action

Обработчик также принимает аргументы $tg и $next.

Пример: Команды только для администратора

Допустим, у нас есть команда /admin, которая должна быть доступна только администраторам бота.

php
<?php
require 'vendor/autoload.php';

use ZhenyaGR\TGZ\TGZ;

const ADMIN_IDS = [12345, 54321]; // Список ID администраторов

$bot = new TGZ('YOUR_BOT_TOKEN');

$bot->onBotCommand('/start')->text('Добро пожаловать!');

// Создаем маршрут для админ-панели и добавляем к нему Middleware
$bot->onBotCommand('/admin')
    ->middleware(function (TGZ $tg, Closure $next) {
        $user_id = $tg->getUserID();

        if (!in_array($user_id, ADMIN_IDS)) {
            // Если пользователь не админ, отправляем сообщение
            // и НЕ вызываем $next().
            $tg->msg('⛔️ У вас нет прав доступа к этой команде.')->send();
            return;
        }

        // Пользователь - админ, продолжаем выполнение
        $next();
    })
    ->func(function (TGZ $tg) {
        // Этот код выполнится только если Middleware вызовет $next()
        $tg->msg('Добро пожаловать в панель администратора!')->send();
    });


$bot->run();

Важно

В отличие от методов access() и noAccess(), которые являются простыми проверками по ID, Middleware позволяет реализовать гораздо более сложную логику: запросы к базе данных, проверку подписки на канал, проверку времени суток и многое другое.

Порядок выполнения

Чтобы лучше понять, как все работает вместе, вот последовательность выполнения:

  1. Приходит новое обновление от Telegram.
  2. Выполняется глобальное Middleware (если установлено).
    • Если оно не вызывает $next(), обработка прекращается.
  3. Библиотека ищет подходящий маршрут (onBotCommand, onText и т.д.).
  4. Если маршрут найден, выполняется Middleware этого маршрута (если установлено).
    • Если оно не вызывает $next(), обработка прекращается.
  5. Выполняется основной обработчик маршрута (установленный через func() или встроенная отправка сообщения через text(), kbd() и т.д.).

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