Как отправить ошибку php

(PHP 4, PHP 5, PHP 7, PHP 8)

error_reportingSets which PHP errors are reported

Description

error_reporting(?int $error_level = null): int

Parameters

error_level

The new error_reporting
level. It takes on either a bitmask, or named constants. Using named
constants is strongly encouraged to ensure compatibility for future
versions. As error levels are added, the range of integers increases,
so older integer-based error levels will not always behave as expected.

The available error level constants and the actual
meanings of these error levels are described in the
predefined constants.

Return Values

Returns the old error_reporting
level or the current level if no error_level parameter is
given.

Changelog

Version Description
8.0.0 error_level is nullable now.

Examples

Example #1 error_reporting() examples


<?php// Turn off all error reporting
error_reporting(0);// Report simple running errors
error_reporting(E_ERROR | E_WARNING | E_PARSE);// Reporting E_NOTICE can be good too (to report uninitialized
// variables or catch variable name misspellings ...)
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);// Report all errors except E_NOTICE
error_reporting(E_ALL & ~E_NOTICE);// Report all PHP errors
error_reporting(E_ALL);// Report all PHP errors
error_reporting(-1);// Same as error_reporting(E_ALL);
ini_set('error_reporting', E_ALL);?>

Notes

Tip

Passing in the value -1 will show every possible error,
even when new levels and constants are added in future PHP versions. The
behavior is equivalent to passing E_ALL constant.

See Also

  • The display_errors directive
  • The html_errors directive
  • The xmlrpc_errors directive
  • ini_set() — Sets the value of a configuration option

info at hephoz dot de

14 years ago


If you just see a blank page instead of an error reporting and you have no server access so you can't edit php configuration files like php.ini try this:

- create a new file in which you include the faulty script:

<?php
error_reporting
(E_ALL);
ini_set("display_errors", 1);
include(
"file_with_errors.php");
?>

- execute this file instead of the faulty script file

now errors of your faulty script should be reported.
this works fine with me. hope it solves your problem as well!


dave at davidhbrown dot us

17 years ago


The example of E_ALL ^ E_NOTICE is a 'bit' confusing for those of us not wholly conversant with bitwise operators.

If you wish to remove notices from the current level, whatever that unknown level might be, use & ~ instead:

<?php
//....
$errorlevel=error_reporting();
error_reporting($errorlevel & ~E_NOTICE);
//...code that generates notices
error_reporting($errorlevel);
//...
?>

^ is the xor (bit flipping) operator and would actually turn notices *on* if they were previously off (in the error level on its left). It works in the example because E_ALL is guaranteed to have the bit for E_NOTICE set, so when ^ flips that bit, it is in fact turned off. & ~ (and not) will always turn off the bits specified by the right-hand parameter, whether or not they were on or off.


jcastromail at yahoo dot es

2 years ago


Under PHP 8.0, error_reporting() does not return 0 when then the code uses a @ character. 

For example

<?php

$a

=$array[20]; // error_reporting() returns 0 in php <8 and 4437 in PHP>=8?>


Fernando Piancastelli

18 years ago


The error_reporting() function won't be effective if your display_errors directive in php.ini is set to "Off", regardless of level reporting you set. I had to set

display_errors = On
error_reporting = ~E_ALL

to keep no error reporting as default, but be able to change error reporting level in my scripts.
I'm using PHP 4.3.9 and Apache 2.0.


ecervetti at orupaca dot fr

14 years ago


It could save two minutes to someone:
E_ALL & ~E_NOTICE  integer value is 6135

kevinson112 at yahoo dot com

5 years ago


I had the problem that if there was an error, php would just give me a blank page.  Any error at all forced a blank page instead of any output whatsoever, even though I made sure that I had error_reporting set to E_ALL, display_errors turned on, etc etc.  But simply running the file in a different directory allowed it to show errors!

Turns out that the error_log file in the one directory was full (2.0 Gb).  I erased the file and now errors are displayed normally.  It might also help to turn error logging off.

https://techysupport.co/norton-tech-support/


luisdev

5 years ago


This article refers to these two reporting levels:

// Report all PHP errors (see changelog)
error_reporting(E_ALL);

// Report all PHP errors
error_reporting(-1);

What is the difference between those two levels?

Please update this article with a clear explanation of the difference and the possible use cases.


lhenry at lhenry dot com

3 years ago


In php7,  what was generally a notice or a deprecated is now a warning : the same level of a mysql error …  unacceptable for me.

I do have dozen of old projects and I surely d'ont want to define every variable which I eventually wrote 20y ago.

So two option: let php7 degrade my expensive SSDs writing Gb/hours or implement smthing like server level monitoring ( with auto_[pre-ap]pend_file in php.ini) and turn off E_WARNING

Custom overriding the level of php errors should be super handy and flexible …


qeremy ! gmail

8 years ago


If you want to see all errors in your local environment, you can set your project URL like "foo.com.local" locally and put that in bootstrap file.

<?php
if (substr($_SERVER['SERVER_NAME'], -6) == '.local') {
   
ini_set('display_errors', 1);
   
ini_set('error_reporting', E_ALL);
   
// or error_reporting(E_ALL);
}
?>


adam at adamhahn dot com

6 years ago


To expand upon the note by chris at ocproducts dot com. If you prepend @ to error_reporting(), the function will always return 0.

<?php
error_reporting
(E_ALL);
var_dump(
   
error_reporting(), // value of E_ALL,
   
@error_reporting() // value is 0
);
?>


keithm at aoeex dot com

12 years ago


Some E_STRICT errors seem to be thrown during the page's compilation process.  This means they cannot be disabled by dynamically altering the error level at run time within that page.

The work-around for this was to rename the file and replace the original with a error_reporting() call and then a require() call.

Ex, rename index.php to index.inc.php, then re-create index.php as:

<?php
error_reporting
(E_ALL & ~(E_STRICT|E_NOTICE));
require(
'index.inc.php');
?>

That allows you to alter the error reporting prior to the file being compiled.

I discovered this recently when I was given code from another development firm that triggered several E_STRICT errors and I wanted to disable E_STRICT on a per-page basis.


chris at ocproducts dot com

6 years ago


The error_reporting() function will return 0 if error suppression is currently active somewhere in the call tree (via the @ operator).

Rash

8 years ago


If you are using the PHP development server, run from the command line via `php -S servername:port`, every single error/notice/warning will be reported in the command line itself, with file name, and line number, and stack trace.

So if you want to keep a log of all the errors even after page reloads (for help in debugging, maybe), running the PHP development server can be useful.


j dot schriver at vindiou dot com

22 years ago


error_reporting() has no effect if you have defined your own error handler with set_error_handler()

[Editor's Note: This is not quite accurate.

E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR and E_COMPILE_WARNING error levels will be handled as per the error_reporting settings.

All other levels of errors will be passed to the custom error handler defined by set_error_handler().

Zeev Suraski suggests that a simple way to use the defined levels of error reporting with your custom error handlers is to add the following line to the top of your error handling function:

if (!($type & error_reporting())) return;

-zak@php.net]


fredrik at demomusic dot nu

17 years ago


Remember that the error_reporting value is an integer, not a string ie "E_ALL & ~E_NOTICE".

This is very useful to remember when setting error_reporting levels in httpd.conf:

Use the table above or:

<?php

ini_set
("error_reporting", E_YOUR_ERROR_LEVEL);

echo
ini_get("error_reporting");

?>



To get the appropriate integer for your error-level. Then use:

php_admin_value error_reporting YOUR_INT

in httpd.conf

I want to share this rather straightforward tip as it is rather annoying for new php users trying to understand why things are not working when the error-level is set to (int) "E_ALL" = 0...

Maybe the PHP-developers should make ie error_reporting("E_ALL"); output a E_NOTICE informative message about the mistake?


rojaro at gmail dot com

12 years ago


To enable error reporting for *ALL* error messages including every error level (including E_STRICT, E_NOTICE etc.), simply use:

<?php error_reporting(-1); ?>


kc8yds at gmail dot com

14 years ago


this is to show all errors for code that may be run on different versions

for php 5 it shows E_ALL^E_STRICT and for other versions just E_ALL

if anyone sees any problems with it please correct this post

<?php

ini_set
('error_reporting', version_compare(PHP_VERSION,5,'>=') && version_compare(PHP_VERSION,6,'<') ?E_ALL^E_STRICT:E_ALL);

?>


vdephily at bluemetrix dot com

18 years ago


Note that E_NOTICE will warn you about uninitialized variables, but assigning a key/value pair counts as initialization, and will not trigger any error :
<?php
error_reporting
(E_ALL);$foo = $bar; //notice : $bar uninitialized$bar['foo'] = 'hello'; // no notice, although $bar itself has never been initialized (with "$bar = array()" for example)$bar = array('foobar' => 'barfoo');
$foo = $bar['foobar'] // ok$foo = $bar['nope'] // notice : no such index
?>

Daz Williams (The Northeast)

14 years ago


Only display php errors to the developer...

<?php

if($_SERVER['REMOTE_ADDR']=="00.00.00.00")

{

 
ini_set('display_errors','On');

}

else

{

 
ini_set('display_errors','Off');

}

?>



Just replace 00.00.00.00 with your ip address.


misplacedme at gmail dot com

13 years ago


I always code with E_ALL set.
After a couple of pages of
<?php
$username
= (isset($_POST['username']) && !empty($_POST['username']))....
?>

I made this function to make things a little bit quicker.  Unset values passed by reference won't trigger a notice.

<?php
function test_ref(&$var,$test_function='',$negate=false) {
   
$stat = true;
    if(!isset(
$var)) $stat = false;
    if (!empty(
$test_function) && function_exists($test_function)){
       
$stat = $test_function($var);
       
$stat = ($negate) ? $stat^1 : $stat;
    }
    elseif(
$test_function == 'empty') {
       
$stat = empty($var);
       
$stat = ($negate) ? $stat^1 : $stat;
    }
    elseif (!
function_exists($test_function)) {
       
$stat = false;
       
trigger_error("$test_function() is not a valid function");
    }
   
$stat = ($stat) ? true : false;
    return
$stat;
}
$a = '';
$b = '15';test_ref($a,'empty',true);  //False
test_ref($a,'is_int');  //False
test_ref($a,'is_numeric');  //False
test_ref($b,'empty',true);  //true
test_ref($b,'is_int');  //False
test_ref($b,'is_numeric');  //false
test_ref($unset,'is_numeric');  //false
test_ref($b,'is_number');  //returns false, with an error.
?>


Alex

16 years ago


error_reporting() may give unexpected results if the @ error suppression directive is used.

<?php
@include 'config.php';
include
'foo.bar';        // non-existent file
?>

config.php
<?php
error_reporting
(0);
?>

will throw an error level E_WARNING in relation to the non-existent file (depending of course on your configuration settings).  If the suppressor is removed, this works as expected.

Alternatively using ini_set('display_errors', 0) in config.php will achieve the same result.  This is contrary to the note above which says that the two instructions are equivalent.


huhiko334 at yandex dot ru

4 years ago


If you get a weird mysql warnings like "Warning: mysql_query() : Your query requires a full tablescan...", don't look for error_reporting settings - it's set in php.ini.
You can turn it off with
ini_set("mysql.trace_mode","Off");
in your script
http://tinymy.link/mctct

teynon1 at gmail dot com

11 years ago


It might be a good idea to include E_COMPILE_ERROR in error_reporting.

If you have a customer error handler that does not output warnings, you may get a white screen of death if a "require" fails.

Example:
<?php
  error_reporting
(E_ERROR | E_WARNING | E_PARSE);

  function

myErrorHandler($errno, $errstr, $errfile, $errline) {
   
// Do something other than output message.
   
return true;
  }
$old_error_handler = set_error_handler("myErrorHandler");

  require

"this file does not exist";
?>

To prevent this, simply include E_COMPILE_ERROR in the error_reporting.

<?php
  error_reporting
(E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
?>


forcemdt

9 years ago


Php >5.4

Creating a Custom Error Handler

set_error_handler("customError",E_ALL);
function customError($errno, $errstr)
  {
  echo "<b>Error:</b> [$errno] $errstr<br>";
  echo "Ending Script";
  die();
  }


DarkGool

17 years ago


In phpinfo() error reporting level display like a bit (such as 4095)

Maybe it is a simply method to understand what a level set on your host

if you are not have access to php.ini file

<?php

$bit
= ini_get('error_reporting');

while (
$bit > 0) {

    for(
$i = 0, $n = 0; $i <= $bit; $i = 1 * pow(2, $n), $n++) {

       
$end = $i;

    }

   
$res[] = $end;

   
$bit = $bit - $end;

}

?>



In $res you will have all constants of error reporting

$res[]=int(16) // E_CORE_ERROR

$res[]=int(8)    // E_NOTICE

...


&IT

3 years ago


error_reporting(E_ALL);
if (!ini_get('display_errors')) {
    ini_set('display_errors', '1');
}

PHP предлагает гибкие настройки вывода ошибок, среди которых функия error_reporting($level) – задает, какие ошибки PHP попадут в отчет, могут быть значения:

  • E_ALL – все ошибки,
  • E_ERROR – критические ошибки,
  • E_WARNING – предупреждения,
  • E_PARSE – ошибки синтаксиса,
  • E_NOTICE – замечания,
  • E_CORE_ERROR – ошибки обработчика,
  • E_CORE_WARNING – предупреждения обработчика,
  • E_COMPILE_ERROR – ошибки компилятора,
  • E_COMPILE_WARNING – предупреждения компилятора,
  • E_USER_ERROR – ошибки пользователей,
  • E_USER_WARNING – предупреждения пользователей,
  • E_USER_NOTICE – уведомления пользователей.

1

Вывод ошибок в браузере

error_reporting(E_ALL);
ini_set('display_errors', 'On'); 

PHP

В htaccess

php_value error_reporting "E_ALL"
php_flag display_errors On

htaccess

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

2

Запись ошибок в лог файл

error_reporting(E_ALL);
ini_set('display_errors', 'Off'); 
ini_set('log_errors', 'On');
ini_set('error_log', $_SERVER['DOCUMENT_ROOT'] . '/logs/php-errors.log');

PHP

Файлы логов также не должны быть доступны из браузера, храните их в закрытой директории с файлом .htaccess:

Order Allow,Deny
Deny from all

htaccess

Или запретить доступ к файлам по расширению .log (заодно и другие системные файлы и исходники):

<FilesMatch ".(htaccess|htpasswd|bak|ini|log|sh|inc|config|psd|fla|ai)$">
Order Allow,Deny
Deny from all
</FilesMatch>

htaccess

3

Отправка ошибок на e-mail

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

Первый – register_shutdown_function() регистрирует функцию, которая выполнится при завершении работы скрипта, error_get_last() получает последнюю ошибку.

register_shutdown_function('error_alert');

function error_alert() 
{ 
	$error = error_get_last();
	if (!empty($error)) {
		mail('mail@example.com', 'Ошибка на сайте example.com', print_r($error, true)); 
	}
}

PHP

Стоит учесть что оператор управления ошибками (знак @) работать в данном случаи не будет и письмо будет отправляться при каждой ошибке.

Второй метод использует «пользовательский обработчик ошибок», поэтому в браузер ошибки выводится не будут.

function error_alert($type, $message, $file, $line, $vars)
{
	$error = array(
		'type'    => $type,
		'message' => $message,
		'file'    => $file,
		'line'    => $line
	);
	error_log(print_r($error, true), 1, 'mail@example.com', 'From: mail@example.com');
}

set_error_handler('error_alert');

PHP

4

Пользовательские ошибки

PHP позволяет разработчику самому объявлять ошибки, которые выведутся в браузере или в логе. Для создания ошибки используется функция trigger_error():

trigger_error('Пользовательская ошибка', E_USER_ERROR);

PHP

Результат:

Fatal error: Пользовательская ошибка in /public_html/script.php on line 2
  • E_USER_ERROR – критическая ошибка,
  • E_USER_WARNING – не критическая,
  • E_USER_NOTICE – сообщения которые не являются ошибками,
  • E_USER_DEPRECATED – сообщения о устаревшем коде.

10.10.2019, обновлено 09.10.2021

Другие публикации

HTTP коды

Список основных кодов состояния HTTP, без WebDAV.

Автоматическое сжатие и оптимизация картинок на сайте

Изображения нужно сжимать для ускорения скорости загрузки сайта, но как это сделать? На многих хостингах нет…

Работа с JSON в PHP

JSON (JavaScript Object Notation) – текстовый формат обмена данными, основанный на JavaScript, который представляет собой набор пар {ключ: значение}. Значение может быть массивом, числом, строкой и…

Пример парсинга html-страницы на phpQuery

phpQuery – это удобный HTML парсер взявший за основу селекторы, фильтры и методы jQuery, которые позволяют…

Примеры отправки AJAX JQuery

AJAX позволяет отправить и получить данные без перезагрузки страницы. Например, делать проверку форм, подгружать контент и т.д. А функции JQuery значительно упрощают работу.

Подключение к платежной системе Сбербанка

После регистрации в системе эквайринга Сбербанка и получив доступ к тестовой среде, можно приступить к интеграции с…

error_reporting

(PHP 4, PHP 5, PHP 7)

error_reporting
Задает, какие ошибки PHP попадут в отчет

Описание

int error_reporting
([ int $level
] )

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

level

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

Доступные константы уровней ошибок и их описания приведены в разделе
Предопределенные константы.

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

Возвращает старое значение уровня
error_reporting либо текущее
значение, если аргумент level не задан.

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

Версия Описание
5.4.0 E_STRICT стал частью
E_ALL.
5.3.0 Добавлены E_DEPRECATED и
E_USER_DEPRECATED.
5.2.0 Добавлена E_RECOVERABLE_ERROR.
5.0.0 Добавлена E_STRICT (не входит в состав
E_ALL).

Примеры

Пример #1 Примеры использования error_reporting()


<?php// Выключение протоколирования ошибок
error_reporting(0);// Включать в отчет простые описания ошибок
error_reporting(E_ERROR E_WARNING E_PARSE);// Включать в отчет E_NOTICE сообщения (добавятся сообщения о 
//непроинициализированных переменных или ошибках в именах переменных)
error_reporting(E_ERROR E_WARNING E_PARSE E_NOTICE);// Добавлять сообщения обо всех ошибках, кроме E_NOTICE
error_reporting(E_ALL & ~E_NOTICE);// Добавлять в отчет все PHP ошибки (см. список изменений)
error_reporting(E_ALL);// Добавлять в отчет все PHP ошибки
error_reporting(-1);// То же, что и error_reporting(E_ALL);
ini_set('error_reporting'E_ALL);?>

Примечания

Внимание

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

Подсказка

Если передать -1, будут отображаться все возможные
ошибки, даже если в новых версиях PHP добавятся уровни или константы. В
версии PHP 5.4. передача константы E_ALL дает
тот же результат.

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

Антон Шевчук // 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
?>

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

Понравилась статья? Поделить с друзьями:
  • Как пишется одна и таже ошибка
  • Как отправить отчет об ошибке android
  • Как пишется нет ни одной ошибки
  • Как отправить отчет ксиоми об ошибке
  • Как отправить на сайт сообщение об ошибке