[size=14pt]Обработка ошибок в PHP[/size]
PHP5 предлагает нам мощный и гибкий механизм исключительных ситуаций. Однако большая часть библиотек языка выполнена в процедурном стиле и обработка ошибок в них сделана соответственно.
[size=14pt]Основное[/size]
Ошибки в PHP делятся на несколько типов:
- [tt]E_PARSE[/tt] — синтаксическая ошибка (незакрытая скобка, лишняя запятая, забытые точка с запятой и т.п.). Такие ошибки обычно (но всегда) обнаруживаются сразу, т.к. с ними PHP просто не сможет распарсить программу.
- [tt]E_ERROR[/tt] — «фатальная» ошибка. Дальнейшее выполнение программы невозможно. Примеры: вызов несуществующей функции, неудачная попытка подключения файла через require, повторное объявление класса или функции, неперехваченное исключение.
- [tt]E_WARNING[/tt] — предупреждение. Примеры – обращение к несуществующему файлу, передача в цикл [tt]foreach[/tt] переменной, не являющейся массивом.
- [tt]E_NOTICE[/tt] — уведомление. Чаще всего вызвано использованием необъявленных переменных, обращению к несуществующим элементам массивов.
- [tt]E_CORE_ERROR[/tt] — фатальная ошибка при запуске PHP. Аналогичен [tt]E_ERROR[/tt], но инициируется ядром.
- [tt]E_CORE_WARNING[/tt] — предупреждение при запуске PHP.
- [tt]E_COMPILE_ERROR[/tt] — ошибка при компиляции.
- [tt]E_COMPILE_WARNING[/tt] — предупреждение при компиляции.
- [tt]E_STRICT[/tt] (появился в PHP 5) — «советы» помогающие сделать ваш код более совместимым с будущими версиями PHP.
- [tt]E_RECOVERABLE_ERROR[/tt] (появился в PHP 5.2) — «поправимая, но потенциально опасная ошибка». Возникает, например, при попытке преобразовать в строку объект, не имеющий метода [tt]__toString()[/tt], при передаче в функцию аргумента неверного типа (если применяется контроль типов).
- [tt]E_DEPRECATED[/tt] (появился в PHP 5.3) — уведомления об использовании устаревших конструкций, несовместимых с будущими версиями PHP.
Описанные выше типы сообщений инициируются самим PHP. Для установки своих сообщений используется функция [tt]trigger_error()[/tt].
bool trigger_error ( string $error_msg [, int $error_type= E_USER_NOTICE ] )
$error_msg — текст сообщения
$error_type — тип ошибки
Типом такого сообщения может быть
- [tt]E_USER_ERROR[/tt],
- [tt]E_USER_WARNING[/tt],
- [tt]E_USER_NOTICE[/tt],
- [tt]E_USER_DEPRECATED[/tt] (начиная с PHP 5.3).
[size=14pt]Настройки вывода ошибок.[/size]
Вывод ошибок определяется директивой [tt]display_errors[/tt]. Её значение можно изменять во время выполнения скрипта с помощью функции [tt]ini_set()[/tt]. Однако, на неустранимые ошибки (такие как [tt]E_PARSE[/tt]) эта директива не влияет.
С помощью функции [tt]error_reporting()[/tt] можно установить, какие ошибки будут обрабатываться PHP.
int error_reporting ([ int $level ] )
Параметр $level определяет тип обрабатываемых ошибок. С помощью побитовых операторов можно комбинировать несколько типов:
error_reporting(E_ERROR | E_WARNING | E_PARSE);
— будут показаны [tt]E_ERROR[/tt], [tt]E_WARNING[/tt] и [tt]E_PARSE[/tt] ошибки.
error_reporting(E_ALL ^ E_NOTICE);
— будут показаны все ошибки, за исключением [tt]E_NOTICE[/tt].
Константа [tt]Е_ALL[/tt] определяет сразу несколько типов ошибок. Её значение может быть различным в зависимости от версии PHP. Например, до PHP 6 в [tt]E_ALL[/tt] не входит [tt]E_STRICT[/tt]. Что бы быть уверенным, что будут обработаны все возможные ошибки, используйте:
error_reporting(-1);
[size=14pt]Установка собственных обработчиков ошибок.[/size]
Стандартное поведение при возникновении ошибки можно изменить.
mixed set_error_handler ( callback $error_handler [, int $error_types = E_ALL | E_STRICT ] )
$error_handler определяет функцию, которая будет вызываться при возникновении ошибки.
$error_types устанавливает типы обрабатываемых ошибок.
Обработчик может принимать от двух до пяти аргументов:
1. тип ошибки,
2. текст сообщения,
3. файл в котором произошла ошибка,
4. строка в этом файле
5. контекст — содержит символьную таблицу участка программы, где произошла ошибка.
Если функция возвращает [tt]false[/tt], то запускается встроенный обработчик.
Напишем свой простой обработчик:
// Обрабатываются все возможные ошибки
error_reporting(-1);
// Функция-обработчик
function myErrorHandler($type, $message, $file, $line)
{
static $titles = array(
E_WARNING => 'Предупреждение',
E_NOTICE => 'Уведомление',
E_USER_ERROR => 'Ошибка, определенная пользователем',
E_USER_WARNING => 'Предупреждение, определенное пользователем',
E_USER_NOTICE => 'Уведомление, определенное пользователем',
E_STRICT => 'Проблема совместимости в коде',
E_RECOVERABLE_ERROR => 'Поправимая ошибка'
);
print '<h3>' . $titles[$type] . '</h3>'
. '<p>' . $message . '<br />'
. 'Источник: ' . basename($file) . ', line ' . $line . '</p>';
return true;
}
// Назначаем обработчик
set_error_handler('myErrorHandler');
Теперь вызовем какую-нибудь ошибку:
echo $X3;
Получим сообщение:
Уведомление
Undefined variable: X3
Источник: err.php, line 108
При этом значение [tt]error_reporting[/tt] не учитывается — обрабатываются все возможные ошибки. Но в самом обработчике можно предусмотреть различное поведение в зависимости от этого значения. Так же прерывание работы скрипта следует предусмотреть самостоятельно (если нужно, конечно).
Часто, даже зная файл и строку где произошла ошибка, не всегда ясно что могло её вызвать. В таких случаях полезно увидеть стек вызовов. Для его получения воспользуемся функцией [tt]debug_backtrace[/tt], которая возвращает массив с информацией о вызванных функциях. Эта информация представляет собой массив, который может следующие ключи:
- function — название функции (или метода),
- line — номер строки,
- file — путь к файлу,
- class — имя класса,
- object — объект (PHP 5.1.1 и выше),
- type — если был вызван метод объекта содержит «[tt]->[/tt]», если статический метод класса — «[tt]::[/tt]», если обычная функция — ничего.
- args — массив аргументов функции.
Добавим в обработчик вывод стека:
$backtrace = debug_backtrace();
array_shift($backtrace); // удалим вызов самого обработчика
print 'Стек вызовов: <ol>';
foreach ($backtrace as $call) {
print '<li>';
if (array_key_exists('file', $call)) {
print basename($call['file']) . ', line '
. $call['line'] . ': ';
}
if (array_key_exists('object', $call) &&
method_exists($call['object'], '__toString')) {
print $call['object'];
}
if (array_key_exists('type', $call)) {
if ($call['type'] == '->') {
print $call['class'] . '->';
} elseif ($call['type'] == '::') {
print $call['class'] . '::';
}
}
print $call['function'] . '(';
$strArgs = '';
foreach ($call['args'] as $arg) {
if (is_null($arg)) {
$strArgs .= 'null';
} elseif (is_bool($arg)) {
$strArgs .= ($arg) ? 'true' : 'false';
} elseif (is_string($arg)) {
$strArgs .= '"' . $arg . '"';
} elseif (is_integer($arg) || is_float($arg)) {
$strArgs .= $arg;
} elseif (is_array($arg)) {
$strArgs .= 'array (' . sizeof($arg) . ')';
} elseif (is_object($arg)) {
$strArgs .= 'object (' . get_class($arg) . ')';
} elseif (is_resource($arg)) {
$strArgs .= 'resource (' . get_resource_type($arg) . ')';
}
$strArgs .= ', ';
}
$strArgs = substr($strArgs, 0, -2);
print $strArgs . ')</li>';
}
print '</ol>';
Вызовем ошибку:
function callUserNotice()
{
trigger_error('Это уведомление', E_USER_NOTICE);
}
callUserNotice("Hello", 123, array());
Получим сообщение:
Уведомление, определенное пользователем
Это уведомление
Источник: err.php, line 132
Стек вызовов:
err.php, line 132: trigger_error("Это уведомление", 1024)
err.php, line 150: callUserNotice("Hello", 123, array (0))
К сожалению, ощибки [tt]E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING[/tt] и большая часть [tt]E_STRICT[/tt] не смогут быть обработаны этой функцией.
Однако, можно воспользоваться функциями [tt]register_shutdown_function()[/tt] и [tt]error_get_last()[/tt]
void register_shutdown_function ( callback $function )
$function определяет имя функции, которая будет вызвана при завершении работы скрипта.
array error_get_last ()
Она возвращает информацию о последней ошибке.
Напишем функцию:
function myFatalCatcher()
{
$error = error_get_last();
if ($error['type'] == E_ERROR ||
$error['type'] == E_CORE_ERROR ||
$error['type'] == E_COMPILE_ERROR ||
$error['type'] == E_USER_ERROR) {
myErrorHandler(
$error['type'],
$error['message'],
$error['file'],
$error['line']);
}
}
register_shutdown_function('myFatalCatcher');
Т.о. если программа была завершена из-за фатальной ошибки, будет вызван обработчик [tt]myErrorHandler()[/tt]. Что бы одновременно не производился стандартный вывод ошибок, следует отключить [tt]display_errors[/tt].
К сожаленью, перехватить ошибки [tt]E_PARSE[/tt] или получить стек вызовов при этом не удастся.
Вызовем фатальную ошибку:
function callError()
{
doSomething();
}
callError();
В результате получим:
Фатальная ошибка
Call to undefined function doSomething()
Источник: err.php, line 126
Стек вызовов:
myFatalCatcher()
Полный код с примерами можно найти в приложении.
[size=14pt]Альтернативы[/size]
Более универсальный способ заключается в использовании функции [tt]ob_start[/tt]. В её параметре можно задать функцию, которая будет вызываться в любом случае, как бы не завершилась программа. В эту функцию передается все содержимое выходного потока, находившееся в буфере, в том числе и сообщение об ошибке. Данный способ подробно описан Д. Котеровым в 45-ой набле.
[size=14pt]Что ещё