Обробка помилок в PHP.
Обробка помилок за допомогою trigger_error () і set_error_handler ()
PHP надає прекрасну можливість контролювати виникають помилки. Тут ми поговоримо про те, як обробити помилку - повідомити (або не повідомити) про подію користувачеві, в разі необхідності - повідомити адміністратору за допомогою електронної пошти, записати інформацію про подію в log-файл.
Отже, для початку давайте визначимося, що таке помилки в PHP.
PHP підтримує такі рівні помилок:
E_ERROR
E_WARNING
E_PARSE
E_NOTICE
E_CORE_ERROR
E_CORE_WARNING
E_COMPILE_ERROR
E_COMPILE_WARNING
E_USER_ERROR
E_USER_WARNING
E_USER_NOTICE
E_ALL
E_STRICT
Насправді - це просто константи, які використовуються для визначення рівня обробки помилок, побудови біт-маски. Константи мають "говорять" імена. Дивлячись на константу - ми можемо сказати, що помилка рівня E_PARSE виникає в разі синтаксичної помилки, E_NOTICE - це нагадування програмісту про порушення "хорошого стилю" програмування на PHP.
Коли з'єднання з базою даних MySQL (або інший) завершується невдачею - інтерпретатор PHP повідомляє про помилку рівня E_WARNING
Warning. mysql_connect (): Access denied for user. 'VVingless @ localhost' (Using password. YES)
In / home / mysite / index. php (line 83)
Зауваження: Для того щоб інтерпретатор PHP повідомляв про помилки - PHP повинен бути налаштований відповідним чином: прапор display_errors потрібно включити - 1, директива error_reporting повинна вказувати на те, що необхідно відображати помилки рівня E_WARNING (бажано звичайно і інші). Якщо значення цих директив не задовольняють вашим вимогам - ви можете спробувати встановити їх самостійно, поклавши в папку зі скриптом файл .htaccess (точка на початку імені обов'язкове) приблизно такого змісту:
php_flag display_errors on
php_value error_reporting "E_ALL
Це означає, що повідомлення про помилки будуть показуватися, причому всіх рівнів, крім E_NOTICE
Коли програміст допускає синтаксичну помилку - інтерпретатор PHP повідомляє про помилку рівня E_PARSE
Parse error: parse error, unexpected '(', expecting T_STRING in /home/mysite/index.php on line 150
Але найцікавіші для нас рівні помилок - E_USER_ERROR і E_USER_WARNING. Як стає зрозуміло з назви - це рівні помилок, які може встановлювати користувач. Для цього існує функція trigger_error () - з її допомогою, Ви можете повідомляти користувачеві про подію так, як це робить PHP.
Як відомо з керівництва по PHP - функція trigger_error () приймає два параметри.
void trigger_error (string error_msg [, int error_type])
Перший параметр - текстове повідомлення про помилку, наприклад "файл не знайдено". Другий параметр - визначає рівень помилки. Функція trigger_error () працює тільки з сімейством помилок E_USER - це значить, що ви можете встановити помилку рівня E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE і не можете встановити помилку рівня E_WARNING. Другий параметр є необов'язковим, і за замовчуванням приймає значення E_USER_NOTICE.
Припустимо, наші дані для стрічки новин зберігаються в файлі news.txt, і якщо файл не знайдений - необхідно повідомити про помилку. Текст програми буде виглядати приблизно так:
if (! file_exists ( '/ home / mysite / news.txt')) trigger_error ( 'News file not found');
>
В результаті інтерпретатор PHP повідомить про помилку рівня E_USER_NOTICE
Notice: News file not found in /home/mysite/index.php on line 47
Але що нам це дає? Для початку те, що якщо в php.ini або файлі .htaccess були встановлені директиви
php_value log_errors "1"
php_value log_errors_max_len "+1024"
php_value error_log "/home/mysite/my.log"
То в файл /home/mysite/my.log автоматично буде додано запис про подію.
Як відомо з мануала - в PHP 4 функція приймає один єдиний строковий параметр - ім'я функції, яка буде виконуватися кожного разу, коли відбувається помилка. PHP 5 дає можливість встановити ще один параметр - тип помилок які будуть оброблятися за допомогою нашого обробника. Функція повертає рядок - ім'я функції обробника, який був встановлений до цього моменту.
string set_error_handler (callback error_handler [, int error_types])
set_error_handler ( "my_error_handler");
Користувацька функція, яка буде обробляти помилки, може приймати наступні вхідні параметри:
- код рівня помилки
- строкова інтерпретація помилки
- ім'я файлу, в якому сталася помилка
- рядок, в якій сталася помилка
Слід так само відзначити, що ця функція не може обробляти помилки рівнів E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING
Це пов'язано з тим, що помилки перерахованих рівнів відбуваються до того, як інтерпретатор отримує інформацію про призначеному для користувача обробнику помилок.
Отже, оголошуємо нашу функцію
function my_error_handler ($ code, $ msg, $ file, $ line)>
Зауваження: кожен більш-менш об'ємний скрипт зазвичай розділяється на кілька файлів для зручності роботи з ним. Як організовувати модульність програми - тема окремо розмови. Зараз же, я хочу лише порадити виділяти загальні налаштування в окремий файл, який буде підключатися на початку програми за допомогою інструкції include, або за допомогою директиви auto_prepend_file. У цей файл можна помістить і наш обробник. Установка обробника помилок повинна здійсниться якомога ближче до початку програми, бажано в самому початку.
Для того щоб переконається що це дійсно працює - створимо новий PHP файл, і спробуємо запустити його
Вміст файлу myerrortest.php
function my_error_handler ($ code. $ msg. $ file. $ line)
echo "Помилка $ msg ($ code)
n ";
echo "$ file ($ line)";
>
if (! file_exists ( '/home/mysite/news.txt')) <
trigger_error ( 'News file not found');
>
Результат обробки даного файлу буде таким:
Сталася помилка News file not found (1024)
/home/mysite/myerrortest.php (12)
Тепер у нас є функція, яка отримує дані про всі відбуваються помилки. Подумаємо, як ми можемо це використовувати.
Будемо обробляти помилки рівнів
E_ERROR
E_WARNING
E_NOTICE
E_USER_ERROR
E_USER_NOTICE
Перші три помилки в хорошій закінченою програмі не повинні відбуватися взагалі, тому про них ми будемо тільки повідомляти користувачеві висновком тексту помилки на екран. Так можна працювати, поки скрипт в стані розробки, потім повідомлення про них можна або відключити, або записувати в log-файл.
Тепер наша функція обробки помилок буде виглядати приблизно так:
// Трохи попередніх налаштувань
// встановлюємо режим відображення помилок
// відображати всі помилки, крім E_NOTICE
error_reporting (E_ALL
// ця константа відповідає за
// включення / вимикання режиму відладки
// під час налагодження - повідомлення не надсилаються
// поштою, а просто друкуються на екран
define ( 'DEBUG'. 0);
// це глобальна змінна, в якій
// буде зберігатися повідомлення, яке
// повинен бачити користувач
$ MSG = '';
// log-файл
define ( 'LOGFILE'. '/home/mysite/mylog.log');
// різниця в часі з сервером (в секундах)
define ( 'TIMEOFFSET'. 0);
function my_error_handler ($ code. $ msg. $ file. $ line)
<
// глобальна змінна, в яку буде
// записуватися повідомлення про помилку.
global $ MSG;
// пропускаємо помилки рівня E_NOTICE
// і ігноруємо помилки, якщо режим повідомлення про помилки відключений
if (($ code == E_NOTICE) or (error_reporting () == 0)) <
return;
>
// якщо ми викликали помилку рівня E_USER_NOTICE - просто
// записати текст помилки в глобальну змінну $ MSG
// і припинити виконання функції
if ($ code == E_USER_NOTICE) <
$ MSG = $ msg;
Return;
>
// якщо помилка рівня E_ERROR - друкуємо текст помилки
// і завершуємо виконання скрипта
if ($ code == E_ERROR) <
die ( '
ERROR: '. $ Msg. '
In '. $ File. '(Line'. $ Line. ')
');
>
// якщо помилка рівня E_WARNING - друкуємо текст помилки
// і припиняємо виконання функції
if ($ code == E_WARNING) <
echo '
WARNING: '. $ Msg. '
In '. $ File. '(Line'. $ Line. ')
';
Return;
>
// якщо помилка рівня E_USER_ERROR
if ($ code == E_USER_ERROR)
$ MSG = 'Критична Помилка: дія виконана не було.
Повідомлення про помилку було відправлено розробнику. ' ;
// подробиці записуємо в змінну $ text
$ Text = $ msg. '
'. 'Файл:'. $ File. '('. $ Line. ')';
// Якщо константа DEBUG встановлена в 1 - друкуємо інформацію про
// помилку на екран, якщо немає - відправляємо текст помилки поштою
// функція error_mail () і пишемо в log - функція error_writelog ()
if (DEBUG == 1) <
error_print ($ text);
> else <
error_mail ($ text);
error_writelog ($ text);
>
// встановлюємо оброблювач
set_error_handler ( 'my_error_handler');
Тепер описуємо службові функції
// ф-я друкує помилку на екран
function error_print ($ text)
<
echo $ text. '
';
>
// ф-я відправляє помилку поштою
function error_mail ($ text)
<
$ Text = str_replace ( "
"." N ". $ Text);
$ Info = 'Час:'. get_datetime (). "NRemote IP:". get_ip (). "N";
mail (ADM_EMAIL. "Error reporting". $ info. $ text);
>
// ф-я пише помилку в лог
function error_writelog ($ text)
<
$ Text = str_replace ( "
"." T ". $ Text);
if (@ $ fh = fopen (LOGFILE. "a +")) <
fputs ($ fh. get_datetime (). "t". get_ip (). "t". $ text. "n");
fclose ($ fh);
>
>
// отримуємо час, з урахуванням різниці в часі
function get_time ()
<
return (date ( "H: i". time () + TIMEOFFSET));
>
// отримуємо дату, з урахуванням різниці в часі
function get_date ()
<
return (date ( "Y-m-d". time () + TIMEOFFSET));
>
// отримуємо дату і час, з урахуванням різниці в часі
function get_datetime ()
<
return get_date (). ''. get_time ();
>
// отримуємо IP
function get_ip ()
<
return ($ _SERVER [ 'REMOTE_ADDR']);
>
І нарешті приклад використання
// ф-я записує новина в файл
function write_news ($ title. $ text)
<
$ News_file = '/home/mysite/news.txt';
// перевіряємо наявність заголовка - помилка не є критичною
if (! trim ($ title))
// для того щоб визначити що функція завершилася
// невдачею - необхідно повернути false. функція
// trigger_error () - повертає true, ми будемо
// повертати її інвертований результат
return. trigger_error ( 'Необхідно вказати заголовок новини');
>
// перевіряємо наявність тексту новини - помилка не є критичною
if (! trim ($ text)) <
return. trigger_error ( 'Необхідно вказати текст новини');
>
// перевіряємо наявність файлу в який будемо писати
// якщо файл не знайдений - виникає критична помилка
if (! file_exists ($ news_file)) <
return. trigger_error ( 'Файл бази новин не знайдено!'. E_USER_ERROR);
>
//. тут попередня обробка даних.
// записуємо новина
$ Fh = fopen ($ news_file. "A +");
fputs ($ fh. $ title. "t". $ text. "n");
fclose ($ fh);
// якщо все нормально - функція повертає true
return true;
>
// намагаємося записати новина
// ці дані можуть приходити з web-форми
$ Res = write_news ( "Моя новина". "Текст моєї новини");
// якщо повернувся false - друкуємо помилку
echo $ MSG;
// якщо все в порядку - можна повідомити про це
// а краще отфорвардіть користувача абикуди.
echo 'Новина була додана';
>
Це досить простий приклад, тему можна розвивати.