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) →