Ти змінив/ла один рядок у PHP-файлі, оновив/ла сторінку, а там порожньо: біла сторінка або лаконічне «500 Internal Server Error». Жодного слова про те, що пішло не так. Знайомо? Це один із найчастіших вступних «квестів» у PHP, і розв'язується він просто: треба або увімкнути показ помилок на екрані, або навчитися знаходити їх у журналі. Розберемо обидва шляхи й заодно домовимося, як це робити безпечно.

Чому PHP мовчить

На багатьох серверах (і майже на всіх бойових) показ помилок вимкнено навмисно: користувачам не слід бачити шляхи до файлів, назви баз даних і шматки коду. Тому замість повідомлення виходить порожня сторінка. Помилка при цьому нікуди не зникла: вона або у журналі помилок, або просто ховається, бо налаштування приховують її від очей.

За це відповідають чотири налаштування PHP:

  • display_errors – показувати помилки на екрані чи ні;
  • error_reporting – які саме помилки показувати (для розробки – усі, E_ALL);
  • log_errors – записувати помилки в журнал;
  • error_log – у який файл писати.

Найшвидший спосіб: два рядки в самому скрипті

Коли треба просто швидко побачити помилку, додай на початок файлу:

<?php
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);

Але в цього способу є застереження. Якщо помилка синтаксична (забув/ла крапку з комою чи дужку) в тому самому файлі, PHP навіть не дійде до виконання цих рядків, бо не зможе розібрати файл. Тоді виручає один із способів нижче, або перевірка файлу з терміналу:

php -l index.php

Команда php -l не виконує скрипт, а лише перевіряє синтаксис і вказує рядок із помилкою. Дуже зручно.

Правильний спосіб: php.ini

Для стабільної роботи налаштування краще тримати в php.ini. Спочатку знайди, який саме файл використовується веб-сервером: створи тимчасовий файл із рядком <?php phpinfo();, відкрий його в браузері й знайди рядок Loaded Configuration File. (Команда php --ini у терміналі показує файл для консольного PHP, а він може відрізнятися від того, що використовує сайт.) Після перевірки цей файл із phpinfo видали, бо він розкриває багато зайвого.

У знайденому файлі встанови для розробки:

display_errors = On
display_startup_errors = On
error_reporting = E_ALL
log_errors = On

Збережи й перезапусти веб-сервер або PHP-FPM, інакше нові значення не підхопляться. Для локальних серверів на Windows і Linux розписував окремо: LAMP у Linux Mint та Ubuntu, локальний сервер на Windows, Laragon та Fedora.

Коли доступу до php.ini немає: .htaccess або .user.ini

На спільному хостингу php.ini часто чіпати не можна, але є обхідні шляхи, залежно від того, як на сервері працює PHP:

  • Apache з модулем PHP. У файл .htaccess у корені сайту додай:
    php_flag display_errors on
    php_value error_reporting -1
  • PHP-FPM або CGI. Директиви php_flag тут не діють (вони й можуть спричинити помилку 500). Натомість створи в корені сайту файл .user.ini з рядками:
    display_errors = On
    error_reporting = E_ALL
    Зміни в ньому підхоплюються не миттєво, а через кілька хвилин.

Не знаєш, який у тебе варіант? Спробуй спершу .user.ini; якщо нічого не змінилося, пиши в підтримку хостингу або подивись у phpinfo() рядок Server API: там буде Apache чи FPM/FastCGI.

Де шукати журнал помилок

Якщо показ помилок на екрані вимкнено (а на живому сайті так і має бути), всі вони потрапляють у журнал. Шлях до нього залежить від налаштування error_log. Якщо воно не задане, PHP пише в журнал помилок веб-сервера:

  • Apache: часто /var/log/apache2/error.log;
  • Nginx із PHP-FPM: зазвичай /var/log/nginx/error.log і окремий журнал PHP-FPM;
  • на хостингах і панелях керування журнал є у вебінтерфейсі: розділ «Журнали» чи «Логи».

Свіжі рядки зручно дивитися в реальному часі:

tail -f /var/log/apache2/error.log

Відкрий сторінку, яка ламається, і в терміналі одразу з'явиться причина. Якщо сервером керуєш ти, можна задати окремий файл для помилок кожного сайту. У конфігурації пулу PHP-FPM це виглядає так:

php_admin_flag[log_errors] = on
php_admin_value[error_log] = /шлях/до/сайту/php_error.log

Так журнал кожного сайту лежить окремо й не змішується з іншими, а ще його легко ротувати. Просто не забудь закрити файл журналу від доступу з інтернету, щоб помилки не читали сторонні.

Показувати на екрані чи писати в журнал?

Просте правило:

  • Локально, при розробці – показуй усе на екрані: display_errors = On, E_ALL.
  • На живому сайті – показ вимкнено: display_errors = Off, зате log_errors = On. Так відвідувачі не бачать нічого зайвого, а ти бачиш усе в журналі.

Не залишай display_errors = On на живому сайті: у повідомленні можуть бути шляхи до файлів, ім'я користувача бази, а інколи й частина запиту з паролем. Для зловмисника це золото.

Які бувають рівні помилок

  • Fatal error – критична: виконання скрипта зупиняється. Саме вона найчастіше дає білу сторінку.
  • Parse error – синтаксична помилка, файл не вдалося розібрати.
  • Warning – попередження: скрипт працює далі, але щось не так (наприклад, не вдалося відкрити файл).
  • Notice – зауваження про дрібницю, наприклад, використання змінної, якої ще не існує.
  • Deprecated – функція застаріла й у майбутньому зникне.

Звикай розбирати навіть дрібні попередження: вони часто вказують на справжню помилку в логіці, яка спрацює в найневдаліший момент.

Помилки бази даних і PDO

Якщо працюєш із MySQL через PDO, увімкни режим винятків, інакше помилки запитів можуть пройти непоміченими:

$pdo = new PDO($dsn, $user, $pass, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

Тепер будь-яка помилка в запиті викидатиме виняток із зрозумілим текстом, а не мовчки повертатиме false. Щоб зручно перевіряти запити й структуру таблиць, дуже допомагає phpMyAdmin.

Якщо працюєш із WordPress

У WordPress свої перемикачі в файлі wp-config.php:

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);

З такими значеннями помилки не показуються відвідувачам, а записуються у файл wp-content/debug.log. Після налагодження не забудь вимкнути WP_DEBUG назад.

Швидкий чекліст, якщо сторінка біла

  1. Перевір синтаксис: php -l файл.php.
  2. Додай на початок файлу ini_set('display_errors', '1'); error_reporting(E_ALL);.
  3. Відкрий журнал помилок і подивись останні рядки.
  4. Перевір, чи не вичерпано пам'ять (Allowed memory size exhausted) і чи встановлені потрібні розширення PHP.
  5. Якщо змінював/ла .htaccess, тимчасово перейменуй його: можливо, помилка 500 саме звідти.

Що далі

Коли помилки видно, налагоджувати код стає в рази легше. Далі раджу навчитися працювати з базою через PDO й безпечно писати запити, а для відправлення дописів із сайту в соцмережі зазирни в мої статті про автопостинг у Telegram на PHP та автопостинг у Facebook: там помилки API видно саме завдяки журналам.

Більше про це – за позначками PHP та Помилки та коди помилок.

А яка найдивніша помилка PHP тобі траплялась? Розкажи в коментарях 🙂