Lift v1.3.0

Отладочная панель

Lift поставляет встроенную в браузер отладочную панель — закреплённую панель, которая накладывается на ваши HTML-страницы с таймингами, данными запроса/ответа, запросами к базе данных, сообщениями логов и страницами неперехваченных исключений. По умолчанию выключена; защищена $app->debug(true), поэтому не может случайно попасть в продакшен.

Ментальная модель: сессия отладки — это всего три части: DebugCollector, который записывает информацию во время запроса, DebugToolbarMiddleware, который внедряет отрендеренный HTML в ответы, и ErrorHandler, который преобразует исключения в насыщенные HTML-страницы вместо простых 500.

Включение

use Lift\Config\Env;

$app->debug([
    'enabled'          => Env::bool('APP_DEBUG', false),     // главный переключатель
    'toolbar'          => true,                              // рендерить HTML-панель
    'position'         => 'bottom-right',                    // или 'bottom-left'
    'only_html'        => true,                              // пропускать JSON/text/бинарные ответы
    'track_php_errors' => true,                              // захватывать warning/notice
    'exception_pages'  => true,                              // красивые HTML-страницы 500
    'hide' => [
        'headers' => ['Authorization', 'Cookie', 'Set-Cookie'],
        'params'  => ['password', 'token', 'secret'],
    ],
]);

Или короткая форма:

$app->debug(true);                       // включить со значениями по умолчанию
$app->debug(Env::bool('APP_DEBUG', false));

Всегда выводите enabled из переменной окружения. Жёстко прописанный true утечёт пути к исходному коду, env-переменные и трассировки стека при следующем деплое.

Что она показывает

Небольшой значок появляется в нижнем углу каждого HTML-ответа. Кликните по нему, чтобы развернуть:

  • Запрос — метод, путь, имя маршрута, контроллер, параметры маршрута, заголовки (с редактированием).
  • Ответ — статус, тип содержимого, заголовки, размер, время рендеринга.
  • Сессия — текущие ключи/значения (когда сессия активна).
  • Запросы — каждый выполненный во время запроса SQL-оператор, с привязками, длительностью и местом вызова.
  • Логи — каждая строка PSR-3 лога, выданная во время запроса.
  • Ошибки PHP — захваченные warning/notice, не переросшие в исключения.
  • Тайминг — разбивка времени загрузки, диспетчеризации, рендеринга.
  • Память — пиковая / текущая.

Список «hide» редактирует чувствительные заголовки/параметры, чтобы скриншоты было безопасно показывать.

Как работает внедрение

DebugToolbarMiddleware выполняется в самом конце конвейера. После того как ваш обработчик вернулся, он:

  1. Проверяет DebugConfig::shouldInject() (пропуская HEAD, 204/304, не-HTML, X-Debug-Toolbar: off, …).
  2. Рендерит HTML панели из собранных данных.
  3. Внедряет его перед закрывающим тегом </body> ответа.

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

X-Debug-Toolbar: off

— полезно для инструментов снятия полностраничных скриншотов, бенчмарков производительности и т. д.

Страницы исключений

Когда exception_pages: true и неперехваченный Throwable достигает ErrorHandler::handle(), Lift рендерит детальную HTML-страницу с:

  • Класс исключения + сообщение + код состояния.
  • Файл / строка выброса, с предпросмотром исходного кода (~10 строк).
  • Полная трассировка стека, каждый кадр разворачивается в собственный предпросмотр исходника.
  • Панель осмотра запроса (те же данные, что и в панели).
  • Ссылки «открыть в редакторе» (vscode://, phpstorm://) на каждом кадре.

Отключите для конкретного окружения:

'exception_pages' => $app->environment() === 'local',

Для JSON API вам обычно нужны и toolbar: false, и exception_pages: false — ответы об ошибках должны оставаться JSON. Middleware уважает эти флаги независимо.

Лог SQL-запросов

$db->onQuery(...) — это то, что питает панель запросов. Lift подключает его автоматически, когда отладка включена и зарегистрирован Connection. Чтобы прикрепить вручную для продвинутых настроек:

$collector = $app->container()->get(\Lift\Debug\DebugCollector::class);
$db->onQuery(fn($sql, $bindings, $ms) => $collector->addQuery($sql, $bindings, $ms));

Каждая строка показывает:

  • SQL с плейсхолдерами ?.
  • Фактический массив $bindings.
  • Время выполнения в мс.

Захват логов

DebugLogHandler — это крошечный обработчик PSR-3, который пересылает каждую строку лога в коллектор. Чтобы включить:

$logger = new \Lift\Log\Logger([
    new \Lift\Log\Handler\FileHandler('storage/logs/app.log'),
    new \Lift\Debug\DebugLogHandler($collector),       // только когда отладка включена
]);

В типичной начальной загрузке:

$app->singleton(\Psr\Log\LoggerInterface::class, function () use ($app) {
    $handlers = [new FileHandler('storage/logs/app.log', 'info')];

    if ($app->container()->has(\Lift\Debug\DebugCollector::class)) {
        $handlers[] = new \Lift\Debug\DebugLogHandler(
            $app->container()->get(\Lift\Debug\DebugCollector::class),
        );
    }

    return new Logger($handlers);
});

Собственные записи коллектора

Любое место в вашем коде может записать в коллектор:

$collector = $app->container()->get(\Lift\Debug\DebugCollector::class);
$collector->addTiming('ai.completion', 1234.5);
$collector->addContext('feature_flags', $flags);

Они появляются в панели под общей панелью «App».

Безопасность в продакшене

Чек-лист на день деплоя:

  • APP_DEBUG=false в вашем продакшен-.env.
  • $app->debug(Env::bool('APP_DEBUG', false))никогда $app->debug(true).
  • ✅ Чувствительные заголовки/параметры в списке hide (Authorization, Cookie, password, token, secret — значения по умолчанию Lift уже их покрывают).
  • OPcache.save_comments = 1 — нормально; панели это конкретно не нужно (нужно маршрутизации через атрибуты).
  • ❌ Не включайте её через $_GET['debug'] = true — это утечёт залогиненным пользователям.

Когда отладка отключена:

  • DebugCollector не строится.
  • Middleware замыкается накоротко (без внедрения).
  • Страницы ошибок проваливаются к вашему обработчику $app->onError(...) (или к JSON 500 Lift по умолчанию).

Результат: нулевые накладные расходы в продакшене.

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

Включённая, панель добавляет ~1–3 мс накладных расходов на запрос (сбор + рендеринг HTML). Отключённая, она добавляет ноль — middleware даже не зарегистрирован, коллектор не сконструирован. Не привязывайте решение о выпуске к соображениям производительности; привязывайте его к безопасности.

Справочник по конфигурации

$app->debug([
    'enabled'           => false,     // главный переключатель
    'toolbar'           => true,      // внедрять HTML-панель
    'position'          => 'bottom-right',  // или 'bottom-left'
    'only_html'         => true,      // пропускать не-HTML ответы
    'track_php_errors'  => true,      // захватывать warning / notice
    'exception_pages'   => true,      // рендерить насыщенный HTML для неперехваченных исключений
    'hide' => [
        'headers' => ['Authorization', 'Cookie', 'Set-Cookie'],
        'params'  => ['password', 'password_confirmation', 'token', 'secret'],
    ],
]);
Ключ По умолчанию Эффект
enabled false Главный переключатель. Без него ничего не работает.
toolbar true Внедрять HTML-панель.
position 'bottom-right' 'bottom-right' или 'bottom-left'.
only_html true Пропускать JSON/бинарные ответы.
track_php_errors true Захватывать E_NOTICE / E_WARNING.
exception_pages true Красивая страница 500 при неперехваченных исключениях.
hide.headers ['Authorization', 'Cookie', 'Set-Cookie'] Маскировать эти заголовки запроса/ответа.
hide.params ['password', 'token', 'secret', …] Маскировать эти параметры query / тела / маршрута.

Советы

  • За CDN? Обходите кэш на отладочных запросах (Cache-Control: private, no-store) — иначе закэшированная страница не будет включать вашу панель.
  • JSON API в разработке? Установите only_html: false, и панель попытается внедриться всё равно. Большинство команд держат only_html: true и просто используют функцию страниц исключений для отладки API.
  • Медленный рендеринг страницы? Откройте панель Тайминг — она обычно сужает это до конкретного middleware / обработчика / запроса.

Частые подводные камни

Симптом Причина Исправление
Панель не появляется ни на одной странице enabled: false (часто через env) Установите APP_DEBUG=true в .env.
Панель не появляется на JSON-эндпоинтах only_html: true (правильное поведение) Либо переключите на false (редко полезно), либо используйте страницы исключений.
X-Debug-Toolbar: off от инструмента скриншотов блокирует отладку посреди теста Заголовок отправлен непреднамеренно Уберите заголовок в настройке теста.
Чувствительный заголовок / параметр всё ещё виден Не в списке hide.headers/hide.params Добавьте его явно.
Панель показывает ноль SQL-запросов Соединение с БД не было построено через контейнер (или связка onQuery пропущена) Убедитесь, что один и тот же экземпляр Connection и обслуживает ваши обработчики, и зарегистрирован в коллекторе.
Страница исключения утекает значения .env exception_pages: true в продакшене Привяжите её к окружению, никогда не прописывайте true жёстко.

Шпаргалка

// Включить (всегда через env-переключатель!)
$app->debug([
    'enabled'         => Env::bool('APP_DEBUG', false),
    'toolbar'         => true,
    'exception_pages' => true,
]);

// Выключить на запрос
// curl -H 'X-Debug-Toolbar: off' …

// Собственный тайминг
$collector->addTiming('llm.call', $ms);

// Собственная панель контекста
$collector->addContext('features', $flagsArray);

// Логгер, питающий панель
new Logger([
    new FileHandler('storage/logs/app.log'),
    new DebugLogHandler($collector),
]);

Асинхронность (Fibers) →