Отладочная панель
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 выполняется в самом конце конвейера. После того как ваш обработчик вернулся, он:
- Проверяет
DebugConfig::shouldInject()(пропуская HEAD, 204/304, не-HTML,X-Debug-Toolbar: off, …). - Рендерит HTML панели из собранных данных.
- Внедряет его перед закрывающим тегом
</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),
]);