<?xml version="1.0" encoding="utf-8"?>
<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink">
<description>
<title-info>
<genre>sf_history</genre>
<author><first-name>Имя автора</first-name><last-name>Фамилия автора</last-name></author>
<book-title>Обработка ошибок с помощью trigger_error() и set_e</book-title>
<date>Дата</date>
<lang>ru</lang>
</title-info>
<document-info>
<author><nickname></nickname></author>
<program-used>Lib converter jcms</program-used>
<id></id>
<version>1.0</version>
</document-info>
</description>
<body><title><p>Обработка ошибок с помощью trigger_error() и set_e</p></title>
<section><p>PHP предоставляет прекрасную возможность контролировать возникающие ошибки. Здесь мы поговорим о том, как обработать ошибку - сообщить (или не сообщить) о происшествии пользователю, в случае необходимости - сообщить администратору с помощью электронной почты, записать информацию о происшествии в log-файл.</p>
<p>Итак, для начала давайте определимся, что такое ошибки в PHP.</p>
<p>PHP поддерживает следующие уровни ошибок:</p>
<p>E_ERROR</p>
<p>E_WARNING</p>
<p>E_PARSE</p>
<p>E_NOTICE</p>
<p>E_CORE_ERROR</p>
<p>E_CORE_WARNING</p>
<p>E_COMPILE_ERROR</p>
<p>E_COMPILE_WARNING</p>
<p>E_USER_ERROR</p>
<p>E_USER_WARNING</p>
<p>E_USER_NOTICE</p>
<p>E_ALL</p>
<p>E_STRICT</p>
<p>На самом деле - это просто константы, которые используются для определения уровня обработки ошибок, построения бит-маски. Константы имеют "говорящие" имена. Глядя на константу - мы можем сказать, что ошибка уровня E_PARSE возникает в случае синтаксической ошибки, E_NOTICE - это напоминание программисту о нарушении "хорошего стиля" программирования на PHP.</p>
<p>Несколько примеров:</p>
<p>Когда соединение с базой данных MySQL (или другой) завершается неудачей - интерпретатор PHP сообщает об ошибке уровня E_WARNING</p>
<p>Warning: mysql_connect(): Access denied for user: 'VVingless@localhost' (Using password: YES)</p>
<p>In /home/mysite/index.php (line 83)</p>
<p>Замечание: Для того чтобы интерпретатор PHP сообщал об ошибках - PHP должен быть настроен соответствующим образом: флаг display_errors должен быть включен - 1, директива error_reporting должна указывать на то, что необходимо отображать ошибки уровня E_WARNING (желательно конечно и другие). Если значения этих директив не удовлетворяют вашим требованиям - вы можете попробовать установить их самостоятельно, положив в папку со скриптом файл .htaccess (точка в начале имени обязательна) примерно такого содержания:</p>
<p>php_flag display_errors on</p>
<p>php_value error_reporting "E_ALL &amp; ~E_NOTICE"</p>
<p>Это означает, что сообщения об ошибках будут показываться, причем всех уровней, кроме E_NOTICE</p>
<p>Когда программист допускает синтаксическую ошибку - интерпретатор PHP сообщает об ошибке уровня E_PARSE</p>
<p>Parse error: parse error, unexpected '(', expecting T_STRING in /home/mysite/index.php on line 150</p>
<p>Но самые интересные для нас уровни ошибок - E_USER_ERROR и E_USER_WARNING. Как становится понятно из названия - это уровни ошибок, которые может устанавливать пользователь. Для этого существует функция trigger_error() - с её помощью, Вы можете сообщать пользователю о происшествии так, как это делает PHP.</p>
<p>Как известно из руководства по PHP - функция trigger_error() принимает два параметра.</p>
<p>void trigger_error ( string error_msg [, int error_type])</p>
<p>Первый параметр - текстовое сообщение об ошибке, например "файл не найден". Второй параметр - определяет уровень ошибки. Функция trigger_error() работает только с семейством ошибок E_USER - это значит, что вы можете установить ошибку уровня E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE и не можете установить ошибку уровня E_WARNING. Второй параметр является не обязательным, и по умолчанию принимает значение E_USER_NOTICE.</p>
<p>Давайте попробуем:</p>
<p>Допустим, наши данные для ленты новостей хранятся в файле news.txt, и если файл не найден - необходимо сообщить об ошибке. Текст программы будет выглядеть примерно так:</p>
<p>if (!file_exists('/home/mysite/news.txt')) {</p>
<p>trigger_error('News file not found');</p>
<p>}</p>
<p>В результате интерпретатор PHP сообщит об ошибке уровня E_USER_NOTICE</p>
<p>Notice: News file not found in /home/mysite/index.php on line 47</p>
<p>Но что нам это даёт? Для начала то, что если в php.ini или файле .htaccess были установлены директивы</p>
<p>php_value log_errors "1"</p>
<p>php_value log_errors_max_len "1024"</p>
<p>php_value error_log "/home/mysite/my.log"</p>
<p>То в файл /home/mysite/my.log автоматически будет добавлена запись о происшествии.</p>
<p>[23-Mar-2004 13:52:03] PHP Notice:  News file not found in /home/mysite/index.php on line 47</p>
<p>Далее, с помощью функции set_error_handler() мы можем установить свой собственный обработчик ошибок возникающих во время выполнения PHP скрипта.</p>
<p>Как известно из мануала - в PHP 4 функция принимает один единственный строковый параметр - имя функции, которая будет выполняться каждый раз, когда происходит ошибка. PHP 5 даёт возможность установить ещё один параметр - тип ошибок которые будут обрабатываться с помощью нашего обработчика. Функция возвращает строку - имя функции обработчика, который был установлен до этого момента.</p>
<p>string set_error_handler ( callback error_handler [, int error_types])</p>
<p>устанавливаем так</p>
<p>set_error_handler ("my_error_handler");</p>
<p>Пользовательская функция, которая будет обрабатывать ошибки, может принимать следующие входные параметры:</p>
<p>- код уровня ошибки</p>
<p>- строковая интерпретация ошибки</p>
<p>- имя файла, в котором произошла ошибка</p>
<p>- строка, в которой произошла ошибка</p>
<p>Следует так же заметить, что эта функция не может обрабатывать ошибки уровней E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING</p>
<p>Это связанно с тем, что ошибки перечисленных уровней происходят до того, как интерпретатор получает информацию о пользовательском обработчике ошибок.</p>
<p>Итак, объявляем нашу функцию</p>
<p>function my_error_handler($code, $msg, $file, $line) {</p>
<p>}</p>
<p>Замечание: каждый более-менее объемный скрипт обычно разделяется на несколько файлов для удобства работы с ним. Как организовывать модульность программы - тема отдельно разговора. Сейчас же, я хочу лишь посоветовать выделять общие настройки в отдельный файл, который будет подключаться в начале программы с помощью инструкции include, либо с помощью директивы auto_prepend_file. В этот файл можно поместит и наш обработчик. Установка обработчика ошибок должна осуществится как можно ближе к началу программы, желательно в самом начале.</p>
<p>Для того чтобы убедится что это действительно работает - создадим новый PHP файл, и попробуем запустить его</p>
<p>Содержимое файла myerrortest.php</p>
<p>Результат обработки данного файла будет таким:</p>
<p>Произошла ошибка News file not found (1024)</p>
<p>/home/mysite/myerrortest.php (12)</p>
<p>Теперь у нас есть функция, которая получает данные обо всех происходящих ошибках. Подумаем, как мы можем это использовать.</p>
<p>Будем обрабатывать ошибки уровней</p>
<p>E_ERROR</p>
<p>E_WARNING</p>
<p>E_NOTICE</p>
<p>E_USER_ERROR</p>
<p>E_USER_NOTICE</p>
<p>Первые три ошибки в хорошей законченной программе не должны происходить вообще, поэтому о них мы будем только сообщать пользователю выводом текста ошибки на экран. Так можно работать, пока скрипт в состоянии разработки, затем сообщения о них можно либо отключить, либо записывать в log-файл.</p>
<p>Что касается остальных двух - как Вы уже догадались - они могу там пригодиться. Мы сами будем вызывать ошибки этих уровней в случае необходимости. Допустим - ошибки уровня E_USER_ERROR - будем вызывать в случае, когда сообщение об ошибке должно попасть в log-файл и быть отправлено на e-mail администратору (например - ошибка при выполнении SQL запроса, или отсутствии парв доступа к необходимому файлу). Ошибки уровня E_USER_NOTICE будут вызываться при возникновении "лёгких" ошибок (например - пользователь некорректно заполнил форму, или запросил из базы несуществующую запись).</p>
<p>Теперь наша функция обработки ошибок будет выглядеть примерно так:</p>
<p>// Немного предварительных настроек</p>
<p>// устанавливаем режим отображения ошибок</p>
<p>// отображать все ошибки, кроме E_NOTICE</p>
<p>error_reporting  (E_ALL &amp; ~E_NOTICE);</p>
<p>// эта константа отвечает за</p>
<p>// включение/выключение режима отладки</p>
<p>// во время отладки - сообщения не отсылаются</p>
<p>// по почте, а просто печатаются на экран</p>
<p>define('DEBUG', 0);</p>
<p>// это глобальная переменная, в которой</p>
<p>// будет храниться сообщение, которое</p>
<p>// должен видеть пользователь</p>
<p>$MSG = '';</p>
<p>// e-mail разработчика, куда отправлять ошибки</p>
<p>define('ADM_EMAIL','admin@example.com');</p>
<p>// log-файл</p>
<p>define('LOGFILE','/home/mysite/mylog.log');</p>
<p>// разница во времени с сервером (в секундах)</p>
<p>define('TIMEOFFSET', 0);</p>
<p>// сама функция</p>
<p>function my_error_handler($code, $msg, $file, $line)</p>
<p>{</p>
<p>// глобальная переменная, в которую будет</p>
<p>// записываться сообщение об ошибке.</p>
<p>global $MSG;</p>
<p>// пропускаем ошибки уровня E_NOTICE</p>
<p>// и игнорируем ошибки, если режим сообщения об ошибках отключен</p>
<p>if ( ($code == E_NOTICE) or (error_reporting() == 0) ) {</p>
<p>return;</p>
<p>}</p>
<p>// если мы вызвали ошибку уровня E_USER_NOTICE - просто</p>
<p>// записать текст ошибки в глобальную переменную $MSG</p>
<p>// и прекратить выполнение функции</p>
<p>if ($code == E_USER_NOTICE) {</p>
<p>$MSG = $msg;</p>
<p>Return;</p>
<p>}</p>
<p>// если ошибка уровня E_ERROR - печатаем текст ошибки</p>
<p>// и завершаем выполнение скрипта</p>
<p>if ($code == E_ERROR) {</p>
<p>die ('</p>
<p>ERROR: '.$msg.'</p>
<p>In '.$file.' (line '.$line.')</p>
<p>');</p>
<p>}</p>
<p>// если ошибка уровня E_WARNING - печатаем текст ошибки</p>
<p>// и прекращаем выполнение функции</p>
<p>if ($code == E_WARNING) {</p>
<p>echo '</p>
<p>WARNING: '.$msg.'</p>
<p>In '.$file.' (line '.$line.')</p>
<p>';</p>
<p>Return;</p>
<p>}</p>
<p>// если ошибка уровня E_USER_ERROR</p>
<p>if ($code == E_USER_ERROR) {</p>
<p>// записываем в переменную $MSG текст, о том что произошла ошибка,</p>
<p>// причины сообщать не будем, только сообщим что подробности</p>
<p>// отправлены	 на e-mail кому следует.</p>
<p>$MSG = 'Критическая Ошибка: действие выполнено небыло.</p>
<p>Сообщение об ошибке было отправлено разработчику.';</p>
<p>// подробности записываем в переменную $text</p>
<p>$text = $msg.'</p>
<p>'.'Файл: '.$file.' ('.$line.')';</p>
<p>// Если константа DEBUG установлена в 1 - печатаем информацию об</p>
<p>// ошибке на экран, если нет - отправляем текст ошибки почтой</p>
<p>// функция error_mail() и пишем в log - функция error_writelog()</p>
<p>if (DEBUG == 1) {</p>
<p>error_print($text);</p>
<p>} else {</p>
<p>error_mail($text);</p>
<p>error_writelog($text);</p>
<p>}</p>
<p>Return;</p>
<p>}</p>
<p>}</p>
<p>// устанавливаем обработчик</p>
<p>set_error_handler('my_error_handler');</p>
<p>Теперь описываем служебные функции</p>
<p>// ф-я печатает ошибку на экран</p>
<p>function error_print($text)</p>
<p>{</p>
<p>echo $text.'';</p>
<p>}</p>
<p>// ф-я отправляет ошибку почтой</p>
<p>function error_mail($text)</p>
<p>{</p>
<p>$text = str_replace("</p>
<p>", "\\n", $text);</p>
<p>$info = 'Время: '.get_datetime()."\\nRemote IP:".get_ip()."\\n";</p>
<p>mail(ADM_EMAIL, "Error reporting", $info.$text);</p>
<p>}</p>
<p>// ф-я пишет ошибку в лог</p>
<p>function error_writelog($text)</p>
<p>{</p>
<p>$text = str_replace("</p>
<p>", "\\t", $text);</p>
<p>if (@$fh = fopen(LOGFILE, "a+")) {</p>
<p>fputs($fh, get_datetime()."\\t".get_ip()."\\t".$text."\\n");</p>
<p>fclose($fh);</p>
<p>}</p>
<p>}</p>
<p>// получаем время, с учётом разницы во времени</p>
<p>function get_time()</p>
<p>{</p>
<p>return(date("H:i", time () + TIMEOFFSET));</p>
<p>}</p>
<p>// получаем дату, с учётом разницы во времени</p>
<p>function get_date()</p>
<p>{</p>
<p>return(date("Y-m-d", time () + TIMEOFFSET));</p>
<p>}</p>
<p>// получаем  дату и время, с учётом разницы во времени</p>
<p>function get_datetime()</p>
<p>{</p>
<p>return get_date().' '.get_time();</p>
<p>}</p>
<p>// получаем IP</p>
<p>function get_ip()</p>
<p>{</p>
<p>return($_SERVER['REMOTE_ADDR']);</p>
<p>}</p>
<p>И наконец пример использования</p>
<p>// ф-я записывает новость в файл</p>
<p>function write_news($title, $text)</p>
<p>{</p>
<p>$news_file = '/home/mysite/news.txt';</p>
<p>// проверяем наличие заголовка - ошибка не критическая</p>
<p>if (!trim($title)) {</p>
<p>// для того чтобы определить что функция завершилась</p>
<p>// неудачей - необходимо вернуть false. Функция</p>
<p>// trigger_error() - возвращает true, мы будем</p>
<p>// возвращать её инвертированный результат</p>
<p>return !trigger_error('Необходимо указать заголовок новости');</p>
<p>}</p>
<p>// проверяем наличие текста новости - ошибка не критическая</p>
<p>if (!trim($text)) {</p>
<p>return !trigger_error('Необходимо указать текст новости');</p>
<p>}</p>
<p>// проверяем наличие файла в который будем писать</p>
<p>// если файл не найден - возникает критическая ошибка</p>
<p>if (!file_exists($news_file)) {</p>
<p>return !trigger_error('Файл базы новостей не найден!', E_USER_ERROR);</p>
<p>}</p>
<p>// ...тут предварительная обработка данных...</p>
<p>// записываем новость</p>
<p>$fh = fopen($news_file, "a+");</p>
<p>fputs($fh, $title."\\t".$text."\\n");</p>
<p>fclose($fh);</p>
<p>// если всё нормально - функция возвращает true</p>
<p>return true;</p>
<p>}</p>
<p>// пытаемся записать новость</p>
<p>// эти данные могут приходить из web-формы</p>
<p>$res = write_news("Моя новость", "Текст моей новости");</p>
<p>if ($res === false) {</p>
<p>// если вернулся false - печатаем ошибку</p>
<p>echo $MSG;</p>
<p>} else {</p>
<p>// если всё в порядке - можно сообщить об этом</p>
<p>// а лучше отфорвардить пользователя куда-нибудь.</p>
<p>echo 'Новость была добавлена';</p>
<p>}</p>
<p>Для того чтобы пример заработал - просто скопируйте в PHP-файл три предыдущих блока кода. Не забудьте установить права доступа на log-файл 777 для того чтобы скрипт мог с ним работать, прописать правильные пути и указать свой e-mail. Вы можете включить режим отладки установкой переменной DEBUG в 1.</p>
</section>
</body>
</FictionBook>