Обработчик ошибок php

PHP has an exception model similar to that of other programming languages. An exception can be thrown, and caught ("catched") within PHP. Code may be surrounded in a try block, to facilitate the catching of potential exceptions. Each try must have at least one corresponding catch or finally block.

Table of Contents

  • Extending Exceptions

PHP has an exception model similar to that of other programming
languages. An exception can be thrown, and caught («catched») within
PHP. Code may be surrounded in a try block, to facilitate the catching
of potential exceptions. Each try must have at least one corresponding
catch or finally block.

If an exception is thrown and its current function scope has no catch
block, the exception will «bubble up» the call stack to the calling
function until it finds a matching catch block. All finally blocks it encounters
along the way will be executed. If the call stack is unwound all the way to the
global scope without encountering a matching catch block, the program will
terminate with a fatal error unless a global exception handler has been set.

The thrown object must be an instanceof Throwable.
Trying to throw an object that is not will result in a PHP Fatal Error.

As of PHP 8.0.0, the throw keyword is an expression and may be used in any expression
context. In prior versions it was a statement and was required to be on its own line.

catch

A catch block defines how to respond to a thrown exception. A catch
block defines one or more types of exception or error it can handle, and
optionally a variable to which to assign the exception. (The variable was
required prior to PHP 8.0.0.) The first catch block a thrown exception
or error encounters that matches the type of the thrown object will handle
the object.

Multiple catch blocks can be used to catch different classes of
exceptions. Normal execution (when no exception is thrown within the try
block) will continue after that last catch block defined in sequence.
Exceptions can be thrown (or re-thrown) within a catch block. If not,
execution will continue after the catch block that was triggered.

When an exception is thrown, code following the statement will not be
executed, and PHP will attempt to find the first matching catch block.
If an exception is not caught, a PHP Fatal Error will be issued with an
«Uncaught Exception ...» message, unless a handler has
been defined with set_exception_handler().

As of PHP 7.1.0, a catch block may specify multiple exceptions
using the pipe (|) character. This is useful for when
different exceptions from different class hierarchies are handled the
same.

As of PHP 8.0.0, the variable name for a caught exception is optional.
If not specified, the catch block will still execute but will not
have access to the thrown object.

finally

A finally block may also be specified after or
instead of catch blocks. Code within the finally block will always be
executed after the try and catch blocks, regardless of whether an
exception has been thrown, and before normal execution resumes.

One notable interaction is between the finally block and a return statement.
If a return statement is encountered inside either the try or the catch blocks,
the finally block will still be executed. Moreover, the return statement is
evaluated when encountered, but the result will be returned after the finally block
is executed. Additionally, if the finally block also contains a return statement,
the value from the finally block is returned.

Global exception handler

If an exception is allowed to bubble up to the global scope, it may be caught
by a global exception handler if set. The set_exception_handler()
function can set a function that will be called in place of a catch block if no
other block is invoked. The effect is essentially the same as if the entire program
were wrapped in a trycatch block with that function as the catch.

Notes

Note:

Internal PHP functions mainly use
Error reporting, only modern
Object-oriented
extensions use exceptions. However, errors can be easily translated to
exceptions with ErrorException.
This technique only works with non-fatal errors, however.

Example #1 Converting error reporting to exceptions


<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
throw new
ErrorException($message, 0, $severity, $filename, $lineno);
}
set_error_handler('exceptions_error_handler');
?>

Examples

Example #2 Throwing an Exception


<?php
function inverse($x) {
if (!
$x) {
throw new
Exception('Division by zero.');
}
return
1/$x;
}

try {
echo

inverse(5) . "n";
echo
inverse(0) . "n";
} catch (
Exception $e) {
echo
'Caught exception: ', $e->getMessage(), "n";
}
// Continue execution
echo "Hello Worldn";
?>

The above example will output:

0.2
Caught exception: Division by zero.
Hello World

Example #3 Exception handling with a finally block


<?php
function inverse($x) {
if (!
$x) {
throw new
Exception('Division by zero.');
}
return
1/$x;
}

try {
echo

inverse(5) . "n";
} catch (
Exception $e) {
echo
'Caught exception: ', $e->getMessage(), "n";
} finally {
echo
"First finally.n";
}

try {
echo

inverse(0) . "n";
} catch (
Exception $e) {
echo
'Caught exception: ', $e->getMessage(), "n";
} finally {
echo
"Second finally.n";
}
// Continue execution
echo "Hello Worldn";
?>

The above example will output:

0.2
First finally.
Caught exception: Division by zero.
Second finally.
Hello World

Example #4 Interaction between the finally block and return


<?phpfunction test() {
try {
throw new
Exception('foo');
} catch (
Exception $e) {
return
'catch';
} finally {
return
'finally';
}
}

echo

test();
?>

The above example will output:

Example #5 Nested Exception


<?phpclass MyException extends Exception { }

class

Test {
public function
testing() {
try {
try {
throw new
MyException('foo!');
} catch (
MyException $e) {
// rethrow it
throw $e;
}
} catch (
Exception $e) {
var_dump($e->getMessage());
}
}
}
$foo = new Test;
$foo->testing();?>

The above example will output:

Example #6 Multi catch exception handling


<?phpclass MyException extends Exception { }

class

MyOtherException extends Exception { }

class

Test {
public function
testing() {
try {
throw new
MyException();
} catch (
MyException | MyOtherException $e) {
var_dump(get_class($e));
}
}
}
$foo = new Test;
$foo->testing();?>

The above example will output:

Example #7 Omitting the caught variable

Only permitted in PHP 8.0.0 and later.


<?phpclass SpecificException extends Exception {}

function

test() {
throw new
SpecificException('Oopsie');
}

try {

test();
} catch (
SpecificException) {
print
"A SpecificException was thrown, but we don't care about the details.";
}
?>

Example #8 Throw as an expression

Only permitted in PHP 8.0.0 and later.


<?phpclass SpecificException extends Exception {}

function

test() {
do_something_risky() or throw new Exception('It did not work');
}

try {

test();
} catch (
Exception $e) {
print
$e->getMessage();
}
?>

ask at nilpo dot com

13 years ago


If you intend on creating a lot of custom exceptions, you may find this code useful.  I've created an interface and an abstract exception class that ensures that all parts of the built-in Exception class are preserved in child classes.  It also properly pushes all information back to the parent constructor ensuring that nothing is lost.  This allows you to quickly create new exceptions on the fly.  It also overrides the default __toString method with a more thorough one.

<?php
interface IException
{
   
/* Protected methods inherited from Exception class */
   
public function getMessage();                 // Exception message
   
public function getCode();                    // User-defined Exception code
   
public function getFile();                    // Source filename
   
public function getLine();                    // Source line
   
public function getTrace();                   // An array of the backtrace()
   
public function getTraceAsString();           // Formated string of trace

        /* Overrideable methods inherited from Exception class */

public function __toString();                 // formated string for display
   
public function __construct($message = null, $code = 0);
}

abstract class

CustomException extends Exception implements IException
{
    protected
$message = 'Unknown exception';     // Exception message
   
private   $string;                            // Unknown
   
protected $code    = 0;                       // User-defined exception code
   
protected $file;                              // Source filename of exception
   
protected $line;                              // Source line of exception
   
private   $trace;                             // Unknownpublic function __construct($message = null, $code = 0)
    {
        if (!
$message) {
            throw new
$this('Unknown '. get_class($this));
        }
       
parent::__construct($message, $code);
    }

        public function

__toString()
    {
        return
get_class($this) . " '{$this->message}' in {$this->file}({$this->line})n"
                               
. "{$this->getTraceAsString()}";
    }
}
?>

Now you can create new exceptions in one line:

<?php
class TestException extends CustomException {}
?>

Here's a test that shows that all information is properly preserved throughout the backtrace.

<?php
function exceptionTest()
{
    try {
        throw new
TestException();
    }
    catch (
TestException $e) {
        echo
"Caught TestException ('{$e->getMessage()}')n{$e}n";
    }
    catch (
Exception $e) {
        echo
"Caught Exception ('{$e->getMessage()}')n{$e}n";
    }
}

echo

'<pre>' . exceptionTest() . '</pre>';
?>

Here's a sample output:

Caught TestException ('Unknown TestException')
TestException 'Unknown TestException' in C:xampphtdocsCustomExceptionCustomException.php(31)
#0 C:xampphtdocsCustomExceptionExceptionTest.php(19): CustomException->__construct()
#1 C:xampphtdocsCustomExceptionExceptionTest.php(43): exceptionTest()
#2 {main}


Johan

11 years ago


Custom error handling on entire pages can avoid half rendered pages for the users:

<?php
ob_start
();
try {
   
/*contains all page logic
    and throws error if needed*/
   
...
} catch (
Exception $e) {
 
ob_end_clean();
 
displayErrorPage($e->getMessage());
}
?>


christof+php[AT]insypro.com

5 years ago


In case your E_WARNING type of errors aren't catchable with try/catch you can change them to another type of error like this:

<?php
    set_error_handler
(function($errno, $errstr, $errfile, $errline){
            if(
$errno === E_WARNING){
               
// make it more serious than a warning so it can be caught
               
trigger_error($errstr, E_ERROR);
                return
true;
            } else {
               
// fallback to default php error handler
               
return false;
            }
    });

    try {

// code that might result in a E_WARNING
   
} catch(Exception $e){
           
// code to handle the E_WARNING (it's actually changed to E_ERROR at this point)
   
} finally {
           
restore_error_handler();
    }
?>


Shot (Piotr Szotkowski)

14 years ago


‘Normal execution (when no exception is thrown within the try block, *or when a catch matching the thrown exception’s class is not present*) will continue after that last catch block defined in sequence.’

‘If an exception is not caught, a PHP Fatal Error will be issued with an “Uncaught Exception …” message, unless a handler has been defined with set_exception_handler().’

These two sentences seem a bit contradicting about what happens ‘when a catch matching the thrown exception’s class is not present’ (and the second sentence is actually correct).


Simo

7 years ago


#3 is not a good example. inverse("0a") would not be caught since (bool) "0a" returns true, yet 1/"0a" casts the string to integer zero and attempts to perform the calculation.

Edu

9 years ago


The "finally" block can change the exception that has been throw by the catch block.

<?php
try{
        try {
                throw new
Exception("Hello");
        } catch(
Exception $e) {
                echo
$e->getMessage()." catch inn";
                throw
$e;
        } finally {
                echo
$e->getMessage()." finally n";
                throw new
Exception("Bye");
        }
} catch (
Exception $e) {
        echo
$e->getMessage()." catch outn";
}
?>

The output is:

Hello catch in
Hello finally
Bye catch out


daviddlowe dot flimm at gmail dot com

5 years ago


Starting in PHP 7, the classes Exception and Error both implement the Throwable interface. This means, if you want to catch both Error instances and Exception instances, you should catch Throwable objects, like this:

<?phptry {
    throw new
Error( "foobar" );
   
// or:
    // throw new Exception( "foobar" );
}
catch (
Throwable $e) {
   
var_export( $e );
}
?>


mlaopane at gmail dot com

4 years ago


<?php/**
* You can catch exceptions thrown in a deep level function
*/
function employee()
{
    throw new
Exception("I am just an employee !");
}

function

manager()
{
   
employee();
}

function

boss()
{
    try {
       
manager();
    } catch (
Exception $e) {
        echo
$e->getMessage();
    }
}
boss(); // output: "I am just an employee !"

telefoontoestel at nospam dot org

8 years ago


When using finally keep in mind that when a exit/die statement is used in the catch block it will NOT go through the finally block.

<?php
try {
    echo
"try block<br />";
    throw new
Exception("test");
} catch (
Exception $ex) {
    echo
"catch block<br />";
} finally {
    echo
"finally block<br />";
}
// try block
// catch block
// finally block
?>

<?php
try {
    echo
"try block<br />";
    throw new
Exception("test");
} catch (
Exception $ex) {
    echo
"catch block<br />";
    exit(
1);
} finally {
    echo
"finally block<br />";
}
// try block
// catch block
?>


Tom Polomsk

8 years ago


Contrary to the documentation it is possible in PHP 5.5 and higher use only try-finally blocks without any catch block.

Sawsan

11 years ago


the following is an example of a re-thrown exception and the using of getPrevious function:

<?php

$name

= "Name";//check if the name contains only letters, and does not contain the word nametry
   {
   try
     {
      if (
preg_match('/[^a-z]/i', $name))
       {
           throw new
Exception("$name contains character other than a-z A-Z");
       }  
       if(
strpos(strtolower($name), 'name') !== FALSE)
       {
          throw new
Exception("$name contains the word name");
       }
       echo
"The Name is valid";
     }
   catch(
Exception $e)
     {
     throw new
Exception("insert name again",0,$e);
     }
   }

catch (

Exception $e)
   {
   if (
$e->getPrevious())
   {
    echo
"The Previous Exception is: ".$e->getPrevious()->getMessage()."<br/>";
   }
   echo
"The Exception is: ".$e->getMessage()."<br/>";
   }
?>


lscorionjs at gmail dot com

26 days ago


<?phptry {
 
$str = 'hi';
  throw new
Exception();
} catch (
Exception) {
 
var_dump($str);
} finally {
 
var_dump($str);
}
?>

Output:
string(2) "hi"
string(2) "hi"

ilia-yats at ukr dot net

1 month ago


Note some undocumented details about exceptions thrown from 'finally' blocks.

When exception is thrown from 'finally' block, it overrides the original not-caught (or re-thrown) exception. So the behavior is similar to 'return': value returned from 'finally' overrides the one returned earlier. And the original exception is automatically appended to the exceptions chain, i.e. becomes 'previous' for the new one. Example:
<?php
try {
    try {
        throw new
Exception('thrown from try');
    } finally {
        throw new
Exception('thrown from finally');
    }
} catch(
Exception $e) {
    echo
$e->getMessage();
    echo
PHP_EOL;
    echo
$e->getPrevious()->getMessage();
}
// will output:
// thrown from finally
// thrown from try
?>

Example with re-throwing:
<?php
try {
    try {
        throw new
Exception('thrown from try');
    } catch (
Exception $e) {
        throw new
Exception('thrown from catch');
    } finally {
        throw new
Exception('thrown from finally');
    }
} catch(
Exception $e) {
    echo
$e->getMessage();
    echo
PHP_EOL;
    echo
$e->getPrevious()->getMessage();
}
// will output:
// thrown from finally
// thrown from catch
?>

The same happens even if explicitly pass null as previous exception:
<?php
try {
    try {
        throw new
Exception('thrown from try');
    } finally {
        throw new
Exception('thrown from finally', null, null);
    }
} catch(
Exception $e) {
    echo
$e->getMessage();
    echo
PHP_EOL;
    echo
$e->getPrevious()->getMessage();
}
// will output:
// thrown from finally
// thrown from try
?>

Also it is possible to pass previous exception explicitly, the 'original' one will be still appended to the chain, e.g.:
<?php
try {
    try {
        throw new
Exception('thrown from try');
    } finally {
        throw new
Exception(
           
'thrown from finally',
           
null,
            new
Exception('Explicitly set previous!')
        );
    }
} catch(
Exception $e) {
    echo
$e->getMessage();
    echo
PHP_EOL;
    echo
$e->getPrevious()->getMessage();
    echo
PHP_EOL;
    echo
$e->getPrevious()->getPrevious()->getMessage();
}
// will output:
// thrown from finally
// Explicitly set previous!
// thrown from try
?>

This seems to be true for versions 5.6-8.2.


Daan

1 year ago


I would like to emphasise that you can not rethrow an Exception inside a catch-block and expect that the next catch-block will handle it.

<?php try {
    throw new
RuntimeException('error');           
} catch (
RuntimeException $e) {
    throw
$e;
} catch (
Exception $e) {
   
// this will not be executed[
}
?>


(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

9 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

4 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

8 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

9 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__);?>


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 )


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");?>


Anonymous

18 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>!!'

?>


silkensedai at online dot fr

15 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

Anonymous

17 years ago


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

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


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


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.


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.


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


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.


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    

   
}

}

?>


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


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.


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

1 year 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

12 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);
    }
}
?>


Хотя PHP уже давно поддерживает обработку исключений, однако, по сравнению с Java эта поддержка была довольно слабой

Первоначальная поддержка обработки исключений была введена в язык с 5 версии PHP, с двумя простыми встроенными классами исключений — Exception и ErrorException, с поддержкой дополнительных классов через SPL. Идея этого поста состоит в том, чтобы представить читателям современные возможности обработки исключений PHP. 

Новый интерфейс

Хотя PHP 7 предоставляет классы Error и Exception, давайте сначала затронем интерфейс Throwable . И Error и Exception классы реализуют Throwable интерфейс — это основа для любого объекта , который может быть брошен с помощью оператора throw. Единственное, что он не может быть реализован непосредственно в классах пользовательского пространства, только через расширение класса Exception. Кроме того, он обеспечивает единую точку для отлова обоих типов ошибок в одном выражении:

<?php

try {
// ваш код
} catch (Throwable $e) {
echo 'Очень хороший способ отловить исключения и ошибки';
}

Список доступных встроенных классов исключений начиная с PHP 7.4:

  • Exception
  • ErrorException
  • Error
  • ArgumentCountError
  • ArithmeticError
  • AssertionError
  • DivisionByZeroError
  • CompileError
  • ParseError
  • TypeError

Дополнительные классы исключений можно найти в стандартной библиотеке PHP . И наиболее заметным из расширений JSON является класс JsonException.

THROWABLE

Интерфейс Throwable  PHP 7:

interface Throwable
{
public function getMessage(): string; // Error reason
public function getCode(): int; // Error code
public function getFile(): string; // Error begin file
public function getLine(): int; // Error begin line
public function getTrace(): array; // Return stack trace as array like debug_backtrace()
public function getTraceAsString(): string; // Return stack trace as string
public function getPrevious(): Throwable; // Return previous `Trowable`
public function __toString(): string; // Convert into string
}

Вот иерархия Throwable:

interface Throwable
|- Error implements Throwable
|- ArithmeticError extends Error
|- DivisionByZeroError extends ArithmeticError
|- AssertionError extends Error
|- ParseError extends Error
|- TypeError extends Error
|- ArgumentCountError extends TypeError
|- Exception implements Throwable
|- ClosedGeneratorException extends Exception
|- DOMException extends Exception
|- ErrorException extends Exception
|- IntlException extends Exception
|- LogicException extends Exception
|- BadFunctionCallException extends LogicException
|- BadMethodCallException extends BadFunctionCallException
|- DomainException extends LogicException
|- InvalidArgumentException extends LogicException
|- LengthException extends LogicException
|- OutOfRangeException extends LogicException
|- PharException extends Exception
|- ReflectionException extends Exception
|- RuntimeException extends Exception
|- OutOfBoundsException extends RuntimeException
|- OverflowException extends RuntimeException
|- PDOException extends RuntimeException
|- RangeException extends RuntimeException
|- UnderflowException extends RuntimeException
|- UnexpectedValueException extends RuntimeException

Ошибка, почему?

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

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

Вот пример ловли фатальной ошибки в PHP 7.1. Обратите внимание, что нефатальная ошибка не обнаружена.

<?php 

try {
// будет генерировать уведомление, которое не будет поймано
echo $someNotSetVariable;
// фатальная ошибка, которая сейчас на самом деле ловится
someNoneExistentFunction();
} catch (Error $e) {
echo "Error caught: " . $e->getMessage();
}

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

Notice: Undefined variable: someNotSetVariable on line 3
Error caught: Call to undefined function someNoneExistentFunction()

Константы ошибок

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

Вот некоторые из наиболее часто встречающихся кодов ошибок:

  • E_DEPRECATED — интерпретатор сгенерирует этот тип предупреждений, если вы используете устаревшую языковую функцию. Сценарий обязательно продолжит работать без ошибок.
  • E_STRICT — аналогично E_DEPRECATED, — указывает на то, что вы используете языковую функцию, которая не является стандартной в настоящее время и может не работать в будущем. Сценарий будет продолжать работать без каких-либо ошибок.
  • E_PARSE — ваш синтаксис не может быть проанализирован, поэтому ваш скрипт не запустится. Выполнение скрипта даже не запустится.
  • E_NOTICE — движок просто выведет информационное сообщение. Выполнение скрипта не прервется, и ни одна из ошибок не будет выдана.
  • E_ERROR — скрипт не может продолжить работу, и завершится. Выдает ошибки, а как они будут обрабатываться, зависит от обработчика ошибок.
  • E_RECOVERABLE_ERROR — указывает на то, что, возможно, произошла опасная ошибка, и движок работает в нестабильном состоянии. Дальнейшее выполнение зависит от обработчика ошибок, и ошибка обязательно будет выдана.

Полный список констант можно найти в руководстве по PHP.

Функция обработчика ошибок

Функция set_error_handler() используется, чтобы сообщить PHP как обрабатывать стандартные ошибки, которые не являются экземплярами класса исключений Error. Вы не можете использовать функцию обработчика ошибок для фатальных ошибок. Исключения ошибок должны обрабатываться с помощью операторов try/catch. set_error_handler() принимает callback функцию в качестве своего параметра. Callback-функции в PHP могут быть заданы двумя способами: либо строкой, обозначающей имя функции, либо передачей массива, который содержит объект и имя метода (именно в этом порядке). Вы можете указать защищенные и приватные методы для callable в объекте. Вы также можете передать значение null, чтобы указать PHP вернуться к использованию стандартного механизма обработки ошибок. Если ваш обработчик ошибок не завершает программу и возвращает результат, ваш сценарий будет продолжать выполняться со строки, следующей за той, где произошла ошибка.

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

Вот пример:

<?php

function myCustomErrorHandler(int $errNo, string $errMsg, string $file, int $line) {
echo "Ух ты, мой обработчик ошибок получил #[$errNo] в [$file] на [$line]: [$errMsg]";
}

set_error_handler('myCustomErrorHandler');

try {
why;
} catch (Throwable $e) {
echo 'И моя ошибка: ' . $e->getMessage();
}

Если вы запустите этот код в PHP-консоли php -a, вы должны получить похожий вывод:

Error #[2] occurred in [php shell code] at line [3]: [Use of undefined constant why - assumed 'why' (this will throw an Error in a future version of PHP)]

Самые известные PHP библиотеки , которые делают обширное использование РНР set_error_handler() и могут сделать хорошие представления исключений и ошибок являются whoops,  и Symony Debug, ErrorHandler компоненты. Я смело рекомендую использовать один из них. Если не собираетесь их использовать в своем проекте, то вы всегда можете черпать вдохновение из их кода. В то время как компонент Debug широко используется в экосистеме Symfony, Whoops остается библиотекой выбора для фреймворка Laravel . 

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

Отображение или подавление нефатальной ошибки

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

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

Для этого вам нужно настроить PHP, используя следующие параметры в вашем файле php.ini:

  • display_errors – может быть установлен в false для подавления сообщений
  • log_errors – может использоваться для хранения сообщений об ошибках в файлах журнала
  • error_reporting – можно настроить, какие ошибки вызывают отчет

Лучше всего корректно обрабатывать ошибки в вашем приложении. В производственном процессе вы должны скорее регистрировать необработанные ошибки, чем разрешать их отображение пользователю. Функция error_log() может использоваться для отправки сообщения одной из определенных процедур обработки ошибок. Вы также можете использовать функцию error_log() для отправки электронных писем, но лично вы бы предпочли использовать хорошее решение для регистрации ошибок и получения уведомлений при возникновении ошибок, например Sentry или Rollbar .

Существует вещь, называемая оператором контроля ошибок ( @ ), который по своей сути может игнорировать и подавлять ошибки. Использование очень простое — просто добавьте любое выражение PHP с символом «собаки», и сгенерированная ошибка будет проигнорирована. Хотя использование этого оператора может показаться интересным, я призываю вас не делать этого. Мне нравится называть это живым пережитком прошлого.

Больше информации для всех функций, связанных с ошибками PHP, можно найти в руководстве.

Исключения (Exceptions)

Исключения являются основной частью объектно-ориентированного программирования и впервые были представлены в PHP 5.0. Исключением является состояние программы, которое требует специальной обработки, поскольку оно не выполняется ожидаемым образом. Вы можете использовать исключение, чтобы изменить поток вашей программы, например, чтобы прекратить что-либо делать, если некоторые предварительные условия не выполняются.

Исключение будет возникать в стеке вызовов, если вы его не перехватите. Давайте посмотрим на простой пример:

try {
print "это наш блок попыток n";
throw new Exception();
} catch (Exception $e) {
print "что-то пошло не так, есть улов!";
} finally {
print "эта часть всегда выполняется";
}

PHP включает в себя несколько стандартных типов исключений, а стандартная библиотека PHP (SPL) включает в себя еще несколько. Хотя вам не нужно использовать эти исключения, это означает, что вы можете использовать более детальное обнаружение ошибок и отчеты. Классы Exception и Error реализуют интерфейс Throwable и, как и любые другие классы, могут быть расширены. Это позволяет вам создавать гибкие иерархии ошибок и адаптировать обработку исключений. Только класс, который реализует класс Throwable, может использоваться с ключевым словом throw. Другими словами, вы не можете объявить свой собственный базовый класс и затем выбросить его как исключение.

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

Ловля исключений

Вы должны использовать try/catch структуру:

<?php

class MyCustomException extends Exception { }

function throwMyCustomException() {
throw new MyCustomException('Здесь что-то не так.');
}

try {
throwMyCustomException();
} catch (MyCustomException $e) {
echo "Ваше пользовательское исключение поймано";
echo $e->getMessage();
} catch (Exception $e) {
echo "Стандартное исключение PHP";
}

Как видите, есть два предложения catch. Исключения будут сопоставляться с предложениями сверху вниз, пока тип исключения не будет соответствовать предложению catch. Эта очень простая функция throwMyCustomException() генерирует исключение MyCustomException, и мы ожидаем, что оно будет перехвачено в первом блоке. Любые другие исключения, которые произойдут, будут перехвачены вторым блоком. Здесь мы вызываем метод getMessage() из базового класса Exception. Вы можете найти больше информации о дополнительном методе в Exception PHP docs.

Кроме того, можно указать несколько исключений, разделяя их трубой ( | ).

Давайте посмотрим на другой пример:

<?php

class MyCustomException extends Exception { }
class MyAnotherCustomException extends Exception { }

try {
throw new MyAnotherCustomException;
} catch (MyCustomException | MyAnotherCustomException $e) {
echo "Caught : " . get_class($e);
}

Этот очень простой блок catch будет перехватывать исключения типа MyCustomException и MyAnotherCustomException.

Немного более продвинутый сценарий:

// exceptions.php
use SymfonyComponentHttpKernelExceptionNotFoundHttpException;

try {
throw new NotFoundHttpException();
} catch (Exception $e) {
echo 1;
} catch (NotFoundHttpException $e) {
echo 2;
} catch (Exception $e) {
echo 3;
} finally {
echo 4;
}

Это ваш окончательный ответ?

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

<?php

class MyCustomException extends Exception { }

function throwMyCustomException() {
throw new MyCustomException('Здесь что-то не так');
}

try {
throwMyCustomException();
} catch (MyCustomException $e) {
echo "Ваше пользовательское исключение поймано ";
echo $e->getMessage();
} catch (Exception $e) {
echo "Стандартное исключение PHP";
} finally {
echo "Я всегда тут";
}

Вот хороший пример того, как работают операторы PHP catch/finally:

<?php

try {
try {
echo 'a-';
throw new exception();
echo 'b-';
} catch (Exception $e) {
echo 'пойманный-';
throw $e;
} finally {
echo 'завершенный-';
}
} catch (Exception $e) {
echo 'конец-';
}

Функция обработчика исключений

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

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

Функция restore_exception_handler() вернет обработчик исключений к его предыдущему значению.

<?php

class MyCustomException extends Exception { }

function exception_handler($exception) {
echo "Uncaught exception: " , $exception->getMessage(), "n";
}

set_exception_handler('exception_handler');

try {
throw new Exception('Uncaught Exception');
} catch (MyCustomException $e) {
echo "Ваше пользовательское исключение поймано ";
echo $e->getMessage();
} finally {
echo "Я всегда тут";
}

print "Не выполнено";

Здесь простая функция exception_handler будет выполняться после блока finally, когда ни один из типов исключений не был сопоставлен. Последний вывод никогда не будет выполнен.

Для получения дополнительной информации обратитесь к документации PHP. 

Старый добрый «T_PAAMAYIM_NEKUDOTAYIM»

Вероятно, это было самое известное сообщение об ошибке PHP. В последние годы было много споров по этому поводу. Вы можете прочитать больше в отличном сообщении в блоге от Фила Осетра .  

Сегодня я с гордостью могу сказать, что если вы запустите этот код с PHP 7, то сообщение о T_PAAMAYIM_NEKUDOTAYIM больше не будет:

<?php

class foo
{
static $bar = 'baz';
}

var_dump('foo'::$bar);

// Output PHP < 7.0:
// PHP Parse error: syntax error, unexpected '::' (T_PAAMAYIM_NEKUDOTAYIM) in php shell code on line 1
// Output PHP > 7.0:
// string(3) "baz"
?>

В заключении

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

Антон Шевчук // Web-разработчик

Не совершает ошибок только тот, кто ничего не делает, и мы тому пример – трудимся не покладая рук над созданием рабочих мест для тестировщиков :)

О да, в этой статье я поведу свой рассказа об ошибках в PHP, и том как их обуздать.

Ошибки

Разновидности в семействе ошибок

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

Чтобы ни одна ошибка не ушла незамеченной потребуется включить отслеживание всех ошибок с помощью функции error_reporting(), а с помощью директивы display_errors включить их отображение:

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

Фатальные ошибки

Самый грозный вид ошибок – фатальные, они могут возникнуть как при компиляции, так и при работе парсера или PHP-скрипта, выполнение скрипта при этом прерывается.

E_PARSE
Это ошибка появляется, когда вы допускаете грубую ошибку синтаксиса и интерпретатор PHP не понимает, что вы от него хотите, например если не закрыли фигурную или круглую скобочку:

<?php
/**
 Parse error: syntax error, unexpected end of file
 */
{

Или написали на непонятном языке:

<?php
/**
 Parse error: syntax error, unexpected '...' (T_STRING)
 */
Тут будет ошибка парсера

Лишние скобочки тоже встречаются, и не важно круглые либо фигурные:

<?php
/**
 Parse error: syntax error, unexpected '}'
 */
}

Отмечу один важный момент – код файла, в котором вы допустили parse error не будет выполнен, следовательно, если вы попытаетесь включить отображение ошибок в том же файле, где возникла ошибка парсера то это не сработает:

<?php
// этот код не сработает
error_reporting(E_ALL);
ini_set('display_errors', 1);

// т.к. вот тут
ошибка парсера

E_ERROR
Это ошибка появляется, когда PHP понял что вы хотите, но сделать сие не получилось ввиду ряда причин, так же прерывает выполнение скрипта, при этом код до появления ошибки сработает:

Не был найден подключаемый файл:

/**
 Fatal error: require_once(): Failed opening required 'not-exists.php' (include_path='.:/usr/share/php:/usr/share/pear')
 */
require_once 'not-exists.php';

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

/**
 Fatal error: Uncaught exception 'Exception'
 */
throw new Exception();

При попытке вызвать несуществующий метод класса:

/**
 Fatal error: Call to undefined method stdClass::notExists()
 */
$stdClass = new stdClass();
$stdClass->notExists();

Отсутствия свободной памяти (больше, чем прописано в директиве memory_limit) или ещё чего-нить подобного:

/**
 Fatal Error: Allowed Memory Size
 */
$arr = array();

while (true) {
    $arr[] = str_pad(' ', 1024);
}

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

Рекурсивный вызов функции. В данном примере он закончился на 256-ой итерации, ибо так прописано в настройках xdebug:

/**
 Fatal error: Maximum function nesting level of '256' reached, aborting!
 */
function deep() {
    deep();
}
deep();

Не фатальные

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

E_WARNING
Частенько встречается, когда подключаешь файл с использованием include, а его не оказывается на сервере или ошиблись указывая путь к файлу:

/**
 Warning: include_once(): Failed opening 'not-exists.php' for inclusion
 */
include_once 'not-exists.php';

Бывает, если используешь неправильный тип аргументов при вызове функций:

/**
 Warning: join(): Invalid arguments passed
 */
join('string', 'string');

Их очень много, и перечислять все не имеет смысла…

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

Когда обращаются к неопределенной переменной:

/**
 Notice: Undefined variable: a
 */
echo $a;

Когда обращаются к несуществующему элементу массива:

<?php
/**
 Notice: Undefined index: a
 */
$b = array();
$b['a'];

Когда обращаются к несуществующей константе:

/**
 Notice: Use of undefined constant UNKNOWN_CONSTANT - assumed 'UNKNOWN_CONSTANT'
 */
echo UNKNOWN_CONSTANT;

Когда не конвертируют типы данных:

/**
 Notice: Array to string conversion
 */
echo array();

Для избежания подобных ошибок – будьте внимательней, и если вам IDE подсказывает о чём-то – не игнорируйте её:

PHP E_NOTICE in PHPStorm

E_STRICT
Это ошибки, которые научат вас писать код правильно, чтобы не было стыдно, тем более IDE вам эти ошибки сразу показывают. Вот например, если вызвали не статический метод как статику, то код будет работать, но это как-то неправильно, и возможно появление серьёзных ошибок, если в дальнейшем метод класса будет изменён, и появится обращение к $this:

/**
 Strict standards: Non-static method Strict::test() should not be called statically
 */
class Strict { 
    public function test() { 
        echo 'Test'; 
    } 
}

Strict::test();

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

/**
 Deprecated: Function split() is deprecated
 */

// популярная функция, всё никак не удалят из PHP
// deprecated since 5.3
split(',', 'a,b');

В моём редакторе подобные функции будут зачёркнуты:

PHP E_DEPRECATED in PHPStorm

Обрабатываемые

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

  • E_USER_ERROR – критическая ошибка
  • E_USER_WARNING – не критическая ошибка
  • E_USER_NOTICE – сообщения которые не являются ошибками

Отдельно стоит отметить E_USER_DEPRECATED – этот вид всё ещё используется очень часто для того, чтобы напомнить программисту, что метод или функция устарели и пора переписать код без использования оной. Для создания этой и подобных ошибок используется функция trigger_error():

/**
 * @deprecated Deprecated since version 1.2, to be removed in 2.0
 */
function generateToken() {
    trigger_error('Function `generateToken` is deprecated, use class `Token` instead', E_USER_DEPRECATED);
    // ...
    // code ...
    // ...
}

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

  • если display_errors = on, то в случае ошибки браузер получит html c текстом ошибки и кодом 200
  • если же display_errors = off, то для фатальных ошибок код ответа будет 500 и результат не будет возвращён пользователю, для остальных ошибок – код будет работать неправильно, но никому об этом не расскажет

Приручение

Для работы с ошибками в PHP существует 3 функции:

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

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

  • $errno – первый аргумент содержит тип ошибки в виде целого числа
  • $errstr – второй аргумент содержит сообщение об ошибке
  • $errfile – необязательный третий аргумент содержит имя файла, в котором произошла ошибка
  • $errline – необязательный четвертый аргумент содержит номер строки, в которой произошла ошибка
  • $errcontext – необязательный пятый аргумент содержит массив всех переменных, существующих в области видимости, где произошла ошибка

В случае если обработчик вернул true, то ошибка будет считаться обработанной и выполнение скрипта продолжится, иначе — будет вызван стандартный обработчик, который логирует ошибку и в зависимости от её типа продолжит выполнение скрипта или завершит его. Вот пример обработчика:

<?php
    // включаем отображение всех ошибок, кроме E_NOTICE
    error_reporting(E_ALL & ~E_NOTICE);
    ini_set('display_errors', 1);
    
    // наш обработчик ошибок
    function myHandler($level, $message, $file, $line, $context) {
        // в зависимости от типа ошибки формируем заголовок сообщения
        switch ($level) {
            case E_WARNING:
                $type = 'Warning';
                break;
            case E_NOTICE:
                $type = 'Notice';
                break;
            default;
                // это не E_WARNING и не E_NOTICE
                // значит мы прекращаем обработку ошибки
                // далее обработка ложится на сам PHP
                return false;
        }
        // выводим текст ошибки
        echo "<h2>$type: $message</h2>";
        echo "<p><strong>File</strong>: $file:$line</p>";
        echo "<p><strong>Context</strong>: $". join(', $', array_keys($context))."</p>";
        // сообщаем, что мы обработали ошибку, и дальнейшая обработка не требуется
        return true;
    }
    
    // регистрируем наш обработчик, он будет срабатывать на для всех типов ошибок
    set_error_handler('myHandler', E_ALL);

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

С обработчиком, который написан выше есть одна существенная проблема – он не ловит фатальные ошибки, и вместо сайта пользователи увидят лишь пустую страницу, либо, что ещё хуже, сообщение об ошибке. Дабы не допустить подобного сценария следует воспользоваться функцией register_shutdown_function() и с её помощью зарегистрировать функцию, которая всегда будет выполняться по окончанию работы скрипта:

function shutdown() {
    echo 'Этот текст будет всегда отображаться';
}
register_shutdown_function('shutdown');

Данная функция будет срабатывать всегда!

Но вернёмся к ошибкам, для отслеживания появления в коде ошибки воспользуемся функцией error_get_last(), с её помощью можно получить информацию о последней выявленной ошибке, а поскольку фатальные ошибки прерывают выполнение кода, то они всегда будут выполнять роль “последних”:

function shutdown() {
    $error = error_get_last();
    if (
        // если в коде была допущена ошибка
        is_array($error) &&
        // и это одна из фатальных ошибок
        in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])
    ) {
        // очищаем буфер вывода (о нём мы ещё поговорим в последующих статьях)
        while (ob_get_level()) {
            ob_end_clean();
        }
        // выводим описание проблемы
        echo 'Сервер находится на техническом обслуживании, зайдите позже';
    }
}
register_shutdown_function('shutdown');

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

О прожорливости

Проведём простой тест, и выясним – сколько драгоценных ресурсов кушает самая тривиальная ошибка:

/**
 * Этот код не вызывает ошибок
 */

// сохраняем параметры памяти и времени выполнения скрипта
$memory = memory_get_usage();
$time= microtime(true);

$a = '';
$arr = [];
for ($i = 0; $i < 10000; $i++) {
    $arr[$a] = $i;
}

printf('%f seconds <br/>', microtime(true) - $time);
echo number_format(memory_get_usage() - $memory, 0, '.', ' '), ' bytes<br/>';

В результате запуска данного скрипта у меня получился вот такой результат:

0.002867 seconds 
984 bytes

Теперь добавим ошибку в цикле:

/**
 * Этот код содержит ошибку
 */

// сохраняем параметры памяти и времени выполнения скрипта
$memory = memory_get_usage();
$time= microtime(true);

$a = '';
$arr = [];
for ($i = 0; $i < 10000; $i++) {
    $arr[$b] = $i; // тут ошиблись с именем переменной
}

printf('%f seconds <br/>', microtime(true) - $time);
echo number_format(memory_get_usage() - $memory, 0, '.', ' '), ' bytes<br/>';

Результат ожидаемо хуже, и на порядок (даже на два порядка!):

0.263645 seconds 
992 bytes

Вывод однозначен – ошибки в коде приводят к лишней прожорливости скриптов – так что во время разработки и тестирования приложения включайте отображение всех ошибок!

Тестирование проводил на PHP версии 5.6, в седьмой версии результат лучше – 0.0004 секунды против 0.0050 – разница только на один порядок, но в любом случае результат стоит прикладываемых усилий по исправлению ошибок

Где собака зарыта

В PHP есть спец символ «@» – оператор подавления ошибок, его используют дабы не писать обработку ошибок, а положится на корректное поведение PHP в случае чего:

<?php
    echo @UNKNOWN_CONSTANT;

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

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

Исключения

В эру PHP4 не было исключений (exceptions), всё было намного сложнее, и разработчики боролись с ошибками как могли, это было сражение не на жизнь, а на смерть… Окунуться в эту увлекательную историю противостояния можете в статье Исключительный код. Часть 1. Стоит ли её читать сейчас? Думаю да, ведь это поможет вам понять эволюцию языка, и раскроет всю прелесть исключений

Исключения — исключительные событие в PHP, в отличии от ошибок не просто констатируют наличие проблемы, а требуют от программиста дополнительных действий по обработке каждого конкретного случая.

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

Исключение – это объект который наследуется от класса Exception, содержит текст ошибки, статус, а также может содержать ссылку на другое исключение которое стало первопричиной данного. Модель исключений в PHP схожа с используемыми в других языках программирования. Исключение можно инициировать (как говорят, “бросить”) при помощи оператора throw, и можно перехватить (“поймать”) оператором catch. Код генерирующий исключение, должен быть окружен блоком try, для того чтобы можно было перехватить исключение. Каждый блок try должен иметь как минимум один соответствующий ему блок catch или finally:

try {
    // код который может выбросить исключение
    if (rand(0, 1)) {
        throw new Exception('One')
    } else {
        echo 'Zero';
    }
} catch (Exception $e) {
    // код который может обработать исключение
    echo $e->getMessage();
}

В каких случаях стоит применять исключения:

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

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

$directory = __DIR__ . DIRECTORY_SEPARATOR . 'logs';

// директории может не быть
if (!is_dir($directory)) {
    throw new Exception('Directory `logs` is not exists');
}

// может не быть прав на запись в директорию
if (!is_writable($directory)) {
    throw new Exception('Directory `logs` is not writable');
}

// возможно кто-то уже создал файл, и закрыл к нему доступ
if (!$file = @fopen($directory . DIRECTORY_SEPARATOR . date('Y-m-d') . '.log', 'a+')) {
    throw new Exception('System can't create log file');
}

fputs($file, date('[H:i:s]') . " donen");
fclose($file);

Соответственно ловить данные исключения будем примерно так:

try {
    // код который пишет в файл
    // ...
} catch (Exception $e) {
    // выводим текст ошибки
    echo 'Не получилось: '. $e->getMessage();
}

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

// исключения файловой системы
class FileSystemException extends Exception {}

// исключения связанные с директориями
class DirectoryException extends FileSystemException {
    // коды исключений
    const DIRECTORY_NOT_EXISTS =  1;
    const DIRECTORY_NOT_WRITABLE = 2;
}

// исключения связанные с файлами
class FileException extends FileSystemException {}

Теперь, если использовать эти исключения то можно получить следующий код:

try {
    // код который пишет в файл
    if (!is_dir($directory)) {
        throw new DirectoryException('Directory `logs` is not exists', DirectoryException::DIRECTORY_NOT_EXISTS);
    }

    if (!is_writable($directory)) {
        throw new DirectoryException('Directory `logs` is not writable', DirectoryException::DIRECTORY_NOT_WRITABLE);
    }

    if (!$file = @fopen($directory . DIRECTORY_SEPARATOR . date('Y-m-d') . '.log', 'a+')) {
        throw new FileException('System can't open log file');
    }

    fputs($file, date('[H:i:s]'') . " donen");
    fclose($file);
} catch (DirectoryException $e) {
    echo 'С директорией возникла проблема: '. $e->getMessage();
} catch (FileException $e) {
    echo 'С файлом возникла проблема: '. $e->getMessage();
} catch (FileSystemException $e) {
    echo 'Ошибка файловой системы: '. $e->getMessage();
} catch (Exception $e) {
    echo 'Ошибка сервера: '. $e->getMessage();
}

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

Так, а что будет если не поймать исключение? Вы получите “Fatal Error: Uncaught exception …”. Неприятно.
Чтобы избежать подобной ситуации следует использовать функцию set_exception_handler() и установить обработчик для исключений, которые брошены вне блока try-catch и не были обработаны. После вызова такого обработчика выполнение скрипта будет остановлено:

// в качестве обработчика событий 
// будем использовать анонимную функцию
set_exception_handler(function($exception) {
    /** @var Exception $exception */
    echo $exception->getMessage(), "<br/>n";
    echo $exception->getFile(), ':', $exception->getLine(), "<br/>n";
    echo $exception->getTraceAsString(), "<br/>n";
});

Ещё расскажу про конструкцию с использованием блока finally – этот блок будет выполнен вне зависимости от того, было выброшено исключение или нет:

try {
    // код который может выбросить исключение
} catch (Exception $e) {
    // код который может обработать исключение
    // если конечно оно появится
} finally {
    // код, который будет выполнен при любом раскладе
}

Для понимания того, что это нам даёт приведу следующий пример использования блока finally:

try {
    // где-то глубоко внутри кода
    // соединение с базой данных
    $handler = mysqli_connect('localhost', 'root', '', 'test');

    try {
        // при работе с БД возникла исключительная ситуация
        // ...
        throw new Exception('DB error');
    } catch (Exception $e) {
        // исключение поймали, обработали на своём уровне
        // и должны его пробросить вверх, для дальнейшей обработки
        throw new Exception('Catch exception', 0, $e);
    } finally {
        // но, соединение с БД необходимо закрыть
        // будем делать это в блоке finally
        mysqli_close($handler);
    }

    // этот код не будет выполнен, если произойдёт исключение в коде выше
    echo "Ok";
} catch (Exception $e) {
    // ловим исключение, и выводим текст
    echo $e->getMessage();
    echo "<br/>";
    // выводим информацию о первоначальном исключении
    echo $e->getPrevious()->getMessage();
}

Т.е. запомните – блок finally будет выполнен даже в том случае, если вы в блоке catch пробрасываете исключение выше (собственно именно так он и задумывался).

Для вводной статьи информации в самый раз, кто жаждет ещё подробностей, то вы их найдёте в статье Исключительный код ;)

Задание
Написать свой обработчик исключений, с выводом текста файла где произошла ошибка, и всё это с подсветкой синтаксиса, так же не забудьте вывести trace в читаемом виде. Для ориентира – посмотрите как это круто выглядит у whoops.

PHP7 – всё не так, как было раньше

Так, вот вы сейчас всю информацию выше усвоили и теперь я буду грузить вас нововведениями в PHP7, т.е. я буду рассказывать о том, с чем вы столкнётесь через год работы PHP разработчиком. Ранее я вам рассказывал и показывал на примерах какой костыль нужно соорудить, чтобы отлавливать критические ошибки, так вот – в PHP7 это решили исправить, но как обычно завязались на обратную совместимость кода, и получили хоть и универсальное решение, но оно далеко от идеала. А теперь по пунктам об изменениях:

  1. при возникновении фатальных ошибок типа E_ERROR или фатальных ошибок с возможностью обработки E_RECOVERABLE_ERROR PHP выбрасывает исключение
  2. эти исключения не наследуют класс Exception (помните я говорил об обратной совместимости, это всё ради неё)
  3. эти исключения наследуют класс Error
  4. оба класса Exception и Error реализуют интерфейс Throwable
  5. вы не можете реализовать интерфейс Throwable в своём коде

Интерфейс Throwable практически полностью повторяет нам Exception:

interface Throwable
{
    public function getMessage(): string;
    public function getCode(): int;
    public function getFile(): string;
    public function getLine(): int;
    public function getTrace(): array;
    public function getTraceAsString(): string;
    public function getPrevious(): Throwable;
    public function __toString(): string;
}

Сложно? Теперь на примерах, возьмём те, что были выше и слегка модернизируем:

try {
    // файл, который вызывает ошибку парсера
    include 'e_parse_include.php';
} catch (Error $e) {
    var_dump($e);
}

В результате ошибку поймаем и выведем:

object(ParseError)#1 (7) {
  ["message":protected] => string(48) "syntax error, unexpected 'будет' (T_STRING)"
  ["string":"Error":private] => string(0) ""
  ["code":protected] => int(0)
  ["file":protected] => string(49) "/www/education/error/e_parse_include.php"
  ["line":protected] => int(4)
  ["trace":"Error":private] => array(0) { }
  ["previous":"Error":private] => NULL
}

Как видите – поймали исключение ParseError, которое является наследником исключения Error, который реализует интерфейс Throwable, в доме который построил Джек. Ещё есть другие, но не буду мучать – для наглядности приведу иерархию исключений:

interface Throwable
  |- Exception implements Throwable
  |    |- ErrorException extends Exception
  |    |- ... extends Exception
  |    `- ... extends Exception
  `- Error implements Throwable
      |- TypeError extends Error
      |- ParseError extends Error
      |- ArithmeticError extends Error
      |  `- DivisionByZeroError extends ArithmeticError
      `- AssertionError extends Error 

TypeError – для ошибок, когда тип аргументов функции не совпадает с передаваемым типом:

try {
    (function(int $one, int $two) {
        return;
    })('one', 'two');
} catch (TypeError $e) {
    echo $e->getMessage();
}

ArithmeticError – могут возникнуть при математических операциях, к примеру когда результат вычисления превышает лимит выделенный для целого числа:

try {
    1 << -1;
} catch (ArithmeticError $e) {
    echo $e->getMessage();
}

DivisionByZeroError – ошибка деления на ноль:

try {
    1 / 0;
} catch (ArithmeticError $e) {
    echo $e->getMessage();
}

AssertionError – редкий зверь, появляется когда условие заданное в assert() не выполняется:

ini_set('zend.assertions', 1);
ini_set('assert.exception', 1);

try {
    assert(1 === 0);
} catch (AssertionError $e) {
    echo $e->getMessage();
}

При настройках production-серверов, директивы zend.assertions и assert.exception отключают, и это правильно

Задание
Написать универсальный обработчик ошибок для PHP7, который будет отлавливать все возможные исключения.

При написании данного раздела были использованы материалы из статьи Throwable Exceptions and Errors in PHP 7

Отладка

Иногда для отладки кода нужно отследить что происходило с переменной или объектом на определённом этапе, для этих целей есть функция debug_backtrace() и debug_print_backtrace() которые вернут историю вызовов функций/методов в обратном порядке:

<?php
function example() {
    echo '<pre>';
    debug_print_backtrace();
    echo '</pre>';
}

class ExampleClass {
    public static function method () {
        example();
    }
}

ExampleClass::method();

В результате выполнения функции debug_print_backtrace() будет выведен список вызовов приведших нас к данной точке:

#0  example() called at [/www/education/error/backtrace.php:10]
#1  ExampleClass::method() called at [/www/education/error/backtrace.php:14]

Проверить код на наличие синтаксических ошибок можно с помощью функции php_check_syntax() или же команды php -l [путь к файлу], но я не встречал использования оных.

Assert

Отдельно хочу рассказать о таком экзотическом звере как assert() в PHP, собственно это кусочек контрактной методологии программирования, и дальше я расскажу вам как я никогда его не использовал :)

Первый случай – это когда вам надо написать TODO прямо в коде, да так, чтобы точно не забыть реализовать заданный функционал:

// включаем вывод ошибок
error_reporting(E_ALL);
ini_set('display_errors', 1);

// включаем asserts
ini_set('zend.assertions', 1);
ini_set('assert.active', 1);

assert(false, "Remove it!");

В результате выполнения данного кода получим E_WARNING:

Warning: assert(): Remove it! failed

PHP7 можно переключить в режим exception, и вместо ошибки будет всегда появляться исключение AssertionError:

// включаем asserts
ini_set('zend.assertions', 1);
ini_set('assert.active', 1);
// переключаем на исключения
ini_set('assert.exception', 1);

assert(false, "Remove it!");

В результате ожидаемо получаем не пойманный AssertionError. При необходимости, можно выбрасывать произвольное исключение:

assert(false, new Exception("Remove it!"));

Но я бы рекомендовал использовать метки @TODO, современные IDE отлично с ними работают, и вам не нужно будет прикладывать дополнительные усилия и ресурсы для работы с ними

Второй вариант использования – это создание некоего подобия TDD, но помните – это лишь подобие. Хотя, если сильно постараться, то можно получить забавный результат, который поможет в тестировании вашего кода:

// callback-функция для вывода информации в браузер
function backlog($script, $line, $code, $message) {
    echo "<h3>$message</h3>";
    highlight_string ($code);
}

// устанавливаем callback-функцию
assert_options(ASSERT_CALLBACK, 'backlog');
// отключаем вывод предупреждений
assert_options(ASSERT_WARNING,  false);

// пишем проверку и её описание
assert("sqr(4) == 16", "When I send integer, function should return square of it");

// функция, которую проверяем
function sqr($a) {
    return; // она не работает
}

Третий теоретический вариант – это непосредственно контрактное программирование – когда вы описали правила использования своей библиотеки, но хотите точно убедится, что вас поняли правильно, и в случае чего сразу указать разработчику на ошибку (я вот даже не уверен, что правильно его понимаю, но пример кода вполне рабочий):

/**
 * Настройки соединения должны передаваться в следующем виде
 *
 *     [
 *         'host' => 'localhost',
 *         'port' => 3306,
 *         'name' => 'dbname',
 *         'user' => 'root',
 *         'pass' => ''
 *     ]
 *
 * @param $settings
 */
function setupDb ($settings) {
    // проверяем настройки
    assert(isset($settings['host']), 'Db `host` is required');
    assert(isset($settings['port']) && is_int($settings['port']), 'Db `port` is required, should be integer');
    assert(isset($settings['name']), 'Db `name` is required, should be integer');

    // соединяем с БД
    // ...
}

setupDb(['host' => 'localhost']);

Никогда не используйте assert() для проверки входных параметров, ведь фактически assert() интерпретирует строковую переменную (ведёт себя как eval()), а это чревато PHP-инъекцией. И да, это правильное поведение, т.к. просто отключив assert’ы всё что передаётся внутрь будет проигнорировано, а если делать как в примере выше, то код будет выполняться, а внутрь отключенного assert’a будет передан булевый результат выполнения

Если у вас есть живой опыт использования assert() – поделитесь со мной, буду благодарен. И да, вот вам ещё занимательно чтива по этой теме – PHP Assertions, с таким же вопросом в конце :)

В заключение

Я за вас напишу выводы из данной статьи:

  • Ошибкам бой – их не должно быть в вашем коде
  • Используйте исключения – работу с ними нужно правильно организовать и будет счастье
  • Assert – узнали о них, и хорошо

P.S. Спасибо Максиму Слесаренко за помощь в написании статьи

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

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

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

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

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

Обычно, когда возникает проблема, препятствующая правильной работе скрипта, механизм 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
?>

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

PHP5 Обработка ошибок



Обработка ошибок по умолчанию в PHP очень проста. Сообщение об ошибке с именем файла, строка число и сообщение, описывающее ошибку, отправляется в браузер.


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

Учебник содержит несколько из наиболее распространенных методов проверки ошибок в PHP.

Вы узнаете различные методы обработки ошибок:

  • Простое заявление это()
  • Пользовательские ошибки и триггеры ошибок
  • Отчеты об ошибках

PHP Основная обработка ошибок

В первом примере показан простой скрипт, открывающий текстовый файл: использование функции это()

Пример

<?php
$file=fopen(«welcome.txt»,»r»);
?>

Если файл не существует, Вы можете получить ошибку, как эта:

Внимание: fopen(welcome.txt) [function.fopen]: не удалось открыть поток:
Нет такого файла или каталога в C:webfoldertest.php на линии 2

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

Пример

<?php
if(!file_exists(«welcome.txt»)) {
  die(«Файл не найден»);
}
else {
  $file=fopen(«welcome.txt»,»r»);
}
?>

Теперь, если файл не существует вы получите ошибку, как эта:

Файл не найден

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

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


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

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

Эта функция должна быть способна обрабатывать, как минимум два параметра (уровень ошибки и сообщение об ошибке),
но можно принимать до пяти параметров (дополнительно: файл, номер строки и контекст ошибки):

Синтаксис

error_function(error_level,error_message,
error_file,error_line,error_context)

Параметр Описание
error_level Необходимо. Указывает уровень отчета об ошибках для пользовательской ошибки.
Должно быть числовое значение. См. таблицу ниже для возможных уровней отчета об ошибках
error_message Необходимо. Указывает сообщение об ошибке определяемая пользователем
error_file Необязательно. Задает имя файла, в котором произошла ошибка
error_line Необязательно. Указывает номер строки, в которой произошла ошибка
error_context Необязательно. Задает массив, содержащий все переменные и их значения, используемые при возникновении ошибки

PHP Уровни отчетов об ошибках

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

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

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

Пример

function customError($errno, $errstr) {
  echo «<b>Ошибка:</b> [$errno] $errstr<br>»;
  echo «Конечный Script»;
  die();
}

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

Теперь, когда Вы создали функцию обработки ошибок, Вы должны решить, когда она должно сработать.


PHP Установить обработчик ошибок

Обработчик ошибок по умолчанию для PHP является встроенным обработчиком ошибок.
Мы собираемся сделать функцию над обработчиком ошибок по умолчанию на время скрипта.

Можно изменить обработчик ошибок для применения только к некоторым ошибкам, таким образом,
сценарий может обрабатывать различные ошибки по-разному.
Однако, в этом примере мы будем использовать наш пользовательский обработчик ошибок для всех ошибок:

set_error_handler(«customError»);

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

Тестирование обработчика ошибок при попытке вывести несуществующую переменную:

Пример

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

//установить обработчик ошибок
set_error_handler(«customError»);

//Вызов ошибки
echo($test);
?>

Выходные данные приведенного выше кода должны быть примерно такими:

Ошибка: [8] Неопределенна переменная: test


PHP Вызвать ошибку

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

В этом примере возникает ошибка, если $test переменная больше, чем 1:

Пример

<?php
$test=2;
if ($test>=1)
{
 
trigger_error(«Значение должно быть 1 или ниже»);
}
?>

Выходные данные приведенного выше кода должны быть примерно такими:

Заметьте: Значение должно быть 1 или ниже
в C:webfoldertest.php на линии 6

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

Возможные типы ошибок:

  • E_USER_ERROR — Неустранимая пользовательская ошибка выполнения. Ошибки, из которых невозможно восстановить. Выполнение скрипта прекращается
  • E_USER_WARNING — Непоправимое пользовательское предупреждение во время выполнения. Выполнение скрипта не останавливается
  • E_USER_NOTICE — Невыполнение. Уведомление о времени выполнения, созданное пользователем. Сценарий нашел что-то, что могло бы быть ошибкой, но могло бы также произойти при запуске сценария

В этом примере E_USER_WARNING происходит, если переменная $test больше, чем 1. Если происходит E_USER_WARNING мы будем использовать наш пользовательский обработчик ошибок и закончить сценарий:

Пример

<?php
//функция обработчика ошибок
function customError($errno, $errstr) {
  echo «<b>Ошибка:</b> [$errno] $errstr<br>»;
  echo «Закончить Script»;
  die();
}

//установить обработчик ошибок
set_error_handler(«customError»,E_USER_WARNING);

//вызов ошибки
$test=2;
if ($test>=1) {
  trigger_error(«Значение должно быть 1 или ниже»,E_USER_WARNING);
}
?>

Выходные данные приведенного выше кода должны быть примерно такими:

Ошибка: [512] Значение должно быть 1 или ниже
Конец скрипта

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


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

По умолчанию, PHP отправляет отчет об ошибке в систему регистрации на сервер или файл,
в зависимости от того, как конфигурация error_log установлена в php.ini-файл. По
с помощью функции error_log() можно отправлять журнал ошибок в указанный файл или в удаленное место назначения.

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

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

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

Пример

<?php
//функция обработчика ошибок
function customError($errno, $errstr) {
  echo «<b>Ошибка:</b> [$errno] $errstr<br>»;
  echo «Веб-мастер был уведомлен»;
  error_log(«Ошибка: [$errno] $errstr»,1,
  «someone@example.com»,»От: webmaster@example.com»);
}

//установить обработчик ошибок
set_error_handler(«customError»,E_USER_WARNING);

//вызов ошибки
$test=2;
if ($test>=1) {
  trigger_error(«Значение должно быть 1 или ниже»,E_USER_WARNING);
}
?>

Выходные данные приведенного выше кода должны быть примерно такими:

Ошибка: [512] Значение должно быть 1 или ниже
Веб-мастер был уведомлен

И почта, полученная из кода выше, выглядит так:

Ошибка: [512] начение должно быть 1 или ниже

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


Понравилась статья? Поделить с друзьями:
  • Объяснительная записка на работе допустила ошибку
  • Обработчик команд windows не работает как исправить
  • Объяснительная бухгалтера об ошибке пример
  • Обработчик 1c web service extension код ошибки 0x8007007f
  • Объявления пользователя скрыты авито как исправить