Ошибки PHP: классификация, примеры, обработка

В статье представлена очередная попытка разобраться с ошибками, которые могут встретиться на вашем пути php-разработчика, их возможная классификация, примеры их возникновения, влияние ошибок на ответ клиенту, а также инструкции по написанию своего обработчика ошибок.

Статья разбита на четыре раздела:
  1. Классификация ошибок.
  2. Пример, демонстрирующий различные виды ошибок и его поведение при различных настройках.
  3. Написание собственного обработчика ошибок.
  4. Полезные ссылки.

Классификация ошибок


Все ошибки, условно, можно разбить на категории по нескольким критериям.
Фатальность:
  • Фатальные
    Неустранимые ошибки. Работа скрипта прекращается.
    E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR.
  • Не фатальные
    Устранимые ошибки. Работа скрипта не прекращается.
    E_WARNING, E_NOTICE, E_CORE_WARNING, E_COMPILE_WARNING, E_USER_WARNING, E_USER_NOTICE, E_STRICT, E_DEPRECATED, E_USER_DEPRECATED.
  • Смешанные
    Фатальные, но только, если не обработаны функцией, определенной пользователем в set_error_handler().
    E_USER_ERROR, E_RECOVERABLE_ERROR.

Возможность перехвата ошибки функцией, определенной в set_error_handler():
  • Перехватываемые (не фатальные и смешанные)
    E_USER_ERROR, E_RECOVERABLE_ERROR, E_WARNING, E_NOTICE, E_USER_WARNING, E_USER_NOTICE, E_STRICT, E_DEPRECATED, E_USER_DEPRECATED.
  • Не перехватываемые (фатальные)
    E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING.

Инициатор:
  • Инициированы пользователем
    E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE.
  • Инициированы PHP
    E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_WARNING, E_NOTICE, E_CORE_WARNING, E_COMPILE_WARNING, E_STRICT, E_DEPRECATED, E_USER_DEPRECATED, E_USER_ERROR, E_RECOVERABLE_ERROR.

Для нас, в рамках данной статьи, наиболее интересны классификации по первым двум критериям, о чем будет рассказано далее.

Примеры возникновения ошибок


Листинг index.php
<?php
// определеяем уровень протоколирования ошибок
error_reporting(E_ALL | E_STRICT);
// определяем режим вывода ошибок
ini_set('display_errors', 'On');
// подключаем файл с ошибками
require 'errors.php';

Листинг errors.php
<?php
echo "Файл с ошибками. Начало<br>";
/*
 * перехватываемые ошибки (ловятся функцией set_error_handler())
 */
// NONFATAL - E_NOTICE
// echo $undefined_var;
// NONFATAL - E_WARNING
// array_key_exists('key', NULL);
// NONFATAL - E_DEPRECATED
split('[/.-]', "12/21/2012"); // split() deprecated начиная с php 5.3.0
// NONFATAL - E_STRICT
// class c {function f(){}} c::f();
// NONFATAL - E_USER_DEPRECATED
// trigger_error("E_USER_DEPRECATED", E_USER_DEPRECATED);
// NONFATAL - E_USER_WARNING
// trigger_error("E_USER_WARNING", E_USER_WARNING);
// NONFATAL - E_USER_NOTICE
// trigger_error("E_USER_NOTICE", E_USER_NOTICE);

// FATAL, если не обработана функцией set_error_handler - E_RECOVERABLE_ERROR
// class b {function f(int $a){}} $b = new b; $b->f(NULL);
// FATAL, если не обработана функцией set_error_handler - E_USER_ERROR
// trigger_error("E_USER_ERROR", E_USER_ERROR);

/*
 * неперехватываемые (не ловятся функцией set_error_handler())
 */
// FATAL - E_ERROR
// undefined_function();
// FATAL - E_PARSE
// parse_error
// FATAL - E_COMPILE_ERROR
// $var[];

echo "Файл с ошибками. Конец<br>";

Примечание: для полной работоспособности скрипта необходим PHP версии не ниже 5.3.0.

В файле errors.php представлены выражения, инициирующие практически все возможные ошибки. Исключение составили: E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_WARNING, генерируемые ядром Zend. В теории, встретить их в реальной работе вы не должны.
В следующей таблице приведены варианты поведения этого скрипта в различных условиях (в зависимости от значений директив display_errors и error_reporting):
Группа ошибок Значения директив* Статус ответа сервера Ответ клиенту**
E_PARSE, E_COMPILE_ERROR*** display_errors = off
error_reporting = ANY
500 Пустое значение
display_errors = on
error_reporting = ANY
200 Сообщение об ошибке
E_USER_ERROR, E_ERROR, E_RECOVERABLE_ERROR display_errors = off
error_reporting = ANY
500 Вывод скрипта до ошибки
display_errors = on
error_reporting = ANY
200 Сообщение об ошибке и вывод скрипта до ошибки
Не фатальные ошибки display_errors = off
error_reporting = ANY
и
display_errors = on
error_reporting = 0
200 Весь вывод скрипта
display_errors = on
error_reporting = E_ALL | E_STRICT
200 Сообщение об ошибке и весь вывод скрипта
* Значение ANY означает E_ALL | E_STRICT или 0.
** Ответ клиенту может отличаться от ответов на реальных скриптах. Например, вывод какой-либо информации до включения файла errors.php, будет фигурировать во всех рассмотренных случаях.
*** Если в файле errors.php заменить пример для ошибки E_COMPILE_ERROR на require "missing_file.php";, то ошибка попадет во вторую группу.

Значение, приведенной выше, таблицы можно описать следующим образом:
  1. Наличие в файле скрипта ошибки, приводящей его в «негодное» состояние (невозможность корректно обработать), на выходе даст пустое значение или же только само сообщение об ошибке, в зависимости от значения директивы display_errors.
  2. Скрипт в файле с фатальной ошибкой, не относящейся к первому пункту, будет выполняться в штатном режиме до самой ошибки.
  3. Наличие в файле фатальной ошибки при display_errors = Off обозначит 500 статус ответа.
  4. Не фатальные ошибки, как и следовало ожидать, в контексте возможности исполнения скрипта в целом, на работоспособность не повлияют.

Собственный обработчик ошибок


Для написания собственного обработчика ошибок необходимо знать, что:
  • для получения информации о последней произошедшей ошибке существует функция error_get_last();
  • для определения собственного обработчика ошибок существует функция set_error_handler(), но фатальные ошибки нельзя «перехватить» этой функцией;
  • используя register_shutdown_function(), можно зарегистрировать свою функцию, выполняемую по завершении работы скрипта, и в ней, используя знания из первого пункта, если фатальная ошибка имела место быть, предпринять необходимые действия;
  • сообщение о фатальной ошибке в любом случае попадет в буфер вывода;
  • воспользовавшись функциями контроля вывода можно предотвратить отображение нежелательной информации;
  • при использовании оператора управления ошибками (знак @) функция, определенная в set_error_handler() все равно будет вызвана, но функция error_reporting() в этом случае вернет 0, чем и можно пользоваться для прекращения работы или определения другого поведения своего обработчика ошибок.

Третий пункт поясню: зарегистрированная нами функция при помощи register_shutdown_function() выполнится в любом случае — корректно ли завершился скрипт, либо же был прерван в связи с критичной (фатальной) ошибкой. Второй вариант мы можем однозначно определить, воспользовавшись информацией предоставленной функцией error_get_last(), и, если ошибка все же была, выполнить наш собственный обработчик ошибок.
Продемонстрируем вышесказанное на модифицированном скрипте index.php:
<?php
/**
 * Обработчик ошибок
 * @param int $errno уровень ошибки
 * @param string $errstr сообщение об ошибке
 * @param string $errfile имя файла, в котором произошла ошибка
 * @param int $errline номер строки, в которой произошла ошибка
 * @return boolean
 */
function error_handler($errno, $errstr, $errfile, $errline)
{
    // если ошибка попадает в отчет (при использовании оператора "@" error_reporting() вернет 0)
    if (error_reporting() & $errno)
    {
        $errors = array(
            E_ERROR => 'E_ERROR',
            E_WARNING => 'E_WARNING',
            E_PARSE => 'E_PARSE',
            E_NOTICE => 'E_NOTICE',
            E_CORE_ERROR => 'E_CORE_ERROR',
            E_CORE_WARNING => 'E_CORE_WARNING',
            E_COMPILE_ERROR => 'E_COMPILE_ERROR',
            E_COMPILE_WARNING => 'E_COMPILE_WARNING',
            E_USER_ERROR => 'E_USER_ERROR',
            E_USER_WARNING => 'E_USER_WARNING',
            E_USER_NOTICE => 'E_USER_NOTICE',
            E_STRICT => 'E_STRICT',
            E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
            E_DEPRECATED => 'E_DEPRECATED',
            E_USER_DEPRECATED => 'E_USER_DEPRECATED',
        );

        // выводим свое сообщение об ошибке
        echo "<b>{$errors[$errno]}</b>[$errno] $errstr ($errfile на $errline строке)<br />\n";
    }

    // не запускаем внутренний обработчик ошибок PHP
    return TRUE;
}

/**
 * Функция перехвата фатальных ошибок
 */
function fatal_error_handler()
{
    // если была ошибка и она фатальна
    if ($error = error_get_last() AND $error['type'] & ( E_ERROR | E_PARSE | E_COMPILE_ERROR | E_CORE_ERROR))
    {
        // очищаем буффер (не выводим стандартное сообщение об ошибке)
        ob_end_clean();
        // запускаем обработчик ошибок
        error_handler($error['type'], $error['message'], $error['file'], $error['line']);
    }
    else
    {
        // отправка (вывод) буфера и его отключение
        ob_end_flush();
    }
}

// определеяем уровень протоколирования ошибок
error_reporting(E_ALL | E_STRICT);
// определяем режим вывода ошибок
ini_set('display_errors', 'On');
// включаем буфферизацию вывода (вывод скрипта сохраняется во внутреннем буфере)
ob_start();
// устанавливаем пользовательский обработчик ошибок
set_error_handler("error_handler");
// регистрируем функцию, которая выполняется после завершения работы скрипта (например, после фатальной ошибки)
register_shutdown_function('fatal_error_handler');

require 'errors.php';

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

Вообще, рассмотренный пример обработчика ошибок, обработкой, как таковой, не занимается, а только демонстрирует саму возможность. Дальнейшее его поведение зависит от ваших желаний и/или требований. Например, все случаи обращения к обработчику можно записывать в лог, а в случае фатальных ошибок, дополнительно, уведомлять об этом администратора ресурса.

Полезные ссылки


AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 75

    +5
    Небольшое улучшение по обработке фатальных ошибок:
    // clean all buffers
    while (ob_get_level()) {
        ob_end_clean();
    }
    
      –5
      >function error_handler
      никогда так не делайте. кидайте ErrorException и обрабатывайте его.
        +2
        Кидать то хорошо, если сам. А если это Fatal Error, то как исключение его не обработаешь, потому что это не исключение )
        –2
        >echo "{$errors[$errno]}[$errno] $errstr ($errfile на $errline строке)
        \n";
        так же очень хочется видеть стек вызовов, входные параметры, разную логику в девелоперском и рабочем окружении
          +1
          1. Включайте трейс в начале скрипта, получайте стек в обработчике, кто мешает?
          2. Ну обьявите окружение, в зависимости от окружения обрабатывайте

          Эта статья — не пример использования, а гайд для тех кто желает разобратся. А уж используйте сами как хотите.
            0
            Для фатальных ошибок трейс перестанет существовать так как текущий контекст исполнения уничтожается.
              0
              можно трейс получить из xdebug, как указано ниже
            0
            Действительно ли отдельная от исключений обработка ошибок имеет смысл? Не слишком ли это все запутано? Не собираются ли разработчики PHP от нее избавляться?
              +1
              хотят наверное, но не могут. ибо порушится половина сайтов где эти ошибки сейчас просто гасятся собаками или настройками php.ini. обратная совместимость чтоб её. может с выносом всех функций о отдельные неймспейс php и можно будет такое сделать. но сейчас точно нет.
                0
                Это вопрос к разработчикам. А пока не избавились иногда без этого всего просто никуда.
                  0
                  Имеет, например, нет смысла бросать исключения в случае E_NOTICE (тем более в случаях E_STRICT или E_DEPRECATED), а сигнализировать об ошибке полезно.
                    0
                    Хм, давно уже не видел людей игнорирующих нотайсы…
                      0
                      Посмотрите на IP.Board (http://community.invisionpower.com/) их даже за ошибки не считают :) но вообще я не говорил что их нужно игнорировать, но вот выкидывать исключения это слишком, ИМХО.
                        +1
                        Ну почему в других языках это не «слишком» а вот конкретно в случае пхп становится «слишком»? :)
                          0
                          В каких других языках с динамической типизацией E_NOTICE при пропуске переменной или E_STRICT или тем более E_DEPRECATED выбрасывают исключение или приводит к фатальным ошибкам?
                            0
                            Не совсем понял что вы имеете в виду под «пропуском переменной».

                            Но если вам так нужен пример к моему коментарию — несуществующий индекс выбросит исключение в питоне, и это нормально.
                              0
                              > Не совсем понял что вы имеете в виду под «пропуском переменной»
                              Индекс как в вашем примере или свойство объекта или просто переменная с очепяткой.

                              > несуществующий индекс выбросит исключение в питоне
                              Но не выбросит в javascript и это тоже нормально.

                              PS: критичные ошибки (с E_WARNING) лично мне были бы удобнее в виде исключений.
                                0
                                Давайте я открою вам глаза…

                                <script>
                                foo={}
                                if(foo.bar == false) {
                                        alert("You won't see me!");
                                }
                                </script>
                                


                                <?php
                                Class Foo
                                {
                                
                                }
                                
                                $bar = new Foo();
                                
                                if($bar->baz == false && $not_defined_var == false)
                                {
                                        echo "BINGO!";
                                }
                                


                                Ну и контрольный в голову:

                                <script>
                                try {
                                        if(not_defined_var == false) {
                                        }
                                } catch (e) {
                                        alert('HAHA!');
                                }
                                </script>
                                
                                  +1
                                  Не совсем понятно что именно вы хотели показать этими примерами?

                                  1) undefined в js для неопределенных свойств (вообще никаких предупреждений)
                                  2) особенности приведения типов в PHP (тут хоть нотис есть)
                                  3) необходимость явного объявления в переменных в js
                                    0
                                    Я хотел сказать что некорректно сравнивать php с js, особенно так как это сделали вы:
                                    > несуществующий индекс выбросит исключение в питоне
                                    Но не выбросит в javascript и это тоже нормально.

                                    Или по вашему поведение типа «не, ну переменная конечно не определена но все равно она равна false потому что я так захотел» это нормально? )

                                    Ну а в последнем примере «просто переменная с очепяткой» выбрасывающая исключение :)
                                      0
                                      > это нормально

                                      Абсолютно нормально. Почему undefined нормально, а это нет?
                                      (они так кстати не равны false, а приводятся к этому типу).
                        0
                        Как заметили выше, есть не только E_NOTICE, но и E_STRICT и E_DEPRECATED. А ещё есть тонны устаревшего кода, который работает и который никто в здравом уме переписывать не будет. Зато будут обновлять версии пхп, дабы улучшить производительность или закрыть дыры в безопасности.

                        Главная особенность исключений в том, что они фатальны, если не перехвачены. Поэтому не получится просто взять и поменять старую систему ошибок на новую.

                        Если интересно, вот мнение расмуса на этот счёт.
                          0
                          Я в курсе насчет мнения расмуса, да и системма ошибок в пхп меня вроде как устраивает.
                          А ответ про нотайсы это просто мысли вслух (которые, впрочем, могут перерасти в нормальную конструктивную дискуссию) :)
                          +1
                          Приезжайте к нам в офис — покажу двух
                            0
                            Ну если бы не в другую страну ехать — может и заехал бы ради интереса :)
                          0
                          Так же как и с ошибками, на что генерировать, а на что нет, должно решаться в настройках, по-моему.
                        0
                        Ну, в error_handler вы можете генерировать исключения, более того есть стандартный класс ErrorException, спроектированный специально для этого (в мануале есть пример его использования в этих целях). Несколько строк в начале скрипта и все перехватываемые ошибки будут генерировать исключение, которые можно ловить в месте вызова.

                        В принципе, наверное, не сложно добавить ini параметр для автоматической генерации, но вряд ли это будет скоро по дефолту.
                        +8
                        Курсовую писали?
                          +1
                          нет) хотя соглашусь, что похоже.
                          на самом деле, хотел сделать небольшой обзор механизма обработки ошибок в кохане (с демонстрацией своего мини-модуля), а как начал копать — не смог остановиться и вот результат ))
                          хотя от первоначальной идеи не отказываюсь — думаю все же написать обзор, как логическое продолжение этой статьи
                            0
                            про кохану было бы интересно, можно ли ждать поста именно с конахой связанного?
                              0
                              ждать конечно можно, но вот точной датой я вас обрадовать не могу. Кто бы мог подумать, но написание статьи отнимает приличное время…
                                0
                                Для 3.2 писал класс перехвата ошибок. Для ajax-а отправлялся json с ошибкой, для CLI текст. Для обычного запроса — выводилась специальная страница ошибки (в зависимости от типа).

                                Для версии 3.3 все стало несколько проще… Или сложнее — кому как.
                                Там теперь можно реализовать метод get_response() в классе *_Exception а в переопределенном Kohana_Exception переопределить статический метод response, который отвечает построение «ответа» ошибки.
                            +2
                            >при использовании оператора "@" error_reporting() вернет 0
                            предлагаю при использовании "@" сразу руки отрывать использующему :)

                            А вообще да, статья полезная для тех кто еще не в курсе, все четко и ясно.
                            Где вы были 3 года назад когда ну очень надо было а доков почти не было? ))
                              +1
                              По поводу @ согласен, но… бывают ситуации разные.
                              Вот вам пример:
                              $fp = fsockopen($host, $port, $errno, $errstr);
                              if (!$fp) {
                                  echo "ERROR: $errno - $errstr<br />\n";
                              } else {
                                  fwrite($fp, "\n");
                                  echo fread($fp, 128);
                                  fclose($fp);
                              }
                              

                              В случае если вдруг не получилось приконнектиться будет Warning: fsockopen(): unable to connect to
                              И как же нужно красиво и правильно обрабатывать такую ситуацию без собачки, чтобы ничего лишнего не было в ответе даже на девелоперской машине (т.к. это может, например, попортить json)? Чем тут так плоха собачка @?
                                0
                                А зачем, извините, использовать безбожно устаревший инструментарий?
                                Не бывает таких ситуаций:
                                php.net/manual/en/function.socket-connect.php
                                php.net/manual/ru/function.stream-socket-client.php

                                Кстати в случае с последним вам только вызов подключения поменять придется, остальной код не изменится.
                                  +1
                                  Разве эти функции не генерируют warnings?
                                    –1
                                    Как это не странно — нет, не генерируют :)
                                      +1
                                      А это тогда что? %)
                                        0
                                        Хм… Извините, был не прав.
                                        Давно с сокетами на пхп не работал, все питон да питон :)

                                        Да, кстати, если вам нужно получать страничку из веба — curl в помощь, тот уж точно не генерирует :)
                                        0
                                        Обе эти функции еще как генерируют варнинги. Можете сами проверить (только показ варнингов включите).
                                        Вот например: PHP Warning: socket_connect(): unable to connect

                                        И как же быть? Собачка в этом случае всё равно такое зло? Если да, то чем же?
                                          +1
                                          Как я уже написал — был не прав. С тем что собака не зло — не соглашусь никогда после того как неделю искал ошибку что притаилась за @session_start написанным каким-то умником в огромном проекте.
                                            +1
                                            Собака — инструмент. Имхо, руки нужно отрывать, если сразу же после её использования не стоит проверка была ли ошибка.
                                  0
                                  О собаках. Я прекрасно понимаю потенциальный вред, который они могут нанести. Но считаю что следует поступать рационально, нежели как гласят какие-то догмы.

                                  Случаи типа

                                  @mysql_connect($host, $user, $pass) or die();
                                  

                                  не рассматриваем. Здесь любой мало-мальски опытный программист увидит зло.

                                  Но представим ситуацию: нужно удалить файл, если он есть. Если мы сделаем так:

                                  if (file_exists($file)) unlink($file);
                                  

                                  Есть вероятность, что файл может быть удален другим скриптом, между проверкой и удалением — получим warning.

                                  А так не получим:

                                  @unlink($file);
                                  

                                  Пример выше зависит от того установлен ли обработчик ошибок и того как он работает.

                                  Едем дальше. В последнее время, все чаще прихожу к выводу, что написание

                                  $name = @$_GET['name'] ?: $defaultName;
                                  

                                  тупо удобнее и лаконичнее, чем

                                  $name = isset($_GET['name']) ? $_GET['name'] : $defaultName;
                                  


                                  Хотелось бы услышать конструктивную критику приведенных примеров.
                                    +4
                                    PHP Filter Functions
                                    $name = filter_input('name') ?: $default_name;
                                      0
                                      Кстати, да. Это тоже удобно. Спасибо, что напомнили.
                                        0
                                        А Вы как считаете, @ хуже чем isset? Т.е. Вы бы мне руки оторвали за такую запись?
                                          0
                                          Моё мнение — хуже. Управлять ошибками через @ мне не нравится. Но, для простых проектов — это нормальное рабочее решение. Для сложных многоуровневых систем — однозначно подавлять ошибки нельзя.
                                        0
                                        Хотелось бы услышать конструктивную критику приведенных примеров.
                                        Использование @ для подавления ошибок снижает производительность. Иногда этим можно пренебречь иногда нет.
                                          0
                                          В php 5.4 над этим поработали.
                                            +1
                                            AndrewStephanoff, не особо в курсе, но разница на простом тесте 0.2-0.3 секунды. PHP 5.4.9.
                                              0
                                              Спасибо за тест, я сам не проверял — помню по мотивам презентации Змиевского. Видимо раньше было ещё хуже :D
                                                0
                                                А что за тест такой?

                                                Тест:
                                                <?php
                                                
                                                $count = 100000;
                                                
                                                $t = microtime(1);
                                                for ($i = 0; $i < $count; $i++) {
                                                	$zzz = isset($xxx);
                                                }
                                                $t = microtime(1) - $t;
                                                echo "isset: $t<br>";
                                                
                                                $t = microtime(1);
                                                for ($i = 0; $i < $count; $i++) {
                                                	$zzz = @$xxx;
                                                }
                                                $t = microtime(1) - $t;
                                                echo "@: $t<br>";
                                                

                                                Результат PHP 5.3:

                                                isset: 0.0094430446624756
                                                @: 0.080410003662109
                                                

                                                Результат PHP 5.4:

                                                isset: 0.0065739154815674
                                                @: 0.059032201766968
                                                
                                                  0
                                                  Итого, чтобы замедлить скрипт Вам понадобится сто тысяч собак :)

                                                  Но, конечно же, эти тесты не говорят ни-че-го. Абсолютно. Медлительность собаки зависит от обработчика ошибок (мы помним, что он запускается всегда). А значит если мы сделаем:

                                                  set_error_handler(function() {
                                                      sleep(1000);
                                                  });
                                                  

                                                  результаты теста очень сильно изменятся :)
                                                    +1
                                                    У вас результат еще хуже, чем у меня :) (значения с @ и без отличаются практически на порядок).
                                                    Тест простой:
                                                    % cat test1.php 
                                                    <?php 
                                                    function x() { } 
                                                    for ($i = 0; $i < 1000000; $i++) { x(); }
                                                    
                                                    % time php test1.php
                                                    php test1.php  1,24s user 0,03s system 69% cpu 1,833 total
                                                    

                                                    % cat test2.php 
                                                    <?php 
                                                    function x() { } 
                                                    for ($i = 0; $i < 1000000; $i++) { @x(); }
                                                    
                                                    % time php test2.php
                                                    php test2.php  1,51s user 0,03s system 75% cpu 2,059 total
                                                    


                                                    + будет райзиться ошибка в кастомном хендлере, если он есть (это дополнительный тормоз)
                                                    + сложнее дебажить
                                                    + ошибки надо исправлять, а не прятать :)
                                                      0
                                                      будет райзиться ошибка в кастомном хендлере, если он есть (это дополнительный тормоз)

                                                      Это верно. Стоит уточнить, что обработчик будет срабатывать только при ошибке. Т.е. код вида

                                                      @foo($bar);
                                                      

                                                      без ошибки будет трансформироваться в

                                                      $e = error_reporting(0);
                                                      foo($bar);
                                                      error_reporting($e);
                                                      

                                                      а с ошибкой в

                                                      $e = error_reporting(0);
                                                      foo($bar);
                                                      error_handler();
                                                      error_reporting($e);
                                                      

                                                      сложнее дебажить

                                                      В общем случае — да. Но если возникают подозрения, делается простой grep @ по коду и в обработчике ошибок начинаем логировать/выводить все подряд.

                                                      ошибки надо исправлять, а не прятать

                                                      @ != "прятать", @ == "подавлять вывод"
                                                      А исправлять конечно надо.
                                                        0
                                                        @ != «прятать», @ == «подавлять вывод»
                                                        По дефолту (если нет своего хендлера) именно прятать. Поскольку подавленный вывод даже в логе (error_log) не осядет.
                                                        делается простой grep @ по коду
                                                        Для этого у меня есть xdebug с опцией scream :)
                                                        Стоит уточнить, что обработчик будет срабатывать только при ошибке.
                                                        Верно. Но даже без ошибок мы получаем накладные расходы, которых можно избежать.
                                                          0
                                                          подавленный вывод даже в логе (error_log) не осядет.

                                                          Кстати, да. Это очень важно.

                                                          Для этого у меня есть xdebug с опцией scream

                                                          Точно! Это я видел, но еще не пробовал.

                                                          даже без ошибок мы получаем накладные расходы

                                                          Однозначно.

                                                          Я, в принципе, согласен по всем пунктам. Приятно общаться со знающими людьми.
                                                0
                                                Это все-таки не аргумент, а фуфло.
                                                Особенно при наличии нормальных аргументов.
                                                –1
                                                Есть вероятность, что файл может быть удален другим скриптом, между проверкой и удалением — получим warning.


                                                То есть то что файл удалился не тем скриптом который его должен был удалить — это нормально и у нас все хорошо, да?

                                                Со стороны выглядит примерно так:
                                                1. Какая-то гадость удаляет мои файлы
                                                2. какого хрена?! разбиратся лень
                                                3. проигнорим мы это дело…
                                                  +1
                                                  Не выглядит. Файл удаляется этим же скриптом. Обычный Race Condition.

                                                  С тем что собака не зло — не соглашусь никогда после того как неделю искал ошибку что притаилась за @session_start написанным каким-то умником в огромном проекте.


                                                  Я Вас понимаю. И не призываю тыкать собаку везде и всюду. Но, Вам тоже не стоит быть таким категоричным. Нужно смотреть зрело, т.е. осознавать к чему это приводит.

                                                  Кстати, в Вашей ситуации с сессией, можно было в обработчике ошибок логировать (или выводить) все подряд, тогда бы Вы не тратили столько времени. «Засобаченая» ошибка в любом случае попадает в обработчик.
                                                  0
                                                  Критика очень простая.
                                                  Типичный похапешник воображает, что ошибка бывает всегда только одна. Та, про которую он, похапешник, случайно узнал.
                                                  Мысль о том, что могут встречаться и другие ошибки, никогда не приходит ему в голову.
                                                  И по этой причине он начинает считать себя хитрее и умнее всех.
                                                  Ровно до тех пор, когда файл невозможно будет удалить не потому что кто-то успел раньше, а потому что нет прав на удаление или имя файла указано неверно.
                                                  В итоге наш похапешник имеет неработающее приложение и ни малейшей идеи — почему.
                                                    0
                                                    Согласен. А что это вы решили написать в прошлое :)?
                                                      0
                                                      На эту статью сослался очередной терпила на тостере.
                                                      Огорчило, что такие позорные статьи висят на хабре, на тему «самый извращенный способ заменить display_errors=0 в php.ini»

                                                      А про собаку уж так — под руку пришлось.
                                                        0
                                                        Ого, подняли тему из пепла)
                                                        Огорчило, что такие позорные статьи висят на хабре, на тему «самый извращенный способ заменить display_errors=0 в php.ini»

                                                        Хотелось бы получить ссылку на вопрос с тостера, чтобы понять контекст вопроса + больше аргументов.
                                                        В статье нет призыва использовать собаку или игнорировать ошибки.
                                                          0
                                                          В статье есть призыв писать при display_errors=on и инструкция, как с честью преодолеть возникающие при этом проблемы.
                                                        0
                                                        Забавно — а вот буквально только что сослались на перевод про ODKU :)
                                                  +2
                                                  С перехватом фатальной ошибки есть небольшой нюанс. Если вы захотите посмотреть стек вызова через debug_print_backtrace(), то увидите, что точка входа в программу — вызов register_shutdown_function(). Чтобы посмотреть нормальный стек вызова, нужно использовать xdebug_get_function_stack() (требует xdebug).
                                                    0
                                                    при использовании оператора управления ошибками (знак @) функция, определенная в set_error_handler() все равно будет вызвана

                                                    Это точно? Независимо от указанных в set_error_handler классов ошибок? Как-то не обращал внимания, наверное @ крайне редко использую :)
                                                      0
                                                      Если вы установили собственную функцию обработки ошибок с помощью set_error_handler(), то она все равно будет вызвана, однако, если внутри этой функции будет вызвана функция error_reporting(), то она вернет 0, если функция, вызвавшая данную ошибку, была подавлена с помощью @.
                                                        0
                                                        Читал перед тем как вопрос написать. Но неоднозначно написано, нет примечания типа "… то она всё равно будет вызвана в любом случае, даже если классы ошибок заданных при установке обработчика и произошедшей не совпадают".

                                                        Вечерком проверю.
                                                          0
                                                          VolCh, походу зависит от заданного типа ошибок.
                                                        0
                                                        Упс, там цитата должна была быть :)
                                                          +1
                                                          да, конечно зависит. если подавляемая ошибка не входит в класс ошибок определенных в set_error_handler(), то функция не будет вызвана.
                                                          здесь я хотел заострить внимание на том, что собака не работает как «выключатель» нашего обработчика и этот момент надо учитывать
                                                            0
                                                            Спасибо, теперь понятно.
                                                          0
                                                          Промахнулся. Перенес ответ выше.

                                                          Only users with full accounts can post comments. Log in, please.