За последние 24 часа нас посетили 46854 программиста и 1672 робота. Сейчас ищут 1023 программиста ...

Обработка ошибок через error_handler и оператор @

Тема в разделе "Прочие вопросы по PHP", создана пользователем Devzirom, 4 фев 2011.

  1. Devzirom

    Devzirom Активный пользователь

    С нами с:
    15 фев 2009
    Сообщения:
    463
    Симпатии:
    0
    Адрес:
    Пермь
    Решил замутить debugger, работает, но вот Error Control Operator @ не хочет скрывать ошибки. Как узнать, был ли использован оператор @?

    Результат работы скрипта

    debugger.sphp
    PHP:
    1. <?php
    2.  
    3. abstract class debugger {
    4.     /**
    5.     * Записывать ошибки в лог
    6.     */
    7.     const LOG_USE = true;
    8.    
    9.     /**
    10.     * Записывать дату, при записи в лог
    11.     */
    12.     const DATE_USE = false;
    13.  
    14.     /**
    15.     * Формат даты, записываемой в лог
    16.     */
    17.     const DATE_TIME = 'Y-m-d H:i:s';
    18.  
    19.     /**
    20.     * Использовать трасировку ошибок
    21.     */
    22.     const TRACING = true;
    23.    
    24.     /**
    25.     * Буффер ошибок
    26.     *
    27.     * @var <string>
    28.     */
    29.     private static $stderr = NULL;
    30.  
    31.     /**
    32.     * Буффер вывода
    33.     *
    34.     * @var <string>
    35.     */
    36.     private static $stdout = NULL;
    37.  
    38.     /**
    39.     * Отладчик инициализирован
    40.     *
    41.     * @var <bool>
    42.     */
    43.     private static $inited = false;
    44.  
    45.     /**
    46.     * Вывод буфферизируется
    47.     *
    48.     * @var <bool>
    49.     */
    50.     private static $output_hooked = false;
    51.  
    52.     /**
    53.     * Возвращает тип ошибки по её коду
    54.     *
    55.     * @param $errno <int> Номер ошибки
    56.     * @return <string> Тип ошибки
    57.     */
    58.     private function error_type($errno) {
    59.         $constants = get_defined_constants(true);
    60.         $matches = array();
    61.         $array = array();
    62.        
    63.         if(isset($constants['Core']))
    64.             $array = $constants['Core'];
    65.         else if(isset($constants['internal']))
    66.             $array = $constants['internal'];
    67.  
    68.         if(!empty($array)) {
    69.             foreach($array AS $key => $value) {
    70.                 if(!preg_match('/^E_(.+)$/', $key, $matches) || $value != $errno)
    71.                     continue;
    72.  
    73.                 return str_replace('_', ' ', 'PHP_'.$matches[1]);
    74.             }
    75.         }
    76.        
    77.         return;
    78.     }
    79.  
    80.     /**
    81.     * Включает/выключает буфферизацию вывода
    82.     *
    83.     * @param $hook <bool> true - включить, false - выключить
    84.     * @return <void> Метод ничего не возвращает
    85.     */
    86.     public function output_hook($hook) {
    87.         if(self::$output_hooked xor $hook) {
    88.             ($hook) ? ob_start(array(__CLASS__, 'output_handler')) : ob_end_clean();
    89.             self::$output_hooked = (bool) $hook;
    90.         }
    91.     }
    92.  
    93.     /**
    94.     * Обработчик вывода.
    95.     * Дописывает строку $buffer в строку self::$stdout
    96.     * и очищает стандартный буффер вывода
    97.     *
    98.     * @param $buffer <string> Часть буффера
    99.     * @return <void> Метод ничего не возвращает
    100.     */
    101.     public function output_handler($buffer) {
    102.         self::$stdout .= $buffer;
    103.  
    104.         return "";
    105.     }
    106.  
    107.     /**
    108.     * Обработчик ошибок.
    109.     * Создаёт строку с описанием ошибки
    110.     *
    111.     * @param $errno <int> Код типа ошибки
    112.     * @param $errstr <string> Описание ошибки
    113.     * @param $errfile <string> Путь до файла, в котором произошла ошибка
    114.     * @param $errline <int> Номер строки, в которой произошла ошибка
    115.     * @return <void> Метод ничего не возвращает
    116.     */
    117.     public function error_handler($errno, $errstr, $errfile, $errline) {
    118.         $errtype = self::error_type($errno);
    119.        
    120.         self::error_message("$errtype: $errstr in $errfile on line $errline");
    121.  
    122.         if(self::TRACING) {
    123.             $backtrace = debug_backtrace();
    124.             array_shift($backtrace);
    125.  
    126.             for($i=0; $i<count($backtrace); $i++) {
    127.                 $err = $backtrace[$i];
    128.                 $args = NULL;
    129.  
    130.                 if(isset($err['args'])) {
    131.                     for($a=0; $a<count($err['args']); $a++) {
    132.                         $args .= ($args) ? ', '.$err['args'][$a] : $err['args'][$a];
    133.                     }
    134.                 }
    135.  
    136.                 $method = isset($err['class']) ? "{$err['class']}{$err['type']}" : NULL;
    137.                 $message = "[$i] {$method}{$err['function']}($args) ";
    138.                 $message .= "in {$err['file']} on line {$err['line']}";
    139.                
    140.                 self::error_message($message);
    141.             }
    142.         }
    143.     }
    144.  
    145.     /**
    146.     * Запись ошибок.
    147.     * Записывает сообщение в лог и в self::$stderr
    148.     *
    149.     * @param $message <string> Сообщение
    150.     * @return <void> Метод ничего не возвращает
    151.     */
    152.     private function error_message($message) {
    153.         self::$stderr .= str_replace($_SERVER['DOCUMENT_ROOT'], '', $message).PHP_EOL;
    154.        
    155.         if(self::LOG_USE) {
    156.             if(self::DATE_USE)
    157.                 $message = '['.date(self::DATE_TIME).'] '.$message;
    158.            
    159.             error_log($message);
    160.         }
    161.     }
    162.    
    163.  
    164.     /**
    165.     * Инициализирует работу этого класса.
    166.     * Устанавливает обработку ошибок через функцию self::error_handler,
    167.     * регистрирует завершение работы скрипта через функцию self::shutdown,
    168.     * включает буфферизацию вывода через функцию self::output_handler,
    169.     * изменяет значение переменной self::$inited на истина
    170.     *
    171.     * @return <void> Метод ничего не возвращает
    172.     */
    173.     public function init() {
    174.         if(!self::$inited) {
    175.             set_error_handler(array(__CLASS__, 'error_handler'), E_ALL);
    176.             register_shutdown_function(array(__CLASS__, 'shutdown'));
    177.             self::output_hook(true);
    178.             self::$inited = true;
    179.         }
    180.     }
    181.  
    182.     /**
    183.     * Обработчик завершения работы скрипта.
    184.     * Выключает буфферизацию вывода через функцию self::output_handler,
    185.     * выводит содержимое self::$stdout.
    186.     * Если во время работы скрипта произошли ошибки,
    187.     * отправляет клиенту заголовок статуса "HTTP/1.0 500 Internal Server Error".
    188.     * Если включен вывод ошибок (display_errors),
    189.     * клиент получает полную информацию о ошибках
    190.     *
    191.     * @return <void> Метод ничего не возвращает
    192.     */
    193.     public function shutdown() {
    194.         self::output_hook(false);
    195.        
    196.         if(!empty(self::$stderr)) {
    197.             $display = @ini_get('display_errors');
    198.             $message = ($display != 'stderr' && $display != false) ? self::$stderr : NULL;
    199.            
    200.             header($_SERVER['SERVER_PROTOCOL'].' 500 Internal Server Error');
    201.             header('Content-Type: text/html; charset=utf-8');
    202.  
    203.             echo "<h1>500 Internal Server Error</h1>";
    204.             echo "<pre>$message</pre>";
    205.         } else
    206.             echo self::$stdout;
    207.     }
    208. }
    209.  
    210. ?>
    run.sphp
    PHP:
    1. <?php
    2. ini_set('display_errors', 'stdout');
    3. header('Content-Type: text/plain; charset=utf-8');
    4.  
    5. require_once('debugger.sphp');
    6.  
    7. debugger::init();
    8.  
    9. @file_get_contents('notfound_file.php'); // Несуществующий файл
    10. ?>
     
  2. Devzirom

    Devzirom Активный пользователь

    С нами с:
    15 фев 2009
    Сообщения:
    463
    Симпатии:
    0
    Адрес:
    Пермь
    Как всегда, разобрался сам

    нужно было добавить в error_handler:

    Код (Text):
    1. if(error_reporting() == 0)
    2.     return false;
    т.к. error_reporting() изменяется на 0, в том местом где стоит @

    Что собственно и сделал:
    PHP:
    1. <?php
    2. ...
    3.     public function error_handler($errno, $errstr, $errfile, $errline) {
    4.         if((error_reporting() & $errno) == 0)
    5.             return false;
    6. ...
    7. ?>
     
  3. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    прикольно, а зачем это?
     
  4. Devzirom

    Devzirom Активный пользователь

    С нами с:
    15 фев 2009
    Сообщения:
    463
    Симпатии:
    0
    Адрес:
    Пермь
    Ну например
    1) в стандартном error_handler нет трассировки и это сильно затрудняет отладку, уже запущенного в свет, проекта.
    2) клиент должен знать, что произошла ошибка, а не просто получать неполную страницу.
    3) к скрипту можно прикрутить функцию отправки письма на e-mail разработчика, в содержимое можно включить не только трассировку, но и значения $_SERVER, $_POST, $_GET, $_SESSION
     
  5. Апельсин

    Апельсин Активный пользователь

    С нами с:
    20 мар 2010
    Сообщения:
    3.645
    Симпатии:
    2
    [​IMG]
     
  6. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    Апельсин
    Кстати, верно. Ошибка в бизнес-логике обычно приводит к лавине ошибок, которая заканчивается каким-нибудь фаталом.
    А если посещаемость хотя-бы 1000 хитов в сутки...
     
  7. Devzirom

    Devzirom Активный пользователь

    С нами с:
    15 фев 2009
    Сообщения:
    463
    Симпатии:
    0
    Адрес:
    Пермь
    Ясно. Видимо для этой цели придется отдельный скрипт писать

    Скажите, что нибудь по коду.

    Пока мне ясно, что это лучше в отдельный инклуд вынести:
    PHP:
    1. <?php
    2. ...
    3.             header($_SERVER['SERVER_PROTOCOL'].' 500 Internal Server Error');
    4.             header('Content-Type: text/html; charset=utf-8');
    5.  
    6.             echo "<h1>500 Internal Server Error</h1>";
    7.             echo "<pre>$message</pre>";
    8. ...
    9. ?>
    10.