Собственный обработчик ошибок php

(PHP 4 >= 4.0.1, PHP 5, PHP 7, PHP 8)

set_error_handler
Задаёт пользовательский обработчик ошибок

Описание

set_error_handler(?callable $callback, int $error_levels = E_ALL): ?callable

Функция может быть использована для определения пользовательских обработчиков
ошибок во время выполнения, например, в приложениях, которые должны выполнять
очистку файлов/данных в случае возникновения критической ошибки
или при инициировании ошибки в ответ на определённые условия
(используя функцию trigger_error()).

Важно помнить, что стандартный обработчик ошибок PHP не будет обрабатывать
никакие типы ошибок, определённые в error_levels,
пока callback-функция не вернёт false. Пользовательский обработчик будет
вызываться в случае возникновения любой ошибке, независимо от настроек, заданных функцией
error_reporting.

Также обратите внимание, что обработчик обязан при необходимости остановить
выполнение скрипта, вызвав функцию exit().
Если происходит возврат из обработчика ошибок,
управление передаётся следующему выражению, стоящему за тем, что вызвало ошибку.

Ошибки следующих типов не могут быть обработаны пользователем:
E_ERROR, E_PARSE,
E_CORE_ERROR, E_CORE_WARNING,
E_COMPILE_ERROR,
E_COMPILE_WARNING независимо от того, где они были сгенерированы и большинство
ошибок E_STRICT, произошедших в файле, где вызвана
функция set_error_handler().

Если ошибки возникают до запуска скрипта (например, пока файл загружается),
пользовательский обработчик не будет вызываться, если на этот момент он
ещё не зарегистрирован.

Список параметров

callback

Если передано значение null, обработчик сбрасывается в состояние по умолчанию.
В противном случае обработчик представляет собой callback-функцию со следующей сигнатурой:

handler(
    int $errno,
    string $errstr,
    string $errfile = ?,
    int $errline = ?,
    array $errcontext = ?
): bool

errno


В первый аргумент errno будет передан уровень
ошибки в виде целого числа.

errstr


Во второй аргумент errstr будет передано сообщение
об ошибке в виде строки.

errfile


Если функция обратного вызова принимает третий параметр
errfile, то в него будет передано
имя файла, в котором произошла ошибка, в виде строки.

errline


Если функция обратного вызова принимает четвёртый параметр
errline, то в него будет передан
номер строки, в которой произошла ошибка, в виде целого
числа.

errcontext


Если функция обратного вызова принимает пятый параметр
errcontext, то в него будет передан
массив указателей на активную таблицу символов в точке, где
произошла ошибка. Другими словами, errcontext
будет содержать массив всех переменных, существующих в области
видимости, где произошла ошибка. Пользовательские обработчики ошибок не
должны изменять этот контекст.

Внимание

Этот параметр объявлен УСТАРЕВШИМ начиная с PHP 7.2.0 и
был УДАЛЁН в PHP 8.0.0. Если в вашей функции этот
параметр используется и для него не задано значение по умолчанию, то при
вызове функции обработчика будет выдана ошибка «too few arguments».

Если функция возвращает false, управление передаётся встроенному
обработчику ошибок.

error_levels

Может использоваться для задания маски, в соответствии с которой будет
вызываться callback, по аналогии с
ini-настройкой error_reporting,
которая отвечает за то, какие ошибки будут показаны в отчёте. Без этой
маски callback будет вызываться для
обработки всех происходящих ошибок, вне зависимости от настроек в
error_reporting.

Возвращаемые значения

Возвращает ранее определённый обработчик ошибок (если есть) Если на данный момент используется встроенный обработчик, функция
вернёт null. Если предыдущий определённый обработчик является методом класса, функция вернёт массив,
содержащий имя класса и имя метода.

Список изменений

Версия Описание
8.0.0 Параметр errcontext был удалён и больше не передаётся в
пользовательскую функцию обработки ошибок.
7.2.0 Параметр errcontext объявлен устаревшим. Теперь при его
использовании будет вызываться ошибка уровня E_DEPRECATED.

Примеры

Пример #1
Обработка ошибок с помощью функций set_error_handler()
и trigger_error()

Пример ниже демонстрирует обработку внутренних исключений путём
вызова ошибок разных типов и их обработки пользовательской функцией:


<?php
// функция обработки ошибок
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
if (!(
error_reporting() & $errno)) {
// Этот код ошибки не включён в error_reporting,
// так что пусть обрабатываются стандартным обработчиком ошибок PHP
return false;
}
// может потребоваться экранирование $errstr:
$errstr = htmlspecialchars($errstr);

switch (

$errno) {
case
E_USER_ERROR:
echo
"<b>Пользовательская ОШИБКА</b> [$errno] $errstr<br />n";
echo
" Фатальная ошибка в строке $errline файла $errfile";
echo
", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />n";
echo
"Завершение работы...<br />n";
exit(
1);

case

E_USER_WARNING:
echo
"<b>Пользовательское ПРЕДУПРЕЖДЕНИЕ</b> [$errno] $errstr<br />n";
break;

case

E_USER_NOTICE:
echo
"<b>Пользовательское УВЕДОМЛЕНИЕ</b> [$errno] $errstr<br />n";
break;

default:
echo

"Неизвестная ошибка: [$errno] $errstr<br />n";
break;
}
/* Не запускаем внутренний обработчик ошибок PHP */
return true;
}
// функция для тестирования обработчика ошибок
function scale_by_log($vect, $scale)
{
if (!
is_numeric($scale) || $scale <= 0) {
trigger_error("log(x) для x <= 0 не определён, вы используете: scale = $scale", E_USER_ERROR);
}

if (!

is_array($vect)) {
trigger_error("Некорректный входной вектор, пропущен массив значений", E_USER_WARNING);
return
null;
}
$temp = array();
foreach(
$vect as $pos => $value) {
if (!
is_numeric($value)) {
trigger_error("Значение на позиции $pos не является числом, будет использован 0 (ноль)", E_USER_NOTICE);
$value = 0;
}
$temp[$pos] = log($scale) * $value;
}

return

$temp;
}
// переключаемся на пользовательский обработчик
$old_error_handler = set_error_handler("myErrorHandler");// вызовем несколько ошибок, во-первых, определим массив с нечисловым элементом
echo "vector an";
$a = array(2, 3, "foo", 5.5, 43.3, 21.11);
print_r($a);// теперь создадим ещё один массив
echo "----nvector b - a notice (b = log(PI) * a)n";
/* Значение на позиции $pos не является числом, будет использован 0 (ноль)*/
$b = scale_by_log($a, M_PI);
print_r($b);// проблема, мы передаём строку вместо массива
echo "----nvector c - a warningn";
/* Некорректный входной вектор, пропущен массив значений */
$c = scale_by_log("not array", 2.3);
var_dump($c); // NULL

// критическая ошибка, логарифм от неположительного числа не определён

echo "----nvector d - fatal errorn";
/* log(x) для x <= 0 не определён, вы используете: scale = $scale */
$d = scale_by_log($a, -2.5);
var_dump($d); // До сюда не дойдём никогда
?>

Результатом выполнения данного примера
будет что-то подобное:

vector a
Array
(
    [0] => 2
    [1] => 3
    [2] => foo
    [3] => 5.5
    [4] => 43.3
    [5] => 21.11
)
----
vector b - a notice (b = log(PI) * a)
<b>Пользовательское УВЕДОМЛЕНИЕ</b> [1024]  Значение на позиции 2 не является числом, будет использован 0 (ноль)<br />
Array
(
    [0] => 2.2894597716988
    [1] => 3.4341896575482
    [2] => 0
    [3] => 6.2960143721717
    [4] => 49.566804057279
    [5] => 24.165247890281
)
----
vector c - a warning
<b>Пользовательское ПРЕДУПРЕЖДЕНИЕ</b> [512] Некорректный входной вектор, пропущен массив значений<br />
NULL
----
vector d - fatal error
<b>Пользовательская ОШИБКА</b> [256] log(x) for x <= 0 is undefined, you used: scale = -2.5<br />
  Фатальная ошибка в строке 35 файла trigger_error.php, PHP 5.2.1 (FreeBSD)<br />
Завершение работы...<br />

Смотрите также

  • ErrorException
  • error_reporting() — Задаёт, какие ошибки PHP попадут в отчёт
  • restore_error_handler() — Восстанавливает предыдущий обработчик ошибок
  • trigger_error() — Вызывает пользовательскую ошибку/предупреждение/уведомление
  • Константы уровней ошибок

Philip

10 years ago


By this function alone you can not catch fatal errors, there is a simple work around. Below is part of my error.php file which handles errors and exceptions in the application. Before someone complains I'll add that I do not care that I am using globals, this file is part of my mini framework and without the 'config' variable the application would crash anyways.

<?php/**
* Error handler, passes flow over the exception logger with new ErrorException.
*/
function log_error( $num, $str, $file, $line, $context = null )
{
   
log_exception( new ErrorException( $str, 0, $num, $file, $line ) );
}
/**
* Uncaught exception handler.
*/
function log_exception( Exception $e )
{
    global
$config;

        if (

$config["debug"] == true )
    {
        print
"<div style='text-align: center;'>";
        print
"<h2 style='color: rgb(190, 50, 50);'>Exception Occured:</h2>";
        print
"<table style='width: 800px; display: inline-block;'>";
        print
"<tr style='background-color:rgb(230,230,230);'><th style='width: 80px;'>Type</th><td>" . get_class( $e ) . "</td></tr>";
        print
"<tr style='background-color:rgb(240,240,240);'><th>Message</th><td>{$e->getMessage()}</td></tr>";
        print
"<tr style='background-color:rgb(230,230,230);'><th>File</th><td>{$e->getFile()}</td></tr>";
        print
"<tr style='background-color:rgb(240,240,240);'><th>Line</th><td>{$e->getLine()}</td></tr>";
        print
"</table></div>";
    }
    else
    {
       
$message = "Type: " . get_class( $e ) . "; Message: {$e->getMessage()}; File: {$e->getFile()}; Line: {$e->getLine()};";
       
file_put_contents( $config["app_dir"] . "/tmp/logs/exceptions.log", $message . PHP_EOL, FILE_APPEND );
       
header( "Location: {$config["error_page"]}" );
    }

        exit();
}

/**
* Checks for a fatal error, work around for set_error_handler not working on fatal errors.
*/
function check_for_fatal()
{
   
$error = error_get_last();
    if (
$error["type"] == E_ERROR )
       
log_error( $error["type"], $error["message"], $error["file"], $error["line"] );
}
register_shutdown_function( "check_for_fatal" );
set_error_handler( "log_error" );
set_exception_handler( "log_exception" );
ini_set( "display_errors", "off" );
error_reporting( E_ALL );


elad dot yosifon at gmail dot com

9 years ago


<?php
/**
* throw exceptions based on E_* error types
*/
set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line, array $err_context)
{
   
// error was suppressed with the @-operator
   
if (0 === error_reporting()) { return false;}
    switch(
$err_severity)
    {
        case
E_ERROR:               throw new ErrorException            ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_WARNING:             throw new WarningException          ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_PARSE:               throw new ParseException            ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_NOTICE:              throw new NoticeException           ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_CORE_ERROR:          throw new CoreErrorException        ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_CORE_WARNING:        throw new CoreWarningException      ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_COMPILE_ERROR:       throw new CompileErrorException     ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_COMPILE_WARNING:     throw new CoreWarningException      ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_USER_ERROR:          throw new UserErrorException        ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_USER_WARNING:        throw new UserWarningException      ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_USER_NOTICE:         throw new UserNoticeException       ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_STRICT:              throw new StrictException           ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_RECOVERABLE_ERROR:   throw new RecoverableErrorException ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_DEPRECATED:          throw new DeprecatedException       ($err_msg, 0, $err_severity, $err_file, $err_line);
        case
E_USER_DEPRECATED:     throw new UserDeprecatedException   ($err_msg, 0, $err_severity, $err_file, $err_line);
    }
});

class

WarningException              extends ErrorException {}
class
ParseException                extends ErrorException {}
class
NoticeException               extends ErrorException {}
class
CoreErrorException            extends ErrorException {}
class
CoreWarningException          extends ErrorException {}
class
CompileErrorException         extends ErrorException {}
class
CompileWarningException       extends ErrorException {}
class
UserErrorException            extends ErrorException {}
class
UserWarningException          extends ErrorException {}
class
UserNoticeException           extends ErrorException {}
class
StrictException               extends ErrorException {}
class
RecoverableErrorException     extends ErrorException {}
class
DeprecatedException           extends ErrorException {}
class
UserDeprecatedException       extends ErrorException {}

aditycse at gmail dot com

7 years ago


<?php
/**
* Used for logging all php notices,warings and etc in a file when error reporting
* is set and display_errors is off
* @uses used in prod env for logging all type of error of php code in a file for further debugging
* and code performance
* @author Aditya Mehrotra<aditycse@gmail.com>
*/
error_reporting(E_ALL);
ini_set("display_errors", "off");
define('ERROR_LOG_FILE', '/var/www/error.log');/**
* Custom error handler
* @param integer $code
* @param string $description
* @param string $file
* @param interger $line
* @param mixed $context
* @return boolean
*/
function handleError($code, $description, $file = null, $line = null, $context = null) {
   
$displayErrors = ini_get("display_errors");
   
$displayErrors = strtolower($displayErrors);
    if (
error_reporting() === 0 || $displayErrors === "on") {
        return
false;
    }
    list(
$error, $log) = mapErrorCode($code);
   
$data = array(
       
'level' => $log,
       
'code' => $code,
       
'error' => $error,
       
'description' => $description,
       
'file' => $file,
       
'line' => $line,
       
'context' => $context,
       
'path' => $file,
       
'message' => $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']'
   
);
    return
fileLog($data);
}
/**
* This method is used to write data in file
* @param mixed $logData
* @param string $fileName
* @return boolean
*/
function fileLog($logData, $fileName = ERROR_LOG_FILE) {
   
$fh = fopen($fileName, 'a+');
    if (
is_array($logData)) {
       
$logData = print_r($logData, 1);
    }
   
$status = fwrite($fh, $logData);
   
fclose($fh);
    return (
$status) ? true : false;
}
/**
* Map an error code into an Error word, and log location.
*
* @param int $code Error code to map
* @return array Array of error word, and log location.
*/
function mapErrorCode($code) {
   
$error = $log = null;
    switch (
$code) {
        case
E_PARSE:
        case
E_ERROR:
        case
E_CORE_ERROR:
        case
E_COMPILE_ERROR:
        case
E_USER_ERROR:
           
$error = 'Fatal Error';
           
$log = LOG_ERR;
            break;
        case
E_WARNING:
        case
E_USER_WARNING:
        case
E_COMPILE_WARNING:
        case
E_RECOVERABLE_ERROR:
           
$error = 'Warning';
           
$log = LOG_WARNING;
            break;
        case
E_NOTICE:
        case
E_USER_NOTICE:
           
$error = 'Notice';
           
$log = LOG_NOTICE;
            break;
        case
E_STRICT:
           
$error = 'Strict';
           
$log = LOG_NOTICE;
            break;
        case
E_DEPRECATED:
        case
E_USER_DEPRECATED:
           
$error = 'Deprecated';
           
$log = LOG_NOTICE;
            break;
        default :
            break;
    }
    return array(
$error, $log);
}
//calling custom error handler
set_error_handler("handleError");print_r($arra); //undefined variable
print_r($dssdfdfgg); //undefined variable
include_once 'file.php'; //No such file or directory
?>

steve962 at gmail dot com

5 years ago


Be careful when using the return value to this function.   Because it returns the old handler, you may be tempted to do something like:

<?php
function do_something()
    {
   
$old = set_error_handler(“my_error_handler”);
   
// Do something you want handled by my_error_handler
   
set_error_handler($old);
    }
?>

This will work, but it will bite you because each time you do this, it will cause a memory leak as the old error handler is put on a stack for the restore_error_handler() function to use.

So always restore the old error handler using that function instead:

<?php
function do_something()
    {
   
set_error_handler(“my_error_handler”);
   
// Do something you want handled by my_error_handler
   
restore_error_handler();
    }
?>


nizamgok at gmail dot com

13 years ago


I have realized that a few people here mentioned that you cannot capture parse errors (type 4, E_PARSE). This is not true. Here is how I do. I hope this helps someone.

1) Create a "auto_prepend.php" file in the web root and add this:

<?php

register_shutdown_function
('error_alert');

function

error_alert()

{

        if(
is_null($e = error_get_last()) === false)

        {

               
mail('your.email@example.com', 'Error from auto_prepend', print_r($e, true));

        }

}

?>



2) Then add this "php_value auto_prepend_file /www/auto_prepend.php" to your .htaccess file in the web root.

* make sure you change the email address and the path to the file.


dannykopping at gmail dot com

9 years ago


Keep in mind that, when attempting to set a statically-defined error handler on a namespaced class in PHP >= 5.3, you need to use the class namespace:

<?php
set_error_handler
('\My\Namespace\Bob::errorHandler');
?>


Jacob Slomp

10 years ago


This might be handy if you don't want your clients to see the errors, and you do want to be one step ahead of them.

It emails you the errors even if it's a parse error.

set_error_handler() doesn't work for what I wanted.

<?php
ini_set
('log_errors',TRUE);
ini_set('error_log','tiny_uploads/errors.txt');

if(

$_SERVER['REMOTE_ADDR'] != "YOUR IP ADDRESS"){
   
ini_set('display_errors',false);
}

   function

byebye(){$dir = dirname(__FILE__);
        if(
file_exists($dir."/tiny_uploads/errors.txt")){$errors = file_get_contents($dir."/tiny_uploads/errors.txt");

                        if(

trim($errors)){$head = "From: php_errors@".str_replace('www.','',$_SERVER['HTTP_HOST'])."rn";$errors .= "---------------------------------------------nn";$errors .= "nnServer Info:nn".print_r($_SERVER, 1)."nn";
               
$errors .= "---------------------------------------------nn";$errors .= "nnCOOKIE:nn".print_r($_COOKIE, 1)."nn";
               
$errors .= "---------------------------------------------nn";$errors .= "nnPOST:nn".print_r($_POST, 1)."nn";
               
$errors .= "---------------------------------------------nn";$errors .= "nnGET:nn".print_r($_GET, 1)."nn";mail("YOUR@EMAIL.COM","PHP Error ".$_SERVER['HTTP_HOST']."", $errors , $head);$fp = fopen($dir."/tiny_uploads/errors.txt","w+");
               
fputs($fp, "");
               
fclose($fp);   
            }   
        }
}
register_shutdown_function("byebye");
?>


kalle at meizo dot com

13 years ago


This may be of help to someone, who is/was looking for a way to get a backtrace of fatal errors such as maximum memory allocation issues, which can not be handled with user-defined functions, to pin-point the problem:

On a server hosting many sites that share common PHP includes, I set in one spot:

<?php

@ini_set ("error_log", "/my/path/php.err-" . $_SERVER ["HTTP_HOST"] . "-" . $_SERVER ["REMOTE_ADDR"] . "-" . $_SERVER ["REQUEST_METHOD"] . "-" . str_replace ("/", "|", $_SERVER ["REQUEST_URI"]));

?>



I actually used some additional information too from my software that I omitted, but that way, you'll find errors sorted more neatly in for example:-

/my/path/php.err-website.com-127.0.0.1-GET-path|index.html?xyz

And that at least helped me tremendously to then further pin-point where the problem is, as opposed to before just seeing the out of memory and not knowing which site/page it was on (as the PHP error only contains the very latest PHP code where it ran out of memory, which usually is just a shared included file, not the actual page).


webmaster at paramiliar dot com

15 years ago


We needed to use an error handler to handle SQL errors while passing the query along so the query is also logged and this is what we came up with, its kind of an ugly bridge but it works 100%

<?phpfunction myErrorHandler($errno, $errstr, $errfile, $errline){
    switch (
$errno) {
    case
E_USER_ERROR:
        if (
$errstr == "(SQL)"){
           
// handling an sql error
           
echo "<b>SQL Error</b> [$errno] " . SQLMESSAGE . "<br />n";
            echo
"Query : " . SQLQUERY . "<br />n";
            echo
"On line " . SQLERRORLINE . " in file " . SQLERRORFILE . " ";
            echo
", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />n";
            echo
"Aborting...<br />n";
        } else {
            echo
"<b>My ERROR</b> [$errno] $errstr<br />n";
            echo
"  Fatal error on line $errline in file $errfile";
            echo
", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />n";
            echo
"Aborting...<br />n";
        }
        exit(
1);
        break;

    case

E_USER_WARNING:
    case
E_USER_NOTICE:
    }
   
/* Don't execute PHP internal error handler */
   
return true;
}
// function to test the error handlingfunction sqlerrorhandler($ERROR, $QUERY, $PHPFILE, $LINE){
   
define("SQLQUERY", $QUERY);
   
define("SQLMESSAGE", $ERROR);
   
define("SQLERRORLINE", $LINE);
   
define("SQLERRORFILE", $PHPFILE);
   
trigger_error("(SQL)", E_USER_ERROR);
}
set_error_handler("myErrorHandler");// trigger an sql error
$query = "SELECT * FROM tbl LIMIT 1";
$sql = @mysql_query($query)
    or
sqlerrorhandler("(".mysql_errno().") ".mysql_error(), $query, $_SERVER['PHP_SELF'], __LINE__);?>


Steffen Staehle

18 years ago


Two notes on using set_error_handler() on behaviour that I noticed when migrating an application from php 4.2.1 to php 4.3.9 (I do not yet have php 5.0 available, this might not apply there!).

1. setting the system error handler

If you want to set the standard php error handler again, after having set your own error handler, this works in php 4.2.1 by passing in an empty string:

<?phpfunction my_handler($log_level, $log_text, $error_file, $error_line)
   {
     
// if an error occurs here, the standard error
      // would be called (to avoid recursion)

      // do something useful
      // ...

}$last_handler = set_error_handler("my_handler");// after this, $last_handler == ""

   // restore standard error handler

$last_handler = set_error_handler("");// after this, $last_handler == "my_handler"?>

The very same code now raises an error in php 4.3.9:

   set_error_handler() expects argument 1, '', to be a valid callback

(Since the return value of the first call to set_error_handler() is still the empty string "", I don't see how this can be done any more. I don't really need this, because I use my own handlers as shown below, but it might be good to be aware of this.)

2. setting your own 'second level' handler

If you have set your own error handler, and want to replace it by another one (other than the standard php error handler) while it is being executed, note that the return value of set_error_handler when used INSIDE the error handler is "" instead of the name of the previous handler! This is not too surprising, because during execution of your self defined error handler, php replaces it with the standard php error handler to avoid infinite loops in case of problems inside the handler. This is only interesting if you want nested handlers as I do. Background of my design:

   1st level handler: log into DB
   2nd level handler: log into flat file (if log into DB fails)
   3rd level handler: print to stdout (if log into flat file fails) (this is the sytem handler, finally).

<?phpfunction my_fallback_handler($log_level, $log_text, $error_file, $error_line)
   {
     
// if an error occurs here, the standard error
      // would be called (to avoid recursion)

      // do something useful
      // ...

} // my_fallback_handlerfunction my_handler($log_level, $log_text, $error_file, $error_line)
   {
     
// if an error occurs here, the standard error
      // would be called (to avoid recursion)

      // but we want to have a fallback handler different
      // to the standard error handler

$last_handler = set_error_handler("my_fallback_handler");// I expected $last_handler == "my_handler"
      // (which it would outside my_handler())
      // but here it is the empty string ""

      // do something useful
      // ...

      // now set the 1st level handler again:
      // (do NOT use $last_handler as argument,
      // because it equals "")

$last_handler = set_error_handler("my_handler");

   }

// my_handler$last_handler = set_error_handler("my_handler");?>


silkensedai at online dot fr

16 years ago


i made an error handler that print also the backtrace and that can die on some errors. It can be useful if you want to die on every error you find.

<?phpfunction my_error_handler($errno, $errstr, $errfile, $errline){
   
$errno = $errno & error_reporting();
    if(
$errno == 0) return;
    if(!
defined('E_STRICT'))            define('E_STRICT', 2048);
    if(!
defined('E_RECOVERABLE_ERROR')) define('E_RECOVERABLE_ERROR', 4096);
    print
"<pre>n<b>";
    switch(
$errno){
        case
E_ERROR:               print "Error";                  break;
        case
E_WARNING:             print "Warning";                break;
        case
E_PARSE:               print "Parse Error";            break;
        case
E_NOTICE:              print "Notice";                 break;
        case
E_CORE_ERROR:          print "Core Error";             break;
        case
E_CORE_WARNING:        print "Core Warning";           break;
        case
E_COMPILE_ERROR:       print "Compile Error";          break;
        case
E_COMPILE_WARNING:     print "Compile Warning";        break;
        case
E_USER_ERROR:          print "User Error";             break;
        case
E_USER_WARNING:        print "User Warning";           break;
        case
E_USER_NOTICE:         print "User Notice";            break;
        case
E_STRICT:              print "Strict Notice";          break;
        case
E_RECOVERABLE_ERROR:   print "Recoverable Error";      break;
        default:                    print
"Unknown error ($errno)"; break;
    }
    print
":</b> <i>$errstr</i> in <b>$errfile</b> on line <b>$errline</b>n";
    if(
function_exists('debug_backtrace')){
       
//print "backtrace:n";
       
$backtrace = debug_backtrace();
       
array_shift($backtrace);
        foreach(
$backtrace as $i=>$l){
            print
"[$i] in function <b>{$l['class']}{$l['type']}{$l['function']}</b>";
            if(
$l['file']) print " in <b>{$l['file']}</b>";
            if(
$l['line']) print " on line <b>{$l['line']}</b>";
            print
"n";
        }
    }
    print
"n</pre>";
    if(isset(
$GLOBALS['error_fatal'])){
        if(
$GLOBALS['error_fatal'] & $errno) die('fatal');
    }
}

function

error_fatal($mask = NULL){
    if(!
is_null($mask)){
       
$GLOBALS['error_fatal'] = $mask;
    }elseif(!isset(
$GLOBALS['die_on'])){
       
$GLOBALS['error_fatal'] = 0;
    }
    return
$GLOBALS['error_fatal'];
}
?>

Usage :

<?php
error_reporting
(E_ALL);      // will report all errors
set_error_handler('my_error_handler');
error_fatal(E_ALL^E_NOTICE); // will die on any error except E_NOTICE
?>


wfinn at riverbed dot com

14 years ago


"The following error types cannot be handled with a user defined function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and most of E_STRICT raised in the file where set_error_handler() is called."

This is not exactly true.  set_error_handler() can't handle them, but ob_start() can handle at least E_ERROR.

<?phpfunction error_handler($output)
{
   
$error = error_get_last();
   
$output = "";
    foreach (
$error as $info => $string)
       
$output .= "{$info}: {$string}n";
    return
$output;
}
ob_start('error_handler');will_this_undefined_function_raise_an_error();?>


francois vespa

12 years ago


This is a note when using php from the terminal (the CLI interface). From the command line, even if you have some kind of error user handler function so STDERR will not display, fatal errors will still cause the PHP interpreter to display error text. There is nothing you can do about that php-wise. If using UNIX/Linux, you can add " 2>/dev/null" at the end of your command to force STDERR not to show

dorphalsig at NOSPAMgmail dot com

11 years ago


This actually works to catch Fatal errors...

<?php

function shutdown()

{

   
$a=error_get_last();

    if(
$a==null)  

        echo
"No errors";

    else

        
print_r($a);

   
}

register_shutdown_function('shutdown');

ini_set('max_execution_time',1 );

sleep(3);

?>



it will output

Array ( [type] => 1 [message] => Maximum execution time of 1 second exceeded [file] => /path/to/file_name.php [line] => 136 )


Marcelius

14 years ago


Another way to catch PHP's fatal errors:

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

    function

shutdown(){
       
$isError = false;
        if (
$error = error_get_last()){
            switch(
$error['type']){
                case
E_ERROR:
                case
E_CORE_ERROR:
                case
E_COMPILE_ERROR:
                case
E_USER_ERROR:
                   
$isError = true;
                    break;
            }
        }

        if (

$isError){
            echo
"Script execution halted ({$error['message']})";
        } else {
            echo
"Script completed";
        }
    }
register_shutdown_function('shutdown');
?>

Note that this will only catch runtime errors. So calling a method in a non existing class, or declaring a function twice does not trigger the shutdown handler.


roy

20 years ago


Useful thing to note - if your error handler throws an error in itself, PHP is smart enough to use the deault error handler to handle it. This way, you don't end up in infinite flaming loops of death. This seems to be true, at least, in PHP 4.2.

('Course, there are ways to create your handler to handle even this situation, but it's probably best left this way for general purposes.)


Anonymous

17 years ago


To honor the value of PHP's error_reporting() function, use:

<?
  if( ($level & error_reporting()) == 0 ) return;
?>


phpmanual at NO_SPHAMnetebb dot com

19 years ago


Given this code:

class CallbackClass {
    function CallbackFunction() {
        // refers to $this
    }

    function StaticFunction() {
        // doesn't refer to $this
    }
}

function NonClassFunction() {
}

there appear to be 3 ways to set a callback function in PHP (using set_error_handler() as an example):

1: set_error_handler('NonClassFunction');

2: set_error_handler(array('CallbackClass', 'StaticFunction'));

3: $o =& new CallbackClass();
   set_error_handler(array($o, 'CallbackFunction'));

The following may also prove useful:

class CallbackClass {
    function CallbackClass() {
        set_error_handler(array(&$this, 'CallbackFunction')); // the & is important
    }

        function CallbackFunction() {
        // refers to $this
    }
}

The documentation is not clear in outlining these three examples.


jtrick77 at gmail dot com

9 years ago


For anyone interested in the actual translated error codes and their meanings:

1    E_ERROR (integer)     Fatal run-time errors. These indicate errors that can not be recovered from, such as a memory allocation problem. Execution of the script is halted.    
2    E_WARNING (integer)     Run-time warnings (non-fatal errors). Execution of the script is not halted.    
4    E_PARSE (integer)     Compile-time parse errors. Parse errors should only be generated by the parser.    
8    E_NOTICE (integer)     Run-time notices. Indicate that the script encountered something that could indicate an error, but could also happen in the normal course of running a script.    
16    E_CORE_ERROR (integer)     Fatal errors that occur during PHP's initial startup. This is like an E_ERROR, except it is generated by the core of PHP.    
32    E_CORE_WARNING (integer)     Warnings (non-fatal errors) that occur during PHP's initial startup. This is like an E_WARNING, except it is generated by the core of PHP.    
64    E_COMPILE_ERROR (integer)     Fatal compile-time errors. This is like an E_ERROR, except it is generated by the Zend Scripting Engine.    
128    E_COMPILE_WARNING (integer)     Compile-time warnings (non-fatal errors). This is like an E_WARNING, except it is generated by the Zend Scripting Engine.    
256    E_USER_ERROR (integer)     User-generated error message. This is like an E_ERROR, except it is generated in PHP code by using the PHP function trigger_error().    
512    E_USER_WARNING (integer)     User-generated warning message. This is like an E_WARNING, except it is generated in PHP code by using the PHP function trigger_error().    
1024    E_USER_NOTICE (integer)     User-generated notice message. This is like an E_NOTICE, except it is generated in PHP code by using the PHP function trigger_error().    
2048    E_STRICT (integer)     Enable to have PHP suggest changes to your code which will ensure the best interoperability and forward compatibility of your code.    Since PHP 5 but not included in E_ALL until PHP 5.4.0
4096    E_RECOVERABLE_ERROR (integer)     Catchable fatal error. It indicates that a probably dangerous error occurred, but did not leave the Engine in an unstable state. If the error is not caught by a user defined handle (see also set_error_handler()), the application aborts as it was an E_ERROR.    Since PHP 5.2.0
8192    E_DEPRECATED (integer)     Run-time notices. Enable this to receive warnings about code that will not work in future versions.    Since PHP 5.3.0
16384    E_USER_DEPRECATED (integer)     User-generated warning message. This is like an E_DEPRECATED, except it is generated in PHP code by using the PHP function trigger_error().    Since PHP 5.3.0
32767    E_ALL (integer)     All errors and warnings, as supported, except of level E_STRICT prior to PHP 5.4.0.     32767 in PHP 5.4.x, 30719 in PHP 5.3.x, 6143 in PHP 5.2.x, 2047 previously

(Copied from http://php.net/manual/en/errorfunc.constants.php)


nicolas dot grekas+php at gmail dot com

9 years ago


If you want to be sure that the native PHP error handler is called without resetting the handler stack (as set_error_handler(null) does), you can simply call set_error_handler with $error_types set to zero. This can be especially use full in conjunction with e.g. error_get_last():

<?php// var_dump or anything else, as this will never be called because of the 0
set_error_handler('var_dump', 0);
@
$undef_var;
restore_error_handler();// error_get_last() is now in a well known state:
// Undefined variable: undef_var
... // Do something$e = error_get_last();

...

?>


phil at propcom dot co dot uk

9 years ago


It is important to note that the registered SPL autoloader will NOT be called if an E_STRICT error triggers the error handler which, in turn, tries to use classes which are not yet loaded.

In this instance, you should manually load classes required by the error handler.


stepheneliotdewey at GmailDotCom

15 years ago


The manual states:

"errcontext will contain an array of every variable that existed in the scope the error was triggered in. User error handler must not modify error context."

But do you know WHY you must not modify the error context? It appears that errcontext is (in effect if not literally) created by taking $GLOBALS and adding the non-global local variables as additional entries in that array, then passing the whole thing *by reference*.

(You can prove this to be true if you set up a custom error handler and then print_r($errcontext) within it, because $GLOBALS will be printed as a recursive array).

In other words, the language in the manual is misleading, because errcontext is NOT a copy of the variables that existed when the error WAS triggered, but rather is a reference to the *existing LIVE variables* in the calling script.

This includes superglobal variables like $_SERVER, $_POST, $_GET, etc., as well as all user-defined variables in scope.

The significance of that is that if you modify errcontext, you will be modifying those other variables, not just for the life of your error handling function, but for the life of the calling script as well.

That doesn't matter if you plan to halt execution in your error handling function, but it will lead to unexpected behavior if you modify $errcontext and then return to the program's normal flow after handling the error, because the variables will stay modified. For example, if you unset $_SERVER in your custom error handling function, it will remain unset once the function is over and you have returned to the page that generated the error.

This should be made clearer in the manual, starting by marking errhandler with an ampersand (&) for passage by reference in the "Parameters" section above, like so:

handler ( int $errno, string $errstr [, string $errfile [, int $errline [, array &$errcontext]]] )


Anonymous

19 years ago


It seems that when you're letting PHP know that you have a custom  error handler, you're not able to -update/set new- variables inside the class. Example:

<?php

class error {

   var
$error;

   function

error() {

      
$this->setIni();    // this causes PHP to ignore all other changes to the class.

  
}

   function

handler() {

       echo
$this->error.'!!';

   }

   function

setText($text) {

        
$this->error = $text;

   }

   function

setIni() {

      
set_error_handler(array($this, 'handler'));

   }

}
$eh = new error;

$eh->setText('Error! <br>');  // this will not be saved
trigger_error('text', E_USER_ERROR);

// prints '!!'

?>



How it should be done:

<?php

class error {

   var
$error;

   function

error() {

        
// dont let PHP know of our error handler yet

  
}

   function

handler() {

       echo
$this->error.'!!';

   }

   function

setText($text) {

        
$this->error = $text;

   }

   function

setIni() {

      
set_error_handler(array($this, 'handler'));

   }

}
$eh = new error;

$eh->setText('Error! <br>');  // this WILL work

$eh->setIni();  // call this method when you're ready with configuring the class. All other methods that will be called will have no effect on the errorHandling by PHP
trigger_error('text', E_USER_ERROR);

// prints 'Error! <br>!!'

?>


periklis

12 years ago


How to handle fatal errors in php 5.2:

<?php

register_shutdown_function
('shutdownFunction');

function
shutDownFunction() {

   
$error = error_get_last();

    if (
$error['type'] == 1) {

       
//do your stuff    

   
}

}

?>


Klauss

5 years ago


Hi everyone. I don't know if it is an old behavior of previous versions, but currently you can set exception and error handlers as private or protected methos, if, only if, you call `set_exception_handler()` or `set_error_handler()` within a context that can access the method.

Example:
    <?PHP
        $Handler
= new class ()
        {
            public function
__construct ()
            {
               
set_error_handler([&$this, 'HandleError']);
               
set_exception_handler([&$this, 'HandleException']);
            }
            protected function
HandleError ( $Code, $Message, $File = null, $Line = 0, $Context = [] )
            {
               
// Handle error here.
           
}
            private function
HandleException ( $Exception )
            {
               
// Handle exception here.
           
}
        }
   
?>

NOTE: these methods must match the callbacks parameters signatures.


RGraph

3 months ago


A simple error handler that makes errors far more visible:

    //
    // Set the error handler to one that prints out more
    // visible errors
    //
    set_error_handler(function ($errno, $errstr)
    {
        $str = '<div style="margin: 20px; background-color: #fdd; border: 3px solid red; padding: 10px; border-radius: 15px; line-height: 25px"><b>Error: </b>%s (error level: %s)</div>';

                printf($str, $errstr, $errno);
    });


a dot ross at amdev dot eu

4 years ago


I'm missing a way to chain error handlers. It's not something offered by set_error_handler. You have to jump through some hoops to get it to work, but it *is* quite possible, by making use of the return value of the function. Here's an example:

<?
$previous = set_error_handler(function ($errno, $errstr, $errfile, $errline, $errcontext) use (&$previous) {
    /* Your custom error handling code here. */

    // If another error handler was defined, call it.
    if ($previous) {
        return $previous($errno, $errstr, $errfile, $errline, $errcontext);
    } else {
        // Use the standard PHP error handler.
        return false;
    }
});
?>


kaioker

1 year ago


super simple error code to human readable conversion:

function prettycode($code){
    return $code == 0 ? "FATAL" : array_search($code, get_defined_constants(true)['Core']);
}


Alex M

2 years ago


If you are new to programming and you would like to know how to add a combination of those error reporting values to .htaccess file. Here's a small guide.

With PHP function error_reporting we can add together option with bitwise add operator | . But we can't use those constants in htaccess file and in my case I have no idea how to add bitwise number.

So, solution can be casting selected options to int:

echo (int)(E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR) ;
->263

Then you can use 263 in .htaccess

php_value error_reporting 263

In my case I needed those errors to be displayed for my debugging server. But the combination can be different from mine.


David Spector

2 years ago


The PHP manual is not very clear about how to handle @ operator error messages.

Here is working code:

    // Do nothing if @ operator
    $errLevel=error_reporting(E_ALL);
    if ($errLevel===0)
        return true; // ignore @ prefixed expression errors


chris at ocproducts dot com

6 years ago


Note that error handlers don't run recursively. If you have an error while an error handler is running (in the error handler itself or code called from it) then you won't get the error handler called again.

This has subtle ramifications for $php_errormsg. If you are relying on your error handler to suppress certain kinds of error message from going into $php_errormsg (via return true; because error_reporting doesn't affect $php_errormsg setting) then this will not work for any code called within that error handler.


devermin at ti0n dot net

13 years ago


At work I have some code with errors that uncatched are the causes of integrity loss (people calling web services with file_get_contents that fails silently and afterwards insert garbage in the database).

here is the solution I found to transform a specific set of errors into exception and afterwards be able to selectively act (with the error code) regarding categories : 

<?php

ini_set
('error_reporting',E_ALL^E_NOTICE);
## first 10 bits reserved for the initial error number

define('EMASK',(~0)<<10);

define('ECODEMASK',~EMASK);

## categories

define('IOERROR', 1<<10);

define('EMPTYPARMS', 1<<11);

define('FAILURE', 1<<12);

## string error patterns => code
$catch_me=array(

   
"/^(file_get_contents)((.*)).*failed to open stream: (.*)/ " =>

        array (
'mesg' => "IO::Failed to open stream with",

               
'code' => IOERROR | FAILURE

       
),

   
"/^fopen(.*): Filename cannot be empty/" =>

        array(
'msg' => "Parameters::empty",

               
'code' => EMPTYPARMS

       
)

    );

function
error_2_exception($errno, $errstr, $errfile, $errline,$context) {

    global
$catch_me;

    foreach (
$catch_me as $regexp  => $res) {

        if(
preg_match($regexp,$errstr,$match)){

            throw new
Exception($res['mesg'],$res['code']|( $errno & EMASK ) );

        }

    }

   
/* switch back to PHP internal error handler  */

   
return false;

}

## => want to catch this one

$f=file_get_contents("mlsdkfm");

## dont want to break existing wrong behaviour yet (so not caught)

$f=file_get_contents('');

## magic

set_error_handler("error_2_exception");

## behaviour remains the same

$f=file_get_contents('');

try {

## web services that dont work now raise an exception o/

$f=file_get_contents("mlsdkfm");

} catch(
Exception $e) {

## and I can group my exception by category

   
echo ( $e->getCode() & FAILURE )  ? "nEPIC FAILn" : "nbegnine";

}
?>


ash

15 years ago


error handling function that handles both errors and exceptions; also features a backtrace including possible function arguments.

<?php

$cfg

= array();
$cfg['debug'] = 1;
$cfg['adminEmail'] = 'name@domain.tld';

function

errorHandler($errno, $errstr='', $errfile='', $errline='')
{
   
// if error has been supressed with an @
   
if (error_reporting() == 0) {
        return;
    }

    global

$cfg;// check if function has been called by an exception
   
if(func_num_args() == 5) {
       
// called by trigger_error()
       
$exception = null;
        list(
$errno, $errstr, $errfile, $errline) = func_get_args();$backtrace = array_reverse(debug_backtrace());

    }else {

// caught exception
       
$exc = func_get_arg(0);
       
$errno = $exc->getCode();
       
$errstr = $exc->getMessage();
       
$errfile = $exc->getFile();
       
$errline = $exc->getLine();$backtrace = $exc->getTrace();
    }
$errorType = array (
              
E_ERROR            => 'ERROR',
              
E_WARNING        => 'WARNING',
              
E_PARSE          => 'PARSING ERROR',
              
E_NOTICE         => 'NOTICE',
              
E_CORE_ERROR     => 'CORE ERROR',
              
E_CORE_WARNING   => 'CORE WARNING',
              
E_COMPILE_ERROR  => 'COMPILE ERROR',
              
E_COMPILE_WARNING => 'COMPILE WARNING',
              
E_USER_ERROR     => 'USER ERROR',
              
E_USER_WARNING   => 'USER WARNING',
              
E_USER_NOTICE    => 'USER NOTICE',
              
E_STRICT         => 'STRICT NOTICE',
              
E_RECOVERABLE_ERROR  => 'RECOVERABLE ERROR'
              
);// create error message
   
if (array_key_exists($errno, $errorType)) {
       
$err = $errorType[$errno];
    } else {
       
$err = 'CAUGHT EXCEPTION';
    }
$errMsg = "$err: $errstr in $errfile on line $errline";// start backtrace
   
foreach ($backtrace as $v) {

        if (isset(

$v['class'])) {$trace = 'in class '.$v['class'].'::'.$v['function'].'(';

            if (isset(

$v['args'])) {
               
$separator = '';

                foreach(

$v['args'] as $arg ) {
                   
$trace .= "$separator".getArgument($arg);
                   
$separator = ', ';
                }
            }
           
$trace .= ')';
        }

        elseif (isset(

$v['function']) && empty($trace)) {
           
$trace = 'in function '.$v['function'].'(';
            if (!empty(
$v['args'])) {$separator = '';

                foreach(

$v['args'] as $arg ) {
                   
$trace .= "$separator".getArgument($arg);
                   
$separator = ', ';
                }
            }
           
$trace .= ')';
        }
    }
// display error msg, if debug is enabled
   
if($cfg['debug'] == 1) {
        echo
'<h2>Debug Msg</h2>'.nl2br($errMsg).'<br />
            Trace: '
.nl2br($trace).'<br />';
    }
// what to do
   
switch ($errno) {
        case
E_NOTICE:
        case
E_USER_NOTICE:
            return;
            break;

        default:
            if(

$cfg['debug'] == 0){
               
// send email to admin
               
if(!empty($cfg['adminEmail'])) {
                    @
mail($cfg['adminEmail'],'critical error on '.$_SERVER['HTTP_HOST'], $errorText,
                           
'From: Error Handler');
                }
               
// end and display error msg
               
exit(displayClientMessage());
            }
            else
                exit(
'<p>aborting.</p>');
            break;

    }

}

// end of errorHandler()function displayClientMessage()
{
    echo
'some html page with error message';

}

function

getArgument($arg)
{
    switch (
strtolower(gettype($arg))) {

        case

'string':
            return(
'"'.str_replace( array("n"), array(''), $arg ).'"' );

        case

'boolean':
            return (bool)
$arg;

        case

'object':
            return
'object('.get_class($arg).')';

        case

'array':
           
$ret = 'array(';
           
$separtor = '';

            foreach (

$arg as $k => $v) {
               
$ret .= $separtor.getArgument($k).' => '.getArgument($v);
               
$separtor = ', ';
            }
           
$ret .= ')';

            return

$ret;

        case

'resource':
            return
'resource('.get_resource_type($arg).')';

        default:
            return

var_export($arg, true);
    }
}
?>


mmtache at yahoo dot com

20 years ago


The @ operator sets the error_reporting() value to 0.
This means you can use it with your own Error Handler too. for example:

function userErrorHandler($errno, $errmsg, $filename, $linenum, $vars) {
   if (error_reporting())
echo $errmsg;
    }
set_error_handler("userErrorHandler");

function test(){
trigger_error("Error Message", E_USER_WARNING);
}

@test(); // doesn't output anything


Иногда ваше приложение не запускается должным образом, что приводит к ошибкам. Есть ряд причин, которые могут вызвать ошибки, например:

  • Веб-серверу может не хватить места на диске;
  • Пользователь мог ввести недопустимое значение в поле формы;
  • Файл или запись базы данных, к которой вы пытались получить доступ, возможно, не существует;
  • Приложение может не иметь разрешения на запись в файл на диске;
  • Служба, к которой приложение должно получить доступ, может быть временно недоступна.

Эти типы ошибок известны как ошибки времени выполнения, потому что они возникают во время выполнения скрипта. Они отличаются от синтаксических ошибок, которые необходимо исправлять перед запуском скриптов.

Профессиональное приложение должно иметь возможность изящно обрабатывать такие ошибки времени выполнения. Обычно это означает более четкое и точное информирование пользователя о проблеме.

Понимание уровней ошибок

Обычно, когда возникает проблема, препятствующая правильной работе скрипта, механизм PHP выдает ошибку. Каждая ошибка представлена целым числом и соответствующей константой. В следующей таблице перечислены некоторые из распространенных уровней ошибок:

Название Значение Описание
E_ERROR 1 Неустранимая ошибка времени выполнения от которой невозможно избавиться. Выполнение скрипта немедленно прекращается.
E_WARNING 2 Предупреждение во время выполнения. Она несущественна, и большинство ошибок попадают в эту категорию. Выполнение скрипта не останавливается.
E_NOTICE 8 Уведомление во время выполнения. Указывает, что скрипт обнаружил что-то, что могло быть ошибкой, хотя такая ситуация также может возникнуть при обычном запуске скрипта.
E_USER_ERROR 256 Сообщение о фатальной пользовательской ошибке. Она похожа на E_ERROR, за исключением того, что она генерируется PHP-скриптом с использованием функции trigger_error().
E_USER_WARNING 512 Предупреждающее сообщение, созданное пользователем без фатального исхода. Она похожа на E_WARNING, за исключением того, что она генерируется PHP-скриптом с использованием функции trigger_error().
E_USER_NOTICE 1024 Сообщение с уведомлением, созданное пользователем. Она похожа на E_NOTICE за исключением того, что она генерируется PHP-скриптом с использованием функции trigger_error().
E_STRICT 2048 Не совсем ошибка, но срабатывает всякий раз, когда PHP встречает код, который может привести к проблемам или несовместимости пересылки.
E_ALL 8191 Все ошибки и предупреждения, кроме E_STRICT до PHP 5.4.0.

Дополнительные сведения об уровнях ошибок см. в справочнике по уровням ошибок PHP.

Механизм PHP вызывает ошибку всякий раз, когда он сталкивается с проблемой в вашем скрипте, но вы также можете инициировать ошибки самостоятельно, чтобы генерировать более удобные сообщения об ошибках. Таким образом вы можете сделать свое приложение более сложным. В следующем разделе описаны некоторые из распространенных методов, используемых для обработки ошибок в PHP:

Базовая обработка ошибок с помощью функции die()

Рассмотрим следующий пример, в котором просто попытаемся открыть текстовый файл только для чтения.

<?php
// Пробуем открыть несуществующий файл
$file = fopen("sample.txt", "r"); // Выводит: Warning: fopen(sample.txt) [function.fopen]: failed to open stream: No such file or directory in C:wampwwwprojecttest.php on line 2
?>

Если мы выполним несколько простых шагов, мы сможем предотвратить получение пользователями такого сообщения об ошибке.

<?php
if(file_exists("sample.txt")){
    $file = fopen("sample.txt", "r");
} else{
    die("Error: The file you are trying to access doesn't exist.");
}
?>

Как вы можете видеть, реализовав простую проверку, существует ли файл перед попыткой доступа к нему, мы можем сгенерировать сообщение об ошибке, которое будет более понятным для пользователя.

Используемая выше функция die() просто отображает пользовательское сообщение об ошибке и завершает текущий скрипт, если файл sample.txt не найден.

Создание собственного обработчика ошибок

Вы можете создать свою собственную функцию обработчика ошибок, чтобы справляться с ошибкой времени выполнения, генерируемой механизмом PHP. Пользовательский обработчик ошибок обеспечивает большую гибкость и лучший контроль над ошибками; он может проверять ошибку и решать, что с ней делать — отображать сообщение пользователю, регистрировать ошибку в файле или базе данных или отправлять по электронной почте, попытаться исправить проблему и продолжить, выйти из выполнения скрипта или вообще игнорировать ошибку.

Функция пользовательского обработчика ошибок должна иметь возможность обрабатывать как минимум два параметра (errno и errstr), однако она может дополнительно принимать три дополнительных параметра (errfile, errline и errcontext), как описано ниже:

Параметр Описание
Обязательно — следующие параметры обязательны
errno. Задает уровень ошибки в виде целого числа. Это соответствует соответствующей константе уровня ошибки (E_ERROR, E_WARNING и т. д.).
errstr. Задает сообщение об ошибке в виде строки.
Опционально — следующие параметры являются необязательными
errfile. Задает имя файла скрипта, в котором произошла ошибка.
errline. Задает номер строки, в которой произошла ошибка.
errcontext. Задает массив, содержащий все переменные и их значения, которые существовали на момент возникновения ошибки. Полезно для отладки.

Вот пример простой пользовательской функции обработки ошибок. Этот обработчик customError() запускается всякий раз, когда возникает ошибка, какой бы тривиальной она ни была. Затем он выводит сведения об ошибке в браузер и останавливает выполнение скрипта.

<?php
// Функция обработчика ошибок
function customError($errno, $errstr){
    echo "<b>Error:</b> [$errno] $errstr";
}
?>

Вам нужно указать PHP, чтобы он использовал вашу пользовательскую функцию обработчика ошибок — просто вызовите встроенную функцию set_error_handler(), передав имя функции.

<?php
// Функция обработчика ошибок
function customError($errno, $errstr){
    echo "<b>Error:</b> [$errno] $errstr";
}
 
// Устанавливаем обработчик ошибок
set_error_handler("customError");
 
// Вызываем ошибку
echo($test);
?>

Регистрация ошибок

Журнал сообщений об ошибках в текстовом файле

Вы также можете записать подробную информацию об ошибке в файл журнала, например:

<?php
function calcDivision($dividend, $divisor){
    if($divisor == 0){
        trigger_error("calcDivision(): The divisor cannot be zero", E_USER_WARNING);
        return false;
    } else{
        return($dividend / $divisor);
    }
}
function customError($errno, $errstr, $errfile, $errline, $errcontext){
    $message = date("Y-m-d H:i:s - ");
    $message .= "Error: [" . $errno ."], " . "$errstr in $errfile on line $errline, ";
    $message .= "Variables:" . print_r($errcontext, true) . "rn";
    
    error_log($message, 3, "logs/app_errors.log");
    die("There was a problem, please try again.");
}
set_error_handler("customError");
echo calcDivision(10, 0);
echo "This will never be printed.";
?>

Отправка сообщений об ошибках по электронной почте

Вы также можете отправить электронное письмо с подробностями об ошибке, используя ту же функцию error_log().

<?php
function calcDivision($dividend, $divisor){
    if ($divisor == 0){
        trigger_error("calcDivision(): The divisor cannot be zero", E_USER_WARNING);
        return false;
    } else{
        return($dividend / $divisor);
    }
}
function customError($errno, $errstr, $errfile, $errline, $errcontext){
    $message = date("Y-m-d H:i:s - ");
    $message .= "Error: [" . $errno ."], " . "$errstr in $errfile on line $errline, ";
    $message .= "Variables:" . print_r($errcontext, true) . "rn";
    
    error_log($message, 1, "webmaster@example.com");
    die("There was a problem, please try again. Error report submitted to webmaster.");
}
set_error_handler("customError");
echo calcDivision(10, 0);
echo "This will never be printed.";
?>

Вызов ошибок

Хотя движок PHP выдает ошибку всякий раз, когда он сталкивается с проблемой в вашем скрипте, вы также можете вызвать ошибки самостоятельно. Это может помочь сделать ваше приложение более надежным, поскольку оно может выявлять потенциальные проблемы до того, как они перерастут в серьезные ошибки.

Чтобы вызвать ошибку в скрипте, вызовите функцию trigger_error(), передав сообщение об ошибке, которое вы хотите сгенерировать:

trigger_error("There was a problem.");

Рассмотрим следующую функцию, которая вычисляет деление двух чисел.

<?php
function calcDivision($dividend, $divisor){
    return($dividend / $divisor);
}
 
// Вызываем функцию
echo calcDivision(10, 0); // Выводит: Warning: Division by zero in C:wampwwwprojecttest.php on line 3
?>

Это сообщение выглядит не очень информативным. Рассмотрим следующий пример, в котором для генерации ошибки используется функция trigger_error().

<?php
function calcDivision($dividend, $divisor){
    if($divisor == 0){
        trigger_error("Делитель не может быть нулевым", E_USER_WARNING);
        return false;
    } else{
        return($dividend / $divisor);
    }
}
 
// Вызываем функцию
echo calcDivision(10, 0); // Выводит: Warning: Делитель не может быть нулевым C:wampwwwprojecterror.php on line 4
?>

Как видите, сообщение об ошибке, созданное во втором примере, более четко объясняет проблему по сравнению с предыдущим.

set_error_handler

(PHP 4 >= 4.0.1, PHP 5, PHP 7)

set_error_handler
Задает определенный пользователем обработчик ошибок

Описание

mixed set_error_handler
( callable $error_handler
[, int $error_types = E_ALL | E_STRICT
] )

Эта функция используется для определения собственного обработчика ошибок
времени выполнения скрипта. Например, если требуется очистить данные/файлы,
когда произошла критическая ошибка, или если нужно переключить тип ошибки,
исходя из каких-то условий (используя функцию
trigger_error()).

Важно помнить, что стандартный обработчик ошибок PHP не будет обрабатывать
никакие типы ошибок, определенные в error_types,
пока callback-функция не вернет FALSE. Пользовательский обработчик будет
вызываться при любой ошибке, независимо от настроек, заданных функцией
error_reporting(). Однако, вы можете прочитать текущее
значение error_reporting и
задать в обработчике соответствующие действия. В частности, это значение
будет равно 0, если выражение, вызвавшее ошибку, начинается с
оператора контроля
ошибок @.

Также важно помнить, что на совести обработчика лежит вызов функции
die() в случае необходимости. Если происходит возврат
их обработчика ошибок, управление передается следующему выражению,
стоящему за тем, что вызвало ошибку.

Ошибки следующих типов не могут быть обработаны пользователем:
E_ERROR, E_PARSE,
E_CORE_ERROR, E_CORE_WARNING,
E_COMPILE_ERROR,
E_COMPILE_WARNING, и большинство
E_STRICT ошибок, произошедших в файле, где вызвана
функция set_error_handler().

Если ошибки возникают до запуска скрипта (например, пока файл загружается),
пользовательский обработчик не будет вызываться, если на этот момент он
еще не зарегистрирован.

Список параметров

error_handler

Функция указанного ниже вида.
Можно передать вместо нее NULL, для сброса обработчика в значение
по умолчанию. Помимо имени функции, можно передать массив, содержащий
объект и имя метода.

bool handler
( int $errno
, string $errstr
[, string $errfile
[, int $errline
[, array $errcontext
]]] )

errno


Первый аргумента errno содержит уровень
ошибки в виде целого числа.

errstr


Второй аргумент errstr содержит сообщение
об ошибке в виде строки.

errfile


Третий необязательный аргумент errfile
содержит имя файла, в котором произошла ошибка, в виде строки.

errline


Четвертый необязательный аргумент errline
содержит номер строки, в которой произошла ошибка, в виде целого
числа.

errcontext


Пятый необязательный аргумент errcontext
содержит массив указателей на активную таблицу символов в точке, где
произошла ошибка. Другими словами, errcontext
будет содержать массив всех переменных, существующих в области
видимости, где произошла ошибка. Пользовательский обработчик не
должен изменять этот контекст.

Если функция возвращает FALSE, управление передается встроенному
обработчику ошибок.

error_types

Может использоваться для задания маски, в соответствии с которой будет
вызываться error_handler, по аналогии с ini
настройкой error_reporting,
которая отвечает за то, какие ошибки будут показаны в отчете. Без этой
маски error_handler будет вызываться для
обработки всех происходящих ошибок, вне зависимости от настроек в
error_reporting.

Возвращаемые значения

Возвращает строку содержащую предыдущий заданный обработчик ошибок (если
есть). Если на данный момент используется встроенный обработчик, функция
вернет NULL. В случае ошибки в работе самой функции, например,
недопустимый обратный вызов, функция также вернет NULL. Если предыдущий
определенный обработчик является методом класса, функция вернет массив
содержащий имя класса и имя метода.

Список изменений

Версия Описание
5.5.0 error_handler теперь может принимать NULL.
5.2.0 Обработчик должен вернуть FALSE, чтобы заполнилось значение
переменной $php_errormsg.

Примеры

Пример #1
Обработка ошибок с помощью функций set_error_handler()
и trigger_error()

Пример ниже демонстрирует обработку внутренних исключений путем
вызова ошибок разных типов и их обработки пользовательской функцией:


<?php
// функция обработки ошибок
function myErrorHandler($errno$errstr$errfile$errline)
{
    if (!(
error_reporting() & $errno)) {
        
// Этот код ошибки не включен в error_reporting
        
return;
    }

    switch (

$errno) {
    case 
E_USER_ERROR:
        echo 
"<b>My ERROR</b> [$errno$errstr<br />n";
        echo 
"  Фатальная ошибка в строке $errline файла $errfile";
        echo 
", PHP " PHP_VERSION " (" PHP_OS ")<br />n";
        echo 
"Завершение работы...<br />n";
        exit(
1);
        break;

    case 

E_USER_WARNING:
        echo 
"<b>My WARNING</b> [$errno$errstr<br />n";
        break;

    case 

E_USER_NOTICE:
        echo 
"<b>My NOTICE</b> [$errno$errstr<br />n";
        break;

    default:
        echo 

"Неизвестная ошибка: [$errno$errstr<br />n";
        break;
    }
/* Не запускаем внутренний обработчик ошибок PHP */
    
return true;
}
// функция для тестирования обработчика ошибок
function scale_by_log($vect$scale)
{
    if (!
is_numeric($scale) || $scale <= 0) {
        
trigger_error("log(x) для x <= 0 не определен, вы используете: scale = $scale"E_USER_ERROR);
    }

    if (!

is_array($vect)) {
        
trigger_error("Некорректный входной вектор, пропущен массив значений"E_USER_WARNING);
        return 
null;
    }
$temp = array();
    foreach(
$vect as $pos => $value) {
        if (!
is_numeric($value)) {
            
trigger_error("Значение на позиции $pos не является числом, будет использован 0 (ноль)"E_USER_NOTICE);
            
$value 0;
        }
        
$temp[$pos] = log($scale) * $value;
    }

    return 

$temp;
}
// переключаемся на пользовательский обработчик
$old_error_handler set_error_handler("myErrorHandler");// вызовем несколько ошибок, во-первых, определим массив с нечисловым элементом
echo "vector an";
$a = array(23"foo"5.543.321.11);
print_r($a);// теперь создадим еще один массив
echo "----nvector b - a notice (b = log(PI) * a)n";
/* Значение на позиции $pos не является числом, будет использован 0 (ноль)*/
$b scale_by_log($aM_PI);
print_r($b);// проблема, мы передаем строку вместо массива
echo "----nvector c - a warningn";
/* Некорректный входной вектор, пропущен массив значений */
$c scale_by_log("not array"2.3);
var_dump($c); // NULL

// критическая ошибка, логарифм от неположительного числа не определен

echo "----nvector d - fatal errorn";
/* log(x) для x <= 0 не определен, вы используете: scale = $scale */
$d scale_by_log($a, -2.5);
var_dump($d); // До сюда не доберемся никогда
?>

Результатом выполнения данного примера
будет что-то подобное:

vector a
Array
(
    [0] => 2
    [1] => 3
    [2] => foo
    [3] => 5.5
    [4] => 43.3
    [5] => 21.11
)
----
vector b - a notice (b = log(PI) * a)
<b>My NOTICE</b> [1024]  Значение на позиции 2 не является числом, будет использован 0 (ноль)<br />
Array
(
    [0] => 2.2894597716988
    [1] => 3.4341896575482
    [2] => 0
    [3] => 6.2960143721717
    [4] => 49.566804057279
    [5] => 24.165247890281
)
----
vector c - a warning
<b>My WARNING</b> [512] Некорректный входной вектор, пропущен массив значений<br />
NULL
----
vector d - fatal error
<b>My ERROR</b> [256] log(x) for x <= 0 is undefined, you used: scale = -2.5<br />
  Фатальная ошибка в строке 35 файла trigger_error.php, PHP 5.2.1 (FreeBSD)<br />
Завершение работы...<br />

Смотрите также

  • ErrorException
  • error_reporting() — Задает, какие ошибки PHP попадут в отчет
  • restore_error_handler() — Восстанавливает предыдущий обработчик ошибок
  • trigger_error() — Вызывает пользовательскую ошибку/предупреждение/уведомление
  • Константы уровней ошибок
  • информация о типе callback

Вернуться к: Функции обработки ошибок

Время на прочтение
6 мин

Количество просмотров 66K

В статье представлена очередная попытка разобраться с ошибками, которые могут встретиться на вашем пути php-разработчика, их возможная классификация, примеры их возникновения, влияние ошибок на ответ клиенту, а также инструкции по написанию своего обработчика ошибок.

Статья разбита на четыре раздела:

  1. Классификация ошибок.
  2. Пример, демонстрирующий различные виды ошибок и его поведение при различных настройках.
  3. Написание собственного обработчика ошибок.
  4. Полезные ссылки.

Классификация ошибок

Все ошибки, условно, можно разбить на категории по нескольким критериям.
Фатальность:

  • Фатальные
    Неустранимые ошибки. Работа скрипта прекращается.
    E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR.
  • Не фатальные
    Устранимые ошибки. Работа скрипта не прекращается.
    E_WARNING, E_NOTICE, E_CORE_WARNING, E_COMPILE_WARNING, E_USER_WARNING, E_USER_NOTICE, E_STRICT, E_DEPRECATED, E_USER_DEPRECATED.
  • Смешанные
    Фатальные, но только, если не обработаны функцией, определенной пользователем в set_error_handler().
    E_USER_ERROR, E_RECOVERABLE_ERROR.

Возможность перехвата ошибки функцией, определенной в set_error_handler():

  • Перехватываемые (не фатальные и смешанные)
    E_USER_ERROR, E_RECOVERABLE_ERROR, E_WARNING, E_NOTICE, E_USER_WARNING, E_USER_NOTICE, E_STRICT, E_DEPRECATED, E_USER_DEPRECATED.
  • Не перехватываемые (фатальные)
    E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING.

Инициатор:

  • Инициированы пользователем
    E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE.
  • Инициированы PHP
    E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_WARNING, E_NOTICE, E_CORE_WARNING, E_COMPILE_WARNING, E_STRICT, E_DEPRECATED, E_USER_DEPRECATED, E_USER_ERROR, E_RECOVERABLE_ERROR.

Для нас, в рамках данной статьи, наиболее интересны классификации по первым двум критериям, о чем будет рассказано далее.

Примеры возникновения ошибок

Листинг index.php

<?php
// определеяем уровень протоколирования ошибок
error_reporting(E_ALL | E_STRICT);
// определяем режим вывода ошибок
ini_set('display_errors', 'On');
// подключаем файл с ошибками
require 'errors.php';

Листинг errors.php

<?php
echo "Файл с ошибками. Начало<br>";
/*
 * перехватываемые ошибки (ловятся функцией set_error_handler())
 */
// NONFATAL - E_NOTICE
// echo $undefined_var;
// NONFATAL - E_WARNING
// array_key_exists('key', NULL);
// NONFATAL - E_DEPRECATED
split('[/.-]', "12/21/2012"); // split() deprecated начиная с php 5.3.0
// NONFATAL - E_STRICT
// class c {function f(){}} c::f();
// NONFATAL - E_USER_DEPRECATED
// trigger_error("E_USER_DEPRECATED", E_USER_DEPRECATED);
// NONFATAL - E_USER_WARNING
// trigger_error("E_USER_WARNING", E_USER_WARNING);
// NONFATAL - E_USER_NOTICE
// trigger_error("E_USER_NOTICE", E_USER_NOTICE);

// FATAL, если не обработана функцией set_error_handler - E_RECOVERABLE_ERROR
// class b {function f(int $a){}} $b = new b; $b->f(NULL);
// FATAL, если не обработана функцией set_error_handler - E_USER_ERROR
// trigger_error("E_USER_ERROR", E_USER_ERROR);

/*
 * неперехватываемые (не ловятся функцией set_error_handler())
 */
// FATAL - E_ERROR
// undefined_function();
// FATAL - E_PARSE
// parse_error
// FATAL - E_COMPILE_ERROR
// $var[];

echo "Файл с ошибками. Конец<br>";

Примечание: для полной работоспособности скрипта необходим PHP версии не ниже 5.3.0.

В файле errors.php представлены выражения, инициирующие практически все возможные ошибки. Исключение составили: E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_WARNING, генерируемые ядром Zend. В теории, встретить их в реальной работе вы не должны.
В следующей таблице приведены варианты поведения этого скрипта в различных условиях (в зависимости от значений директив display_errors и error_reporting):

Группа ошибок Значения директив* Статус ответа сервера Ответ клиенту**
E_PARSE, E_COMPILE_ERROR*** display_errors = off
error_reporting = ANY
500 Пустое значение
display_errors = on
error_reporting = ANY
200 Сообщение об ошибке
E_USER_ERROR, E_ERROR, E_RECOVERABLE_ERROR display_errors = off
error_reporting = ANY
500 Вывод скрипта до ошибки
display_errors = on
error_reporting = ANY
200 Сообщение об ошибке и вывод скрипта до ошибки
Не фатальные ошибки display_errors = off
error_reporting = ANY
и
display_errors = on
error_reporting = 0
200 Весь вывод скрипта
display_errors = on
error_reporting = E_ALL | E_STRICT
200 Сообщение об ошибке и весь вывод скрипта

* Значение ANY означает E_ALL | E_STRICT или 0.
** Ответ клиенту может отличаться от ответов на реальных скриптах. Например, вывод какой-либо информации до включения файла errors.php, будет фигурировать во всех рассмотренных случаях.
*** Если в файле errors.php заменить пример для ошибки E_COMPILE_ERROR на require "missing_file.php";, то ошибка попадет во вторую группу.

Значение, приведенной выше, таблицы можно описать следующим образом:

  1. Наличие в файле скрипта ошибки, приводящей его в «негодное» состояние (невозможность корректно обработать), на выходе даст пустое значение или же только само сообщение об ошибке, в зависимости от значения директивы display_errors.
  2. Скрипт в файле с фатальной ошибкой, не относящейся к первому пункту, будет выполняться в штатном режиме до самой ошибки.
  3. Наличие в файле фатальной ошибки при display_errors = Off обозначит 500 статус ответа.
  4. Не фатальные ошибки, как и следовало ожидать, в контексте возможности исполнения скрипта в целом, на работоспособность не повлияют.

Собственный обработчик ошибок

Для написания собственного обработчика ошибок необходимо знать, что:

  • для получения информации о последней произошедшей ошибке существует функция error_get_last();
  • для определения собственного обработчика ошибок существует функция set_error_handler(), но фатальные ошибки нельзя «перехватить» этой функцией;
  • используя register_shutdown_function(), можно зарегистрировать свою функцию, выполняемую по завершении работы скрипта, и в ней, используя знания из первого пункта, если фатальная ошибка имела место быть, предпринять необходимые действия;
  • сообщение о фатальной ошибке в любом случае попадет в буфер вывода;
  • воспользовавшись функциями контроля вывода можно предотвратить отображение нежелательной информации;
  • при использовании оператора управления ошибками (знак @) функция, определенная в set_error_handler() все равно будет вызвана, но функция error_reporting() в этом случае вернет 0, чем и можно пользоваться для прекращения работы или определения другого поведения своего обработчика ошибок.

Третий пункт поясню: зарегистрированная нами функция при помощи register_shutdown_function() выполнится в любом случае — корректно ли завершился скрипт, либо же был прерван в связи с критичной (фатальной) ошибкой. Второй вариант мы можем однозначно определить, воспользовавшись информацией предоставленной функцией error_get_last(), и, если ошибка все же была, выполнить наш собственный обработчик ошибок.
Продемонстрируем вышесказанное на модифицированном скрипте index.php:

<?php
/**
 * Обработчик ошибок
 * @param int $errno уровень ошибки
 * @param string $errstr сообщение об ошибке
 * @param string $errfile имя файла, в котором произошла ошибка
 * @param int $errline номер строки, в которой произошла ошибка
 * @return boolean
 */
function error_handler($errno, $errstr, $errfile, $errline)
{
    // если ошибка попадает в отчет (при использовании оператора "@" error_reporting() вернет 0)
    if (error_reporting() & $errno)
    {
        $errors = array(
            E_ERROR => 'E_ERROR',
            E_WARNING => 'E_WARNING',
            E_PARSE => 'E_PARSE',
            E_NOTICE => 'E_NOTICE',
            E_CORE_ERROR => 'E_CORE_ERROR',
            E_CORE_WARNING => 'E_CORE_WARNING',
            E_COMPILE_ERROR => 'E_COMPILE_ERROR',
            E_COMPILE_WARNING => 'E_COMPILE_WARNING',
            E_USER_ERROR => 'E_USER_ERROR',
            E_USER_WARNING => 'E_USER_WARNING',
            E_USER_NOTICE => 'E_USER_NOTICE',
            E_STRICT => 'E_STRICT',
            E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
            E_DEPRECATED => 'E_DEPRECATED',
            E_USER_DEPRECATED => 'E_USER_DEPRECATED',
        );

        // выводим свое сообщение об ошибке
        echo "<b>{$errors[$errno]}</b>[$errno] $errstr ($errfile на $errline строке)<br />n";
    }

    // не запускаем внутренний обработчик ошибок PHP
    return TRUE;
}

/**
 * Функция перехвата фатальных ошибок
 */
function fatal_error_handler()
{
    // если была ошибка и она фатальна
    if ($error = error_get_last() AND $error['type'] & ( E_ERROR | E_PARSE | E_COMPILE_ERROR | E_CORE_ERROR))
    {
        // очищаем буффер (не выводим стандартное сообщение об ошибке)
        ob_end_clean();
        // запускаем обработчик ошибок
        error_handler($error['type'], $error['message'], $error['file'], $error['line']);
    }
    else
    {
        // отправка (вывод) буфера и его отключение
        ob_end_flush();
    }
}

// определеяем уровень протоколирования ошибок
error_reporting(E_ALL | E_STRICT);
// определяем режим вывода ошибок
ini_set('display_errors', 'On');
// включаем буфферизацию вывода (вывод скрипта сохраняется во внутреннем буфере)
ob_start();
// устанавливаем пользовательский обработчик ошибок
set_error_handler("error_handler");
// регистрируем функцию, которая выполняется после завершения работы скрипта (например, после фатальной ошибки)
register_shutdown_function('fatal_error_handler');

require 'errors.php';

Не забываем, что ошибки смешанного типа, после объявления собственного обработчика ошибок, стали не фатальными. Плюс к этому, весь вывод скрипта до фатальной ошибки вместе с стандартным сообщением об ошибке будет сброшен.

Вообще, рассмотренный пример обработчика ошибок, обработкой, как таковой, не занимается, а только демонстрирует саму возможность. Дальнейшее его поведение зависит от ваших желаний и/или требований. Например, все случаи обращения к обработчику можно записывать в лог, а в случае фатальных ошибок, дополнительно, уведомлять об этом администратора ресурса.

Полезные ссылки

  • Первоисточник: php.net/manual/ru/book.errorfunc.php
  • Описание ошибок: php.net/manual/ru/errorfunc.constants.php
  • Функции контроля вывода: php.net/manual/ru/ref.outcontrol.php
  • Побитовые операторы: php.net/manual/ru/language.operators.bitwise.php и habrahabr.ru/post/134557
  • Тематически близкая статья: habrahabr.ru/post/134499

Обработка ошибок с помощью trigger_error() и set_error_handler()

PHP предоставляет прекрасную возможность контролировать возникающие ошибки. Здесь мы поговорим о том, как обработать ошибку — сообщить (или не сообщить) о происшествии пользователю, в случае необходимости — сообщить администратору с помощью электронной почты, записать информацию о происшествии в log-файл.

Итак, для начала давайте определимся, что такое ошибки в PHP.

PHP поддерживает следующие уровни ошибок:

E_ERROR
E_WARNING
E_PARSE
E_NOTICE
E_CORE_ERROR
E_CORE_WARNING
E_COMPILE_ERROR
E_COMPILE_WARNING
E_USER_ERROR
E_USER_WARNING
E_USER_NOTICE
E_ALL
E_STRICT

На самом деле — это просто константы, которые используются для определения уровня обработки ошибок, построения бит-маски. Константы имеют «говорящие» имена. Глядя на константу — мы можем сказать, что ошибка уровня E_PARSE возникает в случае синтаксической ошибки, E_NOTICE — это напоминание программисту о нарушении «хорошего стиля» программирования на PHP.

Несколько примеров:

Когда соединение с базой данных MySQL (или другой) завершается неудачей — интерпретатор PHP сообщает об ошибке уровня E_WARNING

Warning: mysql_connect(): Access denied for user: 'VVingless@localhost' (Using password: YES)
 In /home/mysite/index.php (line 83)

Замечание: Для того чтобы интерпретатор PHP сообщал об ошибках — PHP должен быть настроен соответствующим образом: флаг display_errors должен быть включен — 1, директива error_reporting должна указывать на то, что необходимо отображать ошибки уровня E_WARNING (желательно конечно и другие). Если значения этих директив не удовлетворяют вашим требованиям — вы можете попробовать установить их самостоятельно, положив в папку со скриптом файл .htaccess (точка в начале имени обязательна) примерно такого содержания:

php_flag display_errors on
php_value error_reporting «E_ALL & ~E_NOTICE»

Это означает, что сообщения об ошибках будут показываться, причем всех уровней, кроме E_NOTICE
Когда программист допускает синтаксическую ошибку — интерпретатор PHP сообщает об ошибке уровня E_PARSE

Parse error: parse error, unexpected ‘(‘, expecting T_STRING in /home/mysite/index.php on line 150

Но самые интересные для нас уровни ошибок — E_USER_ERROR и E_USER_WARNING. Как становится понятно из названия — это уровни ошибок, которые может устанавливать пользователь. Для этого существует функция trigger_error() — с её помощью, Вы можете сообщать пользователю о происшествии так, как это делает PHP.

Как известно из руководства по PHP — функция trigger_error() принимает два параметра.

void trigger_error ( string error_msg [, int error_type])

Первый параметр — текстовое сообщение об ошибке, например «файл не найден». Второй параметр — определяет уровень ошибки. Функция trigger_error() работает только с семейством ошибок E_USER — это значит, что вы можете установить ошибку уровня E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE и не можете установить ошибку уровня E_WARNING. Второй параметр является не обязательным, и по умолчанию принимает значение E_USER_NOTICE.

Давайте попробуем:

Допустим, наши данные для ленты новостей хранятся в файле news.txt, и если файл не найден — необходимо сообщить об ошибке. Текст программы будет выглядеть примерно так:

if (!file_exists(‘/home/mysite/news.txt’)) {
trigger_error(‘News file not found’);
}

В результате интерпретатор PHP сообщит об ошибке уровня E_USER_NOTICE

Notice: News file not found in /home/mysite/index.php on line 47
Но что нам это даёт? Для начала то, что если в php.ini или файле .htaccess были установлены директивы

php_value log_errors «1»
php_value log_errors_max_len «1024»
php_value error_log «/home/mysite/my.log»
То в файл /home/mysite/my.log автоматически будет добавлена запись о происшествии.

[23-Mar-2004 13:52:03] PHP Notice: News file not found in /home/mysite/index.php on line 47
Далее, с помощью функции set_error_handler() мы можем установить свой собственный обработчик ошибок возникающих во время выполнения PHP скрипта.

Как известно из мануала — в PHP 4 функция принимает один единственный строковый параметр — имя функции, которая будет выполняться каждый раз, когда происходит ошибка. PHP 5 даёт возможность установить ещё один параметр — тип ошибок которые будут обрабатываться с помощью нашего обработчика. Функция возвращает строку — имя функции обработчика, который был установлен до этого момента.

string set_error_handler ( callback error_handler [, int error_types])

устанавливаем так

set_error_handler («my_error_handler»);
Пользовательская функция, которая будет обрабатывать ошибки, может принимать следующие входные параметры:

— код уровня ошибки
— строковая интерпретация ошибки
— имя файла, в котором произошла ошибка
— строка, в которой произошла ошибка

Следует так же заметить, что эта функция не может обрабатывать ошибки уровней E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING

Это связанно с тем, что ошибки перечисленных уровней происходят до того, как интерпретатор получает информацию о пользовательском обработчике ошибок.

Итак, объявляем нашу функцию

function my_error_handler($code, $msg, $file, $line) {
}

Замечание: каждый более-менее объемный скрипт обычно разделяется на несколько файлов для удобства работы с ним. Как организовывать модульность программы — тема отдельно разговора. Сейчас же, я хочу лишь посоветовать выделять общие настройки в отдельный файл, который будет подключаться в начале программы с помощью инструкции include, либо с помощью директивы auto_prepend_file. В этот файл можно поместит и наш обработчик. Установка обработчика ошибок должна осуществится как можно ближе к началу программы, желательно в самом начале.
Для того чтобы убедится что это действительно работает — создадим новый PHP файл, и попробуем запустить его

Содержимое файла myerrortest.php

<?php

function my_error_handler($code, $msg, $file, $line) {

echo "Произошла ошибка $msg ($code)<br>n";
echo "$file ($line)";
}

set_error_handler('my_error_handler');

if (!file_exists('/home/mysite/news.txt')) {
trigger_error('News file not found');
}

?>

Результат обработки данного файла будет таким:

Произошла ошибка News file not found (1024)
/home/mysite/myerrortest.php (12)
Теперь у нас есть функция, которая получает данные обо всех происходящих ошибках. Подумаем, как мы можем это использовать.

Будем обрабатывать ошибки уровней
E_ERROR
E_WARNING
E_NOTICE
E_USER_ERROR
E_USER_NOTICE

Первые три ошибки в хорошей законченной программе не должны происходить вообще, поэтому о них мы будем только сообщать пользователю выводом текста ошибки на экран. Так можно работать, пока скрипт в состоянии разработки, затем сообщения о них можно либо отключить, либо записывать в log-файл.

Что касается остальных двух — как Вы уже догадались — они могу там пригодиться. Мы сами будем вызывать ошибки этих уровней в случае необходимости. Допустим — ошибки уровня E_USER_ERROR — будем вызывать в случае, когда сообщение об ошибке должно попасть в log-файл и быть отправлено на e-mail администратору (например — ошибка при выполнении SQL запроса, или отсутствии парв доступа к необходимому файлу). Ошибки уровня E_USER_NOTICE будут вызываться при возникновении «лёгких» ошибок (например — пользователь некорректно заполнил форму, или запросил из базы несуществующую запись).

Теперь наша функция обработки ошибок будет выглядеть примерно так:

// Немного предварительных настроек

// устанавливаем режим отображения ошибок
// отображать все ошибки, кроме E_NOTICE
error_reporting  (E_ALL & ~E_NOTICE);

// эта константа отвечает за
// включение/выключение режима отладки
// во время отладки - сообщения не отсылаются
// по почте, а просто печатаются на экран
define('DEBUG', 0);

// это глобальная переменная, в которой
// будет храниться сообщение, которое
// должен видеть пользователь
$MSG = '';

// e-mail разработчика, куда отправлять ошибки
define('ADM_EMAIL','admin@example.com');

// log-файл
define('LOGFILE','/home/mysite/mylog.log');

// разница во времени с сервером (в секундах)
define('TIMEOFFSET', 0);

// сама функция

function my_error_handler($code, $msg, $file, $line)
{
// глобальная переменная, в которую будет
// записываться сообщение об ошибке.
global $MSG;

// пропускаем ошибки уровня E_NOTICE
// и игнорируем ошибки, если режим сообщения об ошибках отключен
if ( ($code == E_NOTICE) or (error_reporting() == 0) ) {
return;
}

// если мы вызвали ошибку уровня E_USER_NOTICE - просто
// записать текст ошибки в глобальную переменную $MSG
// и прекратить выполнение функции

if ($code == E_USER_NOTICE) {
$MSG = $msg;
Return;
}

// если ошибка уровня E_ERROR - печатаем текст ошибки
// и завершаем выполнение скрипта

if ($code == E_ERROR) {
die ('<br><b>ERROR:</b> '.$msg.'<br>In '.$file.' (line '.$line.')<br>');
}

// если ошибка уровня E_WARNING - печатаем текст ошибки
// и прекращаем выполнение функции

if ($code == E_WARNING) {
echo '<br><b>WARNING:</b> '.$msg.'<br>In '.$file.' (line '.$line.')<br>';
Return;
}

// если ошибка уровня E_USER_ERROR

if ($code == E_USER_ERROR) {

// записываем в переменную $MSG текст, о том что произошла ошибка,
// причины сообщать не будем, только сообщим что подробности
// отправлены на e-mail кому следует.

$MSG = 'Критическая Ошибка: действие выполнено небыло. <br>
Сообщение об ошибке было отправлено разработчику.';

// подробности записываем в переменную $text

$text = $msg.'<br>'.'Файл: '.$file.' ('.$line.')';

// Если константа DEBUG установлена в 1 - печатаем информацию об
// ошибке на экран, если нет - отправляем текст ошибки почтой
// функция error_mail() и пишем в log - функция error_writelog()

if (DEBUG == 1) {
error_print($text);
} else {
error_mail($text);
error_writelog($text);
}

Return;
}
}


// устанавливаем обработчик
set_error_handler('my_error_handler');

Теперь описываем служебные функции 


// ф-я печатает ошибку на экран
function error_print($text)
{
echo $text.'<p>';
}

// ф-я отправляет ошибку почтой
function error_mail($text)
{
$text = str_replace("<br>", "n", $text);

$info = 'Время: '.get_datetime()."nRemote IP:".get_ip()."n";

mail(ADM_EMAIL, "Error reporting", $info.$text);
}

// ф-я пишет ошибку в лог
function error_writelog($text)
{
$text = str_replace("<br>", "t", $text);
if (@$fh = fopen(LOGFILE, "a+")) {
fputs($fh, get_datetime()."t".get_ip()."t".$text."n");
fclose($fh);
}
}


// получаем время, с учётом разницы во времени
function get_time()
{
return(date("H:i", time () + TIMEOFFSET));
}

// получаем дату, с учётом разницы во времени
function get_date()
{
return(date("Y-m-d", time () + TIMEOFFSET));
}

// получаем  дату и время, с учётом разницы во времени
function get_datetime()
{
return get_date().' '.get_time();
}

// получаем IP
function get_ip()
{
return($_SERVER['REMOTE_ADDR']);
}
И наконец пример использования 

// ф-я записывает новость в файл
function write_news($title, $text)
{
$news_file = '/home/mysite/news.txt';

// проверяем наличие заголовка - ошибка не критическая
if (!trim($title)) {

// для того чтобы определить что функция завершилась
// неудачей - необходимо вернуть false. Функция
// trigger_error() - возвращает true, мы будем
// возвращать её инвертированный результат

return !trigger_error('Необходимо указать заголовок новости');
}

// проверяем наличие текста новости - ошибка не критическая
if (!trim($text)) {
return !trigger_error('Необходимо указать текст новости');
}

// проверяем наличие файла в который будем писать
// если файл не найден - возникает критическая ошибка

if (!file_exists($news_file)) {
return !trigger_error('Файл базы новостей не найден!', E_USER_ERROR);
}

// ...тут предварительная обработка данных...

// записываем новость
$fh = fopen($news_file, "a+");
fputs($fh, $title."t".$text."n");
fclose($fh);

// если всё нормально - функция возвращает true
return true;
}

// пытаемся записать новость
// эти данные могут приходить из web-формы

$res = write_news("Моя новость", "Текст моей новости");

if ($res === false) {

// если вернулся false - печатаем ошибку
echo $MSG;

} else {

// если всё в порядке - можно сообщить об этом
// а лучше отфорвардить пользователя куда-нибудь.
echo 'Новость была добавлена';
}

Для того чтобы пример заработал — просто скопируйте в PHP-файл три предыдущих блока кода. Не забудьте установить права доступа на log-файл 777 для того чтобы скрипт мог с ним работать, прописать правильные пути и указать свой e-mail. Вы можете включить режим отладки установкой переменной DEBUG в 1.

Это довольно простой пример, тему можно развивать.

Понравилась статья? Поделить с друзьями:
  • Снять ошибку лансер 10
  • Снять ошибку бош серия 6
  • Снять ошибку аирбэг
  • Снять ошибку абс ниссан примера р12
  • Снять ошибку f21 на стиральной машине бош