Панель налагодження
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),
]);