Ошибки PHP с кодами: управление обработчиками и журналом
Код ошибки PHP: типы, вывод и перехват
Каждая ошибка PHP имеет числовой код, который указывает на серьёзность проблемы. Код используется внутри интерпретатора, в сообщениях об ошибках и в пользовательских обработчиках. Понимание этих кодов позволяет настроить вывод и запись событий так, чтобы отладка не мешала работе сайта.
Как включить показ и журналирование ошибок с их кодами?
Основной подход для локальной разработки состоит в том, чтобы включить вывод всех ошибок и одновременно записывать их в файл. Для боевого сервера вывод на экран лучше отключить, оставив только журнал.
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/php-error.log');Php код ошибки (код ошибки php)
Первая команда задаёт битовую маску E_ALL. Она включает все известные уровни ошибок. Затем display_errors разрешает печать ошибок в браузер или консоль. display_startup_errors действует на ошибки, возникающие при старте PHP. log_errors включает запись в файл, имя которого задаёт error_log.
Почему сообщение с кодом ошибки не появляется?
Если error_reporting включён, но текст не виден, причина обычно в значении display_errors. На некоторых хостингах ini_set('display_errors', '1') запрещено. Тогда ошибку можно увидеть только в журнале. Для этого log_errors должен быть равен 1, а error_log указывать на доступный для записи файл.
Как отфильтровать определённые коды ошибок?
Коды ошибок объединяются в битовые маски. Для отключения уведомлений и замечаний об устаревшем коде используется исключающий оператор.
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);Php mysql ошибки (ошибки php mysql)
Такой вариант подходит для проектов, где старый код генерирует много второстепенных предупреждений. Он отключает только уведомления E_NOTICE и E_DEPRECATED, оставляя остальные ошибки видимыми.
Как перехватить код ошибки и преобразовать его в исключение?
Для нефатальных ошибок можно установить обработчик, который выбрасывает ErrorException. Это позволяет применять единый механизм try/catch.
set_error_handler(function ($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
return false;
}
throw new ErrorException($message, $severity, $severity, $file, $line);
});
try {
trigger_error('Пользовательская ошибка', E_USER_NOTICE);
} catch (ErrorException $e) {
echo 'Код ' . $e->getCode() . ': ' . $e->getMessage();
}В обработчике проверяется текущая маска error_reporting. Если код не входит в маску, возвращается false, и PHP использует стандартную обработку. Когда обработчик выбрасывает исключение, код попадает в catch, где через getCode() доступен числовой код.
Как определить код фатальной ошибки после остановки скрипта?
Фатальные ошибки не проходят через set_error_handler. Их отслеживает функция, зарегистрированная через register_shutdown_function. Ошибка берётся из error_get_last().
register_shutdown_function(function () {
$last = error_get_last();
if ($last !== null) {
echo 'Фатальный код: ' . $last['type'] . PHP_EOL;
echo 'Сообщение: ' . $last['message'];
}
});После завершения скрипта error_get_last() возвращает массив с последней ошибкой. Если остановка вызвана E_ERROR, в массиве будет тип E_ERROR. Такой же способ работает для E_PARSE и E_COMPILE_ERROR.
Как создать собственную ошибку с нужным кодом?
Функция trigger_error позволяет вызвать ошибку с одним из пользовательских кодов: E_USER_ERROR, E_USER_WARNING или E_USER_NOTICE.
trigger_error('Недостаточно данных', E_USER_WARNING);Это удобно для проверки обработчиков и логирования. Сторонний код тоже может вызывать такие ошибки, если в нём есть вызовы trigger_error.
Почему set_error_handler не обрабатывает фатальные ошибки?
Обработчик ошибок в PHP не вызывается для E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR. Для этих кодов применяется shutdown-перехват. В PHP 7 и новее многие фатальные сбои представлены классом Error, поэтому их допускается ловить через catch (Throwable $e).
Полный список констант с кодами ошибок
Встроенные константы находятся в массиве Core. Их можно вывести с помощью get_defined_constants.
$constants = get_defined_constants(true);
$errorConstants = $constants['Core'] ?? [];
foreach ($errorConstants as $name => $value) {
if (strpos($name, 'E_') === 0) {
echo $name . ' = ' . $value . PHP_EOL;
}
}Результат (фрагмент для PHP 8):
E_ERROR = 1 E_WARNING = 2 E_PARSE = 4 E_NOTICE = 8 E_CORE_ERROR = 16 E_CORE_WARNING = 32 E_COMPILE_ERROR = 64 E_COMPILE_WARNING = 128 E_USER_ERROR = 256 E_USER_WARNING = 512 E_USER_NOTICE = 1024 E_STRICT = 2048 E_RECOVERABLE_ERROR = 4096 E_DEPRECATED = 8192 E_USER_DEPRECATED = 16384 E_ALL = 32767
В разных версиях PHP значения одинаковы, но список может дополняться новыми константами.
Определение имени константы по числовому коду
Для вывода читаемого имени вместо числа применяется поиск по массиву констант.
function getErrorName(int $code): string {
$constants = get_defined_constants(true);
$candidates = [];
foreach ($constants as $group) {
foreach ($group as $name => $value) {
if (strpos($name, 'E_') === 0) {
$candidates[$name] = $value;
}
}
}
$name = array_search($code, $candidates, true);
return $name !== false ? $name : 'UNKNOWN';
}
echo getErrorName(E_USER_ERROR) . PHP_EOL;
echo getErrorName(8) . PHP_EOL;
echo getErrorName(999999) . PHP_EOL;E_USER_ERROR E_NOTICE UNKNOWN
Функция перебирает все группы констант, включая Zend и other. Такой подход полезен в обработчиках ошибок, где код выводится в лог вместе с понятным именем.
Превращение предупреждения в ErrorException
Класс ErrorException содержит поле code, в которое помещается код уровня ошибки. Обработчик, приведённый ниже, перехватывает warnings и notices.
function errorToException($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
return false;
}
throw new ErrorException($message, $severity, $severity, $file, $line);
}
set_error_handler('errorToException');
try {
include 'missing-file.php';
} catch (ErrorException $e) {
echo 'Ошибка с кодом ' . $e->getCode() . ' в файле ' . $e->getFile() . ':' . $e->getLine();
}include несуществующего файла вызывает E_WARNING, поэтому catch получает ErrorException. Код ошибки будет 2. В файле, где нет прав на чтение, может возникнуть E_WARNING с тем же кодом 2.
Результат работы скрипта будет зависеть от имени файла и прав доступа:
Ошибка с кодом 2 в файле /var/www/missing-file.php:3
Важно: не путать код уровня ошибки (2) с кодом HTTP-ответа. Это разные вещи.
Логирование всех пользовательских ошибок с датой и временем
function logError($severity, $message, $file, $line) {
$codeName = getErrorName($severity);
$lineText = sprintf('[%s] %s (code %d): %s in %s on line %d', date('c'), $codeName, $severity, $message, $file, $line) . PHP_EOL;
file_put_contents(__DIR__ . '/errors.log', $lineText, FILE_APPEND | LOCK_EX);
}
set_error_handler('logError');
trigger_error('Пользовательская заметка', E_USER_NOTICE);
trigger_error('Предупреждение в функции', E_USER_WARNING);Содержимое errors.log после двух вызовов:
[2025-04-01T12:30:45+00:00] E_USER_NOTICE (code 1024): Пользовательская заметка in /var/www/log.php on line 10 [2025-04-01T12:30:45+00:00] E_USER_WARNING (code 512): Предупреждение в функции in /var/www/log.php on line 11
Обработчик не прекращает выполнение скрипта и позволяет сохранить оригинальное сообщение. Путь к файлу журнала лучше заменить на абсолютный путь, чтобы запись работала независимо от текущей директории.
Битовые операции с масками для отбора кодов
Так как коды ошибок являются степенью двойки, маска составляется через побитовые И, ИЛИ и NOT.
$allExceptNotice = E_ALL & ~E_NOTICE;
$warningAndUser = E_WARNING | E_USER_WARNING;
$withoutDeprecated = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED;
echo $allExceptNotice . PHP_EOL;
echo $warningAndUser . PHP_EOL;
echo $withoutDeprecated . PHP_EOL;32759 514 8191
Значения могут меняться, если в версии PHP добавлены новые биты. Поэтому в прикладном коде лучше использовать константы, а не числа.
Запуск PHP из командной строки с включёнными ошибками
php -d error_reporting=E_ALL -d display_errors=1 -r "trigger_error('Тест', E_USER_WARNING);"PHP Warning: Тест in Command line code on line 1
Использование кода исключения в catch
try {
throw new InvalidArgumentException('Некорректный параметр', 422);
} catch (Throwable $e) {
echo 'Тип: ' . get_class($e) . PHP_EOL;
echo 'Код: ' . $e->getCode() . PHP_EOL;
echo 'Сообщение: ' . $e->getMessage();
}Тип: InvalidArgumentException Код: 422 Сообщение: Некорректный параметр
Код исключения задаётся вторым аргументом конструктора. Его можно использовать для связи с внешними системами, не путая с уровнем ошибки PHP.
Обработка фатальной ошибки с уникальным идентификатором
Для критических кодов E_ERROR, E_PARSE и похожих применяется shutdown-перехват. Добавление уникального идентификатора помогает найти событие в общем журнале.
register_shutdown_function(function () {
$last = error_get_last();
if ($last && in_array($last['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR], true)) {
$errorId = uniqid('fatal_', true);
$text = sprintf('[%s] %s: %s in %s:%d', $errorId, $last['type'], $last['message'], $last['file'], $last['line']);
file_put_contents(__DIR__ . '/fatal.log', $text . PHP_EOL, FILE_APPEND | LOCK_EX);
echo 'Фатальная ошибка. Событие: ' . $errorId;
}
});
function triggerFatal() {
require '/missing/file.php';
}
triggerFatal();Результат выполнения будет содержать идентификатор события:
Фатальная ошибка. Событие: fatal_65f0ab2c0b6c6
В файле fatal.log появится строка с кодом и сообщением. Идентификатор можно передать пользователю, чтобы потом найти причину без раскрытия внутренних данных.