Главная arrow Программирование arrow HTML arrow PHP: перехват ошибок Saturday, August 19 2017  
ГлавнаяКонтактыАдминистрированиеПрограммированиеСсылки
UK-flag-ico.png English Version
GERMAN-flag-ico.png Die deutsche Version
map.gif карта сайта
нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

Поделиться:

PHP: перехват ошибок Версия для печати
Написал microsin   
25.07.2006
На серьезных сайтах странно видеть, когда ошибки выводятся пользователю в браузер в самых неожиданных местах. Почему они появляются - это отдельный разговор. Но почему они выводятся? Ведь текст ошибок является информацией для дебага и предназначена для разработчика, а не для клиента. Кроме того, именно эта служебная информация обычно помогает злым хакерам ломать сайт. В качестве классического примера можно привести вариант с выводом запроса при ошибке:
"you have an error in query near WHERE id= "...
Большое спасибо. Подставляем после "WHERE id=..." строку "0 OR 1>0" и запрос выполняется по всей таблице. Если запрос на удаление, то...сами понимаете, весело =). Поэтому я всегда переменные в запросах заключаю в кавычки. На всякий случай...

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

Начнем, пожалуй, с краткого обзора видов ошибок в РНР.

Таблица 1. Описания ошибок в PHP4

Числ.
знач.

Константа

Описание

Ловится
/нет

1

E_ERROR

Фатальные ошибки. Например, ошибка при обращении к памяти. Выполнение скрипта при этом прерывается.

нет

2

E_WARNING

Предупреждения (не фатальные ошибки). Выполнение скрипта не прерывается.

да

4

E_PARSE

Ошибки во время анализа синтаксиса. Генерируются парсером.

нет

8

E_NOTICE

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

да

16

E_CORE_ERROR

Ошибки во время загрузки РНР. Аналог E_ERROR, генерируется ядром РНР.

нет

32

E_CORE_WARNING

Предупреждения во время загрузки РНР Аналог E_WARNING, генерируется ядром РНР.

нет

64

E_COMPILE_ERROR

Фатальные ошибки во время компиляции кода. Аналог E_ERROR, генерируется зендовским движком.

нет

128

E_COMPILE_WARNING

Предупреждения во время компиляции кода. Аналог E_WARNING, генерируется зендовским движком.

нет

256

E_USER_ERROR

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

да

512

E_USER_WARNING

Пользовательское предупреждение.

да

1024

E_USER_NOTICE

Пользовательское замечание

да






























Нас интересуют те ошибки, которые мы можем перехватить. К ним относятся: E_WARNING, E_NOTICE и E_USER_*. Остальные виды ошибок перехвату не поддаются либо из-за того, что происходят они еще до окончания загрузки самого ядра РНР, либо из-за того, что происходят на этапе синтаксического анализа и компилирования РНР-кода, поэтому их вывод придется просто отключить:
ini_set('display_errors',0);

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

По умолчанию уровень ошибок в РНР имеет значение E_ALL & ~E_NOTICE (или 2039 в числовой форме), что означает, что мы пропускаем мимо ушей замечания, но сообщаем о всех остальных ошибках.
Кстати, сами разработчики рекомендуют включать на стадии разработки и E_NOTICE - помогает обнаружить потенциально опасные места. Поэтому изменим уровень вывода ошибок на E_ALL:
error_reporting(E_ALL);

Теперь переопределим хэндлер ошибок и подставим вместо него нашу функцию user_log(), которая и будет заниматься теперь обработкой ошибок:
set_error_handler('user_log');

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

Итак, код с комментариями:
<?php
/* Наша функция-хэндлер */
function user_log ($errno, $errmsg, $file, $line) {
   // время события
   $timestamp = time();

   //формируем новую строку в логе
   $err_str = $timestamp.'||';
   $err_str .= $errno.'||';
   $err_str .= $file.'||';   
   $err_str .= $line.'||';
   $err_str .= $errmsg."\n";

   //проверка на максимальный размер
   if (is_file(LOG_FILE_NAME) AND filesize(LOG_FILE_NAME)>=(LOG_FILE_MAXSIZE*1024)) {
       //проверяем настройки, если установлен лог_ротэйт,
       //то "сдвигаем" старые файлы на один вниз и создаем пустой лог
       //если нет - чистим и пишем вместо старого лога
       if (LOG_ROTATE===true) {
           $i=1;
           //считаем старые логи в каталоге
           while (is_file(LOG_FILE_NAME.'.'.$i)) { $i++; }
           $i--;
           //у каждого из них по очереди увеличиваем номер на 1
           while ($i>0) {
               rename(LOG_FILE_NAME.'..'.$i,LOG_FILE_NAME. '.' .(1+$i--));
           }
           rename (LOG_FILE_NAME,LOG_FILE_NAME.'.1');
           touch(LOG_FILE_NAME);
       }
       elseif(is_file(LOG_FILE_NAME)) {
           //если пишем логи сверху, то удалим
           //и создадим заново пустой файл
           unlink(LOG_FILE_NAME);
           touch(LOG_FILE_NAME);
       }
   }

   /*
   проверяем есть ли такой файл
   если нет - можем ли мы его создать
   если есть - можем ли мы писать в него
   */
   if(!is_file(LOG_FILE_NAME)) {
       if (!touch(LOG_FILE_NAME)) {
           return 'can\'t create log file';
       }
   }
   elseif(!is_writable(LOG_FILE_NAME)) {
       return 'can\'t write to log file';
   }
  
   //обратите внимание на функцию, которой мы пишем лог.
   error_log($err_str, 3, LOG_FILE_NAME);
}
?>

Можно было бы, конечно, использовать более логичное для таких целей хранилище - базу, но ведь ошибки, в большинстве своем, возникают именно при работе с базой, поэтому я бы на нее не полагался.
Собственно, это все. Остальное, я думаю, не составит для вас труда, особенно, если пользоваться функциями file(); & explode();.

Еще разные мысли на эту тему:
- при устаревании лога gz'иповать файл и складывать его в архив;
- то же, но с посылкой на почту;
- при возникновении критических ошибок - слать мэйл (см. пример из мануала по функции set_error_handler);
- для мазохистов можно использовать при этом XML;

Еще способ - просто в php.ini указать error_log = "log_file.log". Достоинство - простота, недостаток - Файл пишется не в нашем формате. Нельзя делать что угодно с этими ошибками. В случае с error_log = "имя_файла" - ошибки ТОЛЬКО пишутся в файл и ничего более. Да и не везде вас пустят к php.ini.

автор: Довгаль Антон
источник: http://detail.phpclub.net/article/2002-10-03

См. также "PHP: скрытие от пользователя ошибок и предупреждений".

Последнее обновление ( 19.01.2008 )
 

Добавить комментарий

:D:lol::-);-)8):-|:-*:oops::sad::cry::o:-?:-x:eek::zzz:P:roll::sigh:

Защитный код
Обновить

< Пред.   След. >

Top of Page
 
microsin © 2017