Правильная обработка ошибок в PHP

    Что я понимаю под правильной обработкой:


    • Универсальное решение, которое можно вставить в любой существующий код;
    • Легко расширяемое решение;
    • В PHP аж три «механизма ошибок»: собственно ошибки (error), исключения (exception) и утверждения (assertion). Свести три механизма к одному — exception. В комментариях к предыдущей статье на эту тему выражалось мнение, что exception это плохой и/или сложный метод обработки ошибок. Я так не считаю и готов это обсудить в комментариях;
    • Опциональное логирование;
    • Общий обработчик exception, который будет поддерживать разные форматы вывода и debug/production режимы;
    • В debug режиме должен выводится trace. Требования к trace: компактный, понятный и по возможности ссылки на открытие файлов в IDE.


    Универсальное решение


    Для этого сделаем класс exceptionHandlerClass. В exceptionHandlerClass будут храниться настройки и статические методы — обработчики error, exception и assertion. Еще нам нужны методы setupHandlers и restoreHandlers. Первый метод настроит перехват ошибок. Error и assertion обработчики будут бросать ErrorException. Exception обработчик будет обрабатывать необработанные Exception и в зависимости от настроек выводить соответствующий ответ. restoreHandlers вернет все обработчики в изначальное состояние — это поможет при встраивании класса в код с существующим механизмом обработки ошибок. Подключение выглядит так:
    1. require 'exceptionHandler/exceptionHandlerClass.php';
    2. exceptionHandlerClass::setupHandlers();

    включение debug режима (по умолчанию выключен) :
    1. exceptionHandlerClass::$debug = true;

    Форматы вывода


    Проще объяснить на примере: для вывода trace на веб странице я оберну его в таги pre и применю htmlspecialchars(), с другой стороны этот же trace при выводе в консоль будет не удобно читать, было бы проще, если бы это был plainText. Если нужно вывести ошибку как ответ SоapServer, то это должен быть правильно сформированный XML документ (SoapFault). Если скрипт выводит бинарные данные, например изображение, то удобней выводить ошибки через WildFire. Во всех этих ситуация нужно просто применить разные форматы вывода.
    Для разных форматов будем создавать разные классы. Я для начала реализую два формата вывода exceptionHandlerOutputWeb(для веба) и exceptionHandlerOutputCli(для командной строки). Так же нам понадобиться класс фабрика (exceptionHandlerOutputFactory), в котором будет инкапсулирована логика, когда какой формат вывода применить.
    1. public function getExceptionHandlerOutput(){
    2.     if(php_sapi_name() == 'cli'){
    3.         return new exceptionHandlerOutputCli();
    4.     }
    5.     return new exceptionHandlerOutputWeb();
    6. }

    При вызове setupHandlers можно установить формат вывода, передав экземпляр класса exceptionHandlerOutput* или exceptionHandlerOutputFactory*.
    1. exceptionHandlerClass::setupHandlers(new exceptionHandlerOutputAjax());

    Благодаря такой архитектуре можно легко расширять форматы. Для создания нового формата достаточно создать класс, который будет наследоваться от абстрактного класса exceptionHandlerOutput и реализовать один метод (output).
    1. class exceptionHandlerOutputAjax extends exceptionHandlerOutput{
    2.     public function output($exception, $debug){
    3.         header('HTTP/1.0 500 Internal Server Error', true, 500);
    4.         header('Status: 500 Internal Server Error', true, 500);
    5.         $response = array(
    6.             'error' => true,
    7.             'message' => '',
    8.         );
    9.         if($debug){
    10.             $response['message'] = $exception->getMessage();
    11.         } else {
    12.             $response['message'] = self::$productionMessage;
    13.         }
    14.         exit(json_encode($response));
    15.     }
    16. }

    Если нужна более сложная логика для автоматического выбора формата вывода, нужно создать класс, наследуемый от exceptionHandlerOutputFactory и реализовать метод getExceptionHandlerOutput.
    1. class exceptionHandlerOutputAjaxFactory extends exceptionHandlerOutputDefaultFactory{
    2.     public function getExceptionHandlerOutput() {
    3.         if( self::detect() ){
    4.             return new exceptionHandlerOutputAjax();
    5.         }
    6.         parent::getExceptionHandlerOutput();
    7.     }
    8.     public static function detect(){
    9.         return (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) 
    10.             && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');
    11.     }
    12. }
    13. exceptionHandlerClass::setupHandlers(new exceptionHandlerOutputAjaxFactory());

    Логирование


    Как я и сказал выше логирование можно включать по желанию. Для этого в exceptionHandlerClass создан метод exceptionLog
    1. public static function exceptionLog($exception, $logPriority = null){
    2.     if(!is_null(self::$exceptionHandlerLog)){
    3.         self::$exceptionHandlerLog->log($exception, $logPriority);
    4.     }
    5. }

    если нужно включить логирование, то достаточно сделать следующее:
    1. exceptionHandlerClass::$exceptionHandlerLog = new exceptionHandlerSimpleLog();

    Класс для логирования должен наследоваться от абстрактного exceptionHandlerLog и реализовывать метод log
    1. class exceptionHandlerSimpleLog extends exceptionHandlerLog{
    2.     public function log($exception, $logType){
    3.         switch ($logType){
    4.             case self::uncaughtException:
    5.                 error_log($exception->getMessage());
    6.                 break;
    7.         }
    8.     }
    9. }

    logType это одна из констант объявленных exceptionHandlerLog
    1. const uncaughtException = 0; //необработанные исключения
    2. const caughtException = 1; //вызов метода логирования вне обработчиков ошибок
    3. const ignoredError = 2; //ошибки маскированные @, логируются если выключена опция scream
    4. const lowPriorityError = 3; //ошибки которые не превращаются exception
    5. const assertion = 4; //assertion 

    Имея logType и exception разработчик может сам решить какие искллючения и как логировать. Например, uncaughtException можно высылать по почте, ignoredError с severity E_ERROR логировать в файл итп.

    Trace


    При выводе trace я хочу видеть тип исключения, сообщение и собственно сам trace. В trace для каждого вызова должно выводится, какая функция вызвалась, список «кратких» параметров, файл и строка где произошел вызов. Что такое «краткие» параметры объясню на примерах: если функцию вызвали со строкой длиной в 1000 символов, то наличие этой строки в trace ничем не поможет при решении проблемы, а только затруднит чтение trace, это же касается массивов с большой вложенностью. Вывод trace на экран просто должен подсказать, где искать. Чтобы разобраться, что именно происходит нужно дебажить с помощью xdebug или примитивных var_dump() и die(), кому как больше нравится.
    Пример trace:
    	[ErrorException]: E_WARNING - mysql_connect(): Can't connect to MySQL server on 'localhost' (10061)
    	#0: mysql_connect()
    	    D:\projects1\d\index.php:19
    	#1: testClass::test1("длиная строка…eeeeeery long string"(56))
    	    D:\projects1\d\index.php:22
    	#2: testClass->test2(testClass(), -∞, i:iTest, c:testClass, fa:testClass::test2)
    	    D:\projects1\d\index.php:27
    	#3: testAll(r:stream, fs:testClass::test1)
    	    D:\projects1\d\index.php:30
    
    Легенда
    • r: — resource
    • fs: — function (callable string)
    • fa: — function (callable array)
    • i: — interface (string)
    • c: — class (string)
    • ∞/INF — infinity
    • testClass() — object of type testClass
    • ""(n) — string, в скобках указана длина, … — место где вырезана часть строки
    • array(n) — array, в скобках указана длина

    И самое полезное… ссылки на открытие файлов в IDE прямо из trace.

    При нажатии на ссылку в IDE откроется соответствующий файл на соответствующей строке.
    Для консольного режима (консоль NetBeans):
    NetBeans Console
    1. exceptionHandlerOutputCli::setFileLinkFormat(': in %f on line %l');
    Для веб режима (TextMate):
    1. exceptionHandlerOutputWeb::setFileLinkFormat('txmt://open/?file://%f&line=%l');

    Можно реализовать для NetbBeans (или другого IDE). Для этого нужно: зарегистрировать протокол; сделать обработчик этого протокола (самое простое — bat файл). В обработчике через командную строку вызвать NetBeans с соответствующим файлом и строкой. Но это тема для следующей статьи.
    Код писался за два дня так, что возможны мелкие недочеты. Скачать (не было времени, чтобы выложить в репозиторий).

    UPD: перенесено в блог PHP
    UPD2: в продолжение темы работа с исключениями в PHP
    Share post

    Similar posts

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

    More
    Ads

    Comments 102

      0
      > Теперь по попорядку
      Исправьте :)
      Идея не нова, но реализация понравилась. Спасибо за материал.

      • UFO just landed and posted this here
        +2
        Один момент состоит в том что Fatal Error, который наиболее неприятен, это решение не перехватит. Как это сделать можно посмотреть в Debug_ErrorHook с dklab'а.
        В остальном решение хорошее, спасибо.
          +4
          Спасибо за подсказку. Решение до гениальности простое — register_shutdown_function();
          Учту в следующей версии библиотеки.
          • UFO just landed and posted this here
              0
              Спасибо. Обязательно посмотрю.
              Я тоже думал о подобном решении. Только кроме логирования я хочу еще и правильный ответ выдать. Мой вариант писать в header особую метку. Если метки нет (значит скрипт не правильно отработал), то reverse proxy отдаст 500 ошибку и залогирует событие. Это может пригодится при автоматическом тестировании.
              Баг в ПХП из-за которого приходится так извращатся bugs.php.net/bug.php?id=50921
                0
                >register_shutdown_function не всегда срабатывает при Fatal Error.

                Несогласен. Можете показать пример кода, когда она не срабатывает? Если вы имеете ввиду при E_PARSE в том же файле где определена register_shutdown_function(), то это логично и естественно — т. к. PHP-код из этого файла даже не успеет превратится в байт-код для интерпретатора.
                • UFO just landed and posted this here
                    0
                    версия PHP какая?
                    • UFO just landed and posted this here
                      0
                      У меня эти ошибки свободно ловятся с помощью register_shutdown_function(). PHP 5.2.9
                    • UFO just landed and posted this here
                        0
                        class FatalErrorClass{
                            public function  __toString() {
                                throw new Exception();
                            }
                        }
                        echo new FatalErrorClass();
                  –8
                  > таги pre

                  чоткий поцан х)

                  > Универсальное решение
                  > Error и assertion обработчики будут бросать ErrorException.

                  даже фатальные? ;-) если фатальные ошибки молча сглатываются, то это фигня, а не универсальное решение.

                  > При вызове setupHandlers можно установить формат вывода, передав экземпляр класса

                  почему слой работы с исключениями у тебя общается напрямую с клиентом, игнорируя фронт контроллер, который специально для того и предназначен, чтобы скрыть от приложения способ, которым клиент подключается к серверу?

                  > Для создания нового формата достаточно создать класс, который будет наследоваться от абстрактного класса exceptionHandlerOutput и реализовать один метод (output).

                  в чём сакральный смысл наполнять приложение грудой классов из одного единственного метода в каждом?

                  > Как я и сказал выше логирование можно включать по желанию.

                  может лучше наоборот — по желанию отключать?

                  > exceptionHandlerLog::uncaughtException

                  из названия константы не очевиден её смысл.
                    0
                    Кто-то настроение испортил?
                    1. 1. Про фатальные ошибки обсудили в комментариях выше
                    2. 2. Я обрабатываю только необработанные исключения, можно обрабатывать и на уровне приложения; в bootsrap файле может быть что-то типа
                      try{
                          myApplication::dispatch();
                      } catch(Exception $e){
                         //обработка
                      }
                      

                      Я предложил один из вариантов...
                    3. 3. Сакральный смысл — сделать расширяемое решение. Груда классов вряд ли понадобится — достаточно будет 1 или 2 кастомных обработчика (зависит от потребностей приложения)
                      –2
                      неа о0"

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

                      3. через обычные функции его не расширить?
                        0
                        2. пользователю надо выдавать нормальную страничку с сообщением об ошибке. с навигацией, поиском и прочими плюшками.
                        Я только за.
                        3. через обычные функции получится не ООП код. А я предпочитаю ООП
                          0
                          а… религия… понятно х)
                            0
                            это не религия, это легкая поддержка кода, читаемость, reusablity, encapsulation, modularity и многое другое.
                              +3
                              какое всё это умеет отношение к заворачиванию функций в классы?
                                –4
                                это ООП, детка. читай книги, их пишут умные люди, педалить можно научиться быстно, писать красивые приложения очень сложно… можно сделать все, но как, это уже другой вопрос
                                  0
                                  эта спесь со временем пройдёт, оставив после себя лишь чувство стыда за свою юношескую фанатичность
                                    0
                                    Лучше поздно чем никогда: </irony>
                                    А вообще, по сути камента: да, мне нравится ООП, и я считаю его хорошим подходом, это не спесь а в большей степени практика работы и с ооп и с функциональным подходом
                                  0
                                  разница между функцией и объектом, в том что объект еще может иметь состояние. Например, мне нужно реализовать формат вывода ошибок для SOAP сервера, для того чтобы вернуть SoapFault мне нужен инстанс SoapServer. В класс инстанс SoapServer можно передать, например, как параметр конструктора (см. код). А как это сделать для функции? Через глобальную переменую…
                                  Если вас и этот аргумент не убедил, тогда я прошу помощь зала
                                    0
                                    м… да, согласен. на пхп по другому никак…
                                      0
                                      можно извратится вот так.
                                      Думаю всем понятно, почему так делать не надо
                                        0
                                        ну, это ничем не лучше глобальной переменной
                                  +2
                                  В ООП должна быть логика, а когда под ООП подразумевается «понаделать классов», то спешу огорчить, это даже не ООП.

                                  Про «многое другое» не скажу, а вот легкая поддержка кода, читаемость, reusablity, modularity реализуется грамотным проектированием приложения и качественной реализацией.
                        +4
                        Мне одному кажется, что все попытки втащить в PHP куски кода и стиль из Явы и прочих Си-шарпов — это все от банального незнания (и нежелания знать) самого языка и чувства ложного превосходства над «быдло-кодерами на похапе»?

                        Или я ошибаюсь (допускаю и такой вариант)?
                          +3
                          Это не стиль Явы или Си-шарпа это стиль ООП. Я считаю, что ООП код гораздо проще поддерживать, он гораздо компактней и понятней. Но это дело вкуса.
                            +3
                            ОК, объяснюсь.

                            Вы пытаетесь обрабатывать ошибки интерпретатора средствами самого интерпретируемого языка. Это один подход.

                            Есть другой, реализованный в PHP и «родной» для языка — обработка ошибок интерпретатора передается внешнему обработчику, который делает то, что Вам надо — вывод стека вызовов, запись в лог, вывод на экран. В контексте PHP это более правильно, именно так построен стандартный обработчик и xdebug.

                            Вопрос к Вам: почему бы Ваш замечательный функционал не реализовать расширением к PHP, не было бы это правильнее?
                              +3
                              Это наиболее простой и легкоподдерживаемый способ. Плюс — доступность на шаред-хостинге.
                                –1
                                Разработчиков xdebug Ваш довод не убедил, хотя я уверен, что они с ним знакомы. Почему?
                          • UFO just landed and posted this here
                              +1
                              Рискую напороться на минуса. Мне вот всегда было непонятно, что же это за «PHP-way». Т.е. вот есть куча языков и считается, что у каждого свои преимущества и недостатки, и вообще, думать надо для специфического языка по-своему. Так вот: я никогда не улавливал чего-то специфического, что предлагает PHP. Ну, точнее один момент уловил — когда нужно что-то архи-, мегапростое, то проще писать небольшой скрипт на PHP, чем мучиться с настройкой Hibernate, написанием шаблонов, контроллеров, разворачиванием всего этого дело на Tomcat. Что же насчёт больших проектов — непонятно, где преимущество подхода PHP и в чём же именно он отличается в предлагаемых средствах от Java (и вообще, нафига ему динамическая типизация). Т.е., например, я прекрасно понимаю, зачем динамическая типизация в Python и почему ряд задач на нём решается на порядок выразительнее чем на Java. А в PHP как раз для «больших» приложений начали утаскивать чуть ли не один-в-один ООП из Java, причём, похоже, динамическая типизация обеспечивает там одно «преимущество» — отсутствие необходимости аннотации типов. Оно сомнительно на фоне того, что те же Scala или F# вполне себе статически типизированы, но при том не требуют аннотации типов. А вот то, что поддержка в IDE из-за этого никакая — это, конечно, просто ужасно.

                              Так что я думаю, нечего пинать явистов с претензиями «вы нас не понимаете». Как мне кажется, у PHP своя ниша образовалась исторически. А может, из-за низкого порога вхождения. Но никак не потому, что PHP вносит что-то принципиально новое в мышление человека, пишущего на нём, и что мир PHP сильно отличается от Java, C# или Python.
                                0
                                >Мне вот всегда было непонятно, что же это за «PHP-way».
                                Сам придумал и сам ищи ответ :)))
                              –36
                              Единственно правильная работа с похапе — это выкинуть его на хуй и осилить какой-нибудь язык программирования.
                                0
                                так почему же вы этого не сделали?
                                  –7
                                  Сделал уже давным-давно.
                                    +1
                                    ну тогда наверно не мешайте другим обучаться, радуйтесь другому языку молча, и все будут довольны
                                  0
                                  Чем же он вам так не угодил? Расскажите подробнее о его недостатках перед другими языками, может тогда вы бы выглядели не так банально. Например тежи разработчики facebook с вами не согласны.
                                    –2
                                    Недостатки, пожалуй, относительные, но каждый раз о них рассказывать…
                                    Что сейчас на ум приходит
                                    — нет настоящего FastCGI (частично решается opcode кешерами, но все равно сохранить объект между запросами без сериализации невозможно)
                                    — Не очень удобная работа с исключениями (решается использованием фреймворков)
                                    — Нет поддержки многопоточности и, вроде бы, асинхронных сокетов тоже (не всегда актуально, но многие любят на PHP демонов и всяких грабберов/парсеров писать)
                                    — пространства имен появились совсем недавно и почти нигде не используются (это ваще ужасно). Разделитель пространств имен — обратный слеш — отдельный спор.
                                    — Относительно много-длинно-словный синтаксис
                                    да и тут еще немного написано ru.wikipedia.org/wiki/PHP#.D0.9A.D1.80.D0.B8.D1.82.D0.B8.D0.BA.D0.B0

                                    По поводу FaceBook, мне кажется, использует PHP т.к. у них
                                    1) накопилась большая кодовая база, переписывать слишком затратно
                                    2) есть большие команды относительно дешевых разработчиков
                                    3) они уже транслятор php в c++ написали…
                                    Ну и реалтаймовые ответственные вещи у них давно на Java/C++/Python пишут (Cassandra, Tornado etc)
                                      +1
                                      То есть, для средне статистического веб приложения недостатков практически нет?

                                      Зато есть жирный достаток в виде: «есть большие команды относительно дешевых разработчиков»
                                        0
                                        По идее, для среднестатистических проектов используют фреймворки/CMS, которые многие проблемы решают. И это нормально.
                                        Но да, для простых приложений PHP пригоден (если забыть про неоптимальный синтаксис и несогласованность многих функций), более сложные могут начать тормозить из за слишком толстых фреймворков/оберток
                                          +1
                                          Кстати да, тормозить будет. Но есть мнение что железо дешевле программистов :)
                                      0
                                      А! еще вспомнил! Бесит что нельзя передать аргументом функции другую функцию/класс как таковую, только строку с ее именем (вроде того, как в Javascript назначаются каллбеки). Не знаю как такая возможность правильно называется, к сожалению.
                                        0
                                        в 5.3 можно делать так:

                                        $greet = function($name)
                                        {
                                        printf(«Hello %s\r\n», $name);
                                        };

                                        ну а потом передать ее куда угодно. Оно?
                                          0
                                          Лямбда — функции? Хм, не уверен что это именно то… Уже существующую функцию, видимо, можно передать только обернув в лямбду…

                                          А можно ли передать не функцию а класс?

                                          Учитывая, что я забыл уже где мне это было как то очень нужно, возможно что это не так уж и важно.
                                            0
                                            array('Class', 'Method') — вот так можно
                                            А в будущих версиях будет и нормальный синтаксис.
                                              0
                                              В PHP 5.3 есть возможность задать метод __invoke, класс обладающий таким методом можно вызывать как функцию, ну и соот-но использовать как callback тоже, а-ля:
                                              class ErrorHandler {
                                              
                                                public function __invoke($severity, $message, $file, $line) {
                                                  throw new ErrorException($message, 0, $severity, $file, $line);
                                                }
                                              
                                              }
                                              
                                              set_error_handler(new ErrorHandler);
                                              
                                      0
                                      А можно меня, тупого, ткнуть пальцем туда, где он детектит ошибки? :)
                                        0
                                        В статье сами обработчики не описываются. Можете посмотреть в коде (ссылка в конце статьи). Файл ExceptionHandlerClass.php статические методы *handler().
                                          +1
                                          Да, давно такого ООП не видел… :)
                                          Пойду копать.
                                        0
                                        Я тоже как-то заморочился проблемой отлова ошибок, при чем, Fatal тоже нужно было обязательно отловить.
                                        Ничего лучше не придумал, чем:

                                        <?
                                        ob_start(«fatal_error_handler»);
                                        set_error_handler(«handle_error»);

                                        ob_end_flush();
                                        ?>

                                        Если есть варианты отлова и вывода фаталов, ткните пожалуйста -)
                                          0
                                          register_shutdown_function
                                            0
                                            эээ, а как в ней получить буфер вывода с ошибкой?
                                              +2
                                              error_get_last
                                          0
                                          require на require_once подправьте
                                            0
                                            зачем, если он это делает один раз за все существование запроса?
                                            если можно обойтись без require_once, лучше обходиться, он медленнее из-за соответствующей проверки.
                                              0
                                              Экономия на спичках? Типа того, чтобы использовать одинарные кавычки вместо двойных?
                                                0
                                                Да, это очень важно в высоконагруженных приложениях. Как правило люди, забывающие эти правила допускают очень много таких мелочей, которые в итоге можно еще ускорить на 5-20%
                                                  +1
                                                  1. В высоконагруженных проектах нужно использовать APC и иже с ними, в таком случае проблема отпадает сама собой.
                                                  2. Разница есть только в случае использования относительных путей.
                                                  3. Если вообще не использовать require_once, в некоторых случаях придётся писать обёртки вокруг require, и не факт, что оно будет работать быстрее.
                                                  4. Обычно оптимизация алгоритмов даёт куда больше, чем 5%.

                                                  Итого: оптимизировать лучше в другом месте (например, поставить кэшер опкода).
                                                    0
                                                    Я не призываю писать обертку над require, я призываю не использовать require_once там, где он явно не нужен, тут именно такая ситуация.
                                                    APC кеширует опкод или он еще и оптимизирует что-то?

                                                    Я не против оптимизации алгоритмов, я призываю писать правильно всюду, даже в мелочах.
                                                      0
                                                      APC берёт на себя все require* и include* запросы и обрабатывает их сам и без лишней возни, вообще не касаясь файловой системы (если файл уже в кэше). require_once как раз подтормаживает ввиду многократных state директорий.

                                                      «Писать правильно» — среди прочего это означает, что при подключении файлов с классами, нужно использовать require_once. В большинстве ситуации, инициализация данного модуля должна быть где-то в начале кода, но бывает всяко, и теоретически может случиться повторное включение файла. Хотя, это скорее исключение.
                                            0
                                            Я все же НЕ считаю, что выбрасывать исключение по notice или warning было бы хорошей идеей.

                                            Я сам для обработки ошибок использую www.firephp.org и всем доволен, кроме случаев, когда в стэке оказываются данные большого размера ))

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

                                            Использовать Firephp проще простого

                                              0
                                              С моим решением можно использовать FirePhp (он же WildFire). Для этого нужно создать класс exceptionHandlerOutputFirephp и экземпляр этого класса передать как параметр для setupHandlers. Могу попозже выложить код такого класса.
                                                +1
                                                $firephp = FirePHP::getInstance(true);
                                                $firephp->registerErrorHandler();
                                                $firephp->registerExceptionHandler();
                                                  +1
                                                  Хорошо написанная программа не notice не warning не бросает. Соответсвенно, notice говорит о том. что где-то недоработка (проблема), надо разбираться
                                                  • UFO just landed and posted this here
                                                      0
                                                      Собственно, удобная обработка ошибок для того и нужна, чтобы было проще разобраться ;-)
                                                    • UFO just landed and posted this here
                                                        0
                                                        Я считаю, что "PHP Notice: Use of undefined constant myvar - assumed 'myvar'" и "PHP Warning: mysql_connect(): [2002] A connection attempt failed" вполне достойны для того, чтобы бросить исключение.
                                                        Хотя у меня в классе есть настройка, которая говорит какие ошибки превращать в исключения. По умолчанию E_ALL. Те ошибки которые не будут превращаться в исключения будут залогированы с флагом lowPriorityError.
                                                        0
                                                        А как же библиотека обработки ошибок PHP под названием Lagger? Она и фаталы отлавливает, и обработку там можно унифицировать как угодно.
                                                          0
                                                          Она умеет писать в файл (или куда-то еще) фаталы и неотловленные исключения?

                                                          Отловить то их не проблема, а вот прологировать — так и не разобрались как, т.к. после неотловленного эксепшна или фатала PHP режет любые попытки открыть хендлы или соединения.
                                                            0
                                                            error_log умеет
                                                              0
                                                              error_log пишет не туда, куда надо (например, если я хочу писать XML в другое место).

                                                              Так или иначе, проблемы с другими хранилищами остаются открытыми. В нашем случае было бы удобно писать в какой-нибудь Redis и собирать потом демоном для отправки в баг-трек.

                                                              Возможно только открывать соединение до возникновения фатала, но это расточительно и бессмысленно, если фатала в итоге все равно нет.

                                                                +1
                                                                error_log($messge."\n", 3, 'path/to/my-errors.log') пишет туда куда надо
                                                                А какие XML данные вы собираетесь писать при фатале, которых нет в стандартных логах вебсервера и php?
                                                                  0
                                                                  Вспомнил, правы. Но в итоге все равно понадобилось использовать другое хранилище.

                                                                  Юзер-агенты, текущего пользователя, куки, POST/GET, isXmlHttpRequest и пр.
                                                              0
                                                              Умеет. Это вообще никогда не было проблемой, если в fopen путь к файлу абсолютный задаётся.
                                                                0
                                                                Видимо, я плохо смотрел. Протестирую и отпишусь.

                                                                Опять же, как там дела с соединениями (MySQL, mongo и т.л.) после фатала?
                                                              0
                                                              Спасибо за ссылку. Может возьму оттуда идеи. Смущаает следующее (то что сразу заметил):
                                                              1. 1. на главной написано «Just 20kb of 100% OOP source code», а настройки сделаны через глобальные константы;
                                                              2. 2. коду два года (так написано на главной), а в коде есть тудушки с элементарными вещами, за такое время можно было додлеать
                                                                0
                                                                1. на главной написано «Just 20kb of 100% OOP source code», а настройки сделаны через глобальные константы;

                                                                Это просто для примера. Если бы подробнее разобрались, то поняли бы, что они там могут как угодно задаваться.

                                                                2. коду два года (так написано на главной), а в коде есть тудушки с элементарными вещами, за такое время можно было додлеать

                                                                Последний коммит был 19 января 2010, см. лог. Тудушки там абсолютно не критичны.
                                                              –3
                                                              Вопрос несколько не в тему. Возможно уважаемый автор что-то посоветует. Вопрос по отладке. Как отлаживать сайт, если:

                                                              а) Редактирование php файлов происходит по FTP(SSH) (через Aptana)
                                                              б) На сайте нет прямых вызовов скрипта, все через mod_rewrite
                                                              в) Хочу просто обновить страницу в бразуере и чтоб перекинуло в редактор на брейкоинт.

                                                              Какие переменные из этого уравнение нужно выкинуть, а какие добавить чтобы решить задачу? С XDebug никогда не работал.
                                                                0
                                                                я думаю самый простой вариант — xDebug, FireFox и easyXdebug
                                                                  0
                                                                  Да по хорошему в Aptana (eclipse) встроен клиент для XDebug и настроить это дело довольно просто.
                                                                  Только желательно чтоб сервер и Aptana на одном компьютере находились (точнее — чтобы СЕРВЕР МОГ подключаться к компьютеру с Aptana — типа внешний IP или одна сеть)
                                                                  mod_rewrite особого значения не имеет
                                                                  На шаред хостинге скорее всего такое настроить не удастся.
                                                                  0
                                                                  class exceptionHandlerOutputAjaxFactory extends exceptionHandlerOutputDefaultFactory
                                                                  класс!
                                                                    0
                                                                    В PHP аж три «механизма ошибок»: собственно ошибки (error), исключения (exception) и утверждения (assertion). Свести три механизма к одному — exception.


                                                                    Поправьте меня, если ошибаюсь. Типов ошибок в программе примерно два:
                                                                    1. Ожидаемые (expected error, checked exception — не получилось открыть файл по причине его отсутствия)
                                                                    2. Не ожидаемые (unexpected error, unchecked exception — закончилась память, неверный индекс массива, ошибка логики и так далее). Иногда не ожидаемые еще делят на системные (access violation) и программные (неверный аргумент функции, закончилась память, ошибка логики).

                                                                    Я правильно понимаю, что Вы для всех типов ошибок хотите применять исключения?
                                                                      0
                                                                      Exception применяются для ошибок которые можно обработать на програмном уровне. Фактически все те, которые можно было бы обойти дополнительными проверками, например file_exists() перед чтением файла или array_key_exists для неверного индекса массива.
                                                                        0
                                                                        Тоесть, если я правильно понял фразу «которые можно обработать на програмном уровне», то для всех типов ошибок, которые я выше перечислил. Тогда это вызывает ряд вопросов:

                                                                        1. Из кода будет не видна работа с ожидаемыми ошибками. Тоесть мы будем видеть вызовы функций, но не будем знать — обрабатывает ли программа от них ожидаемые ошибки где-нибудь выше или нет. Это, наверное, не очень хорошо.
                                                                        2. Добавление кода в любую функцию будет очень стремным процессом, так как по ее сигнатуре не будет видно, ожидаются ли от нее ошибки (тоесть если функция/метод возвращает bool, то понятно что от нее ожидают ошибки и добавляемый код эту ошибку может вернуть. А если функция ничего не возвращает и кидает исключения — то добавляя любой код мы сильно рискуем тем что он кинет исключение ожидаемой ошибки, которое выше никто не обработает).
                                                                          0
                                                                          Да такой подход предполагает внимательную обработку исключений. В месте где обработка критична нужно будет обработать все исключения (вплоть до верхнеуровневого — Exception) пример кода.
                                                                          Где обработка менее критична, можно оставить это на откуп глобального обработчика — try...catch на самом верхнем уровне в бутсрапе или exceptionHandler.
                                                                          Такой подход не означает, что не нужно делать проверки в коде, он даже стимулирует — потому, что вместо ворнинга, который можно игнорить получаешь исключение.
                                                                      0
                                                                      Да такой подход предполагает внимательную обработку исключений. В месте где обработка критична нужно будет обработать все исключения (вплоть до верхнеуровневого — Exception)


                                                                      Не боитесь, что код достаточного большого и сложного проекта, построенного по такой архитектуре, будет плохо читаем из-за того, что громную его часть будет составлять олинаковые россыпи try… catch? Не опасаетесь ошибок из-за copy-paste и замыливании взгляда при таком количестве одинаковых блоков?
                                                                        +1
                                                                        При правильном использовании try catch код становится более читабельным, чем при использовании if else. При if else обычно получается лесенка, и всякие else условия отвлекают от основной логики. При exception код «выпрамляется», можно четко увидеть позитивную ветку логики. Вот для сравнения два примера: код на if else, код на exception. И совсем экстремальный вариант при преобразовании ошибок в exception, не скажу что это правильный вариант, так как это определный magic — все таки разработчик должен точно знать и обрабатывать все ошибочные ситуации.
                                                                        Насчет замыливания глаза, это для каждого по своему. А copy-paste в первую очередь зависит от квалификации и ответсвености разработчика. И архитектура не может ни стимулировать ни предотвратить этого.
                                                                          0
                                                                          Код с использованием if-else выглядит как лесенка только если нужно ручное сворачивание стека. А так как у нас есть классы, то… if-else без лесенки
                                                                            0
                                                                            ну так конечно симпатичней. А если нужно передать из функции кроме факта ошибки(return false) еще и детали ошибки, то можно передать скажем ассоциативный массив, в котором будет тип/номер ошибки, меседж, ит.п; но массив это не стандартизация, если несколько разработчиков, то каждый может создавать ключи в массиве кто какие хочет — заменяем массив объектом (инстанс класса ошибки); теперь типы ошибок тоже надо бы стандартизовать — проще простого, вводим константы, чтоб они не висели в глобальном скопе кладем их в класс ошибки. А теперь, хочется еще и иметь возможность передать эту ошибку выше, т.е. в функцию которая вызвала функция…
                                                                            Так слово за слово. Где-то в конце этих рассуждений, можно прийти к модели очень сильно напоминающую исключения. И понять, что получился кривоватый велосипед, для работы с которым написан мануал, при работе с «велосипедом» он диктует как кодить (обязательно должен быть ретерн в функции, на выходе нужно проверить ни является ли объект инстансом класса ошибки и.т.п). Но если остановится где-то на ранних стадиях, то такой подход имеет право на жизнь. А если вы начинаете что-то изобретать, я бы посоветовал использовать готовое
                                                                              0
                                                                              А если нужно передать из функции кроме факта ошибки(return false) еще и детали ошибки, то можно передать скажем ассоциативный массив, в котором будет тип/номер ошибки, меседж, ит.п


                                                                              Как показывает достаточно широкая практика, из функции надо очень-очень редко вернуть что-то отличное от false. Более того, для исключений возврат дополнительной информации приводит к необходимости… нового класса исключения :). Так что тут как бы паритет в области, которая случается раз в пару месяцев (для библиотек — чуть чаще, но мы же не всегда пишем библиотеки?) и ни на что не влияет.

                                                                              Я правильно понимаю, что больше аргументов в пользу использования исключения для обработки ожидаемых ошибок нету?
                                                                                0
                                                                                Как показывает достаточно широкая практика, из функции надо очень-очень редко вернуть что-то отличное от false

                                                                                Ну вот в приведенном примере, задача вывести информативное сообщение пользователю. Если нет конекта к базе «сервер временно не доступен», если какой-то файл не читаем «Не возможно открыть файл <имя файла>», если какой-то файл не подходит по формату «Не верный формат файл <имя файла>».

                                                                                Если действительно в функции есть два исхода true/false и неудача происходит только по одной различимой причине, то подход с return true/false тоже подойдет. Но с другой стороны если, когда-то нужно будет расширить функционал, то придется делать или исключения или собственный механизм работы с ошибками. Вариант с собственным механизмом работы с ошибками мне не нравится. Остаются исключения.
                                                                                  0
                                                                                  Вопрос как и где рапортовать пользователю неудачи — это как правило архитектурный вопрос. Как обычно делают:
                                                                                  pastebin.com/DKid5jbf
                                                                                    0
                                                                                    А если view надо отделить от model/control?
                                                                                      +1
                                                                                      Как уже говорилось выше, это вопросы архитектуры приложения. Мало ошибку поймать — очень часто с ней надо что-то делать :). Рассмотрим самую простую ситуацию когда у нас есть веб страничка, где в качестве UI выступает HTML, а в качестве Model выступает написанный на php код, который выполняется на сервере. Ну и хотим мы, значит, в случае ошибки выдать пользователю подробный лог своих внутренностей (btw, задача странная, обычно это на сервере в лог складируется, а наружу у нас классическое хабровское 'что-то случилось, попробуйте еще разок').

                                                                                      Соответственно, код у нас будет выглядеть примерно следующим образом (PHP знаю плохо, поэтому ограничимся псевдокодом):

                                                                                      //  Временный объект, будет собирать нам ошибки.
                                                                                      $handler = new ErrorHandler();
                                                                                      соединим $database.ReportError с $handler.OnError
                                                                                      //  Все сломалось?
                                                                                      if( ! $database.DoSomeComplexAction() )
                                                                                      {
                                                                                        //  Получаем текст с красивым сообщением о том, что именно поломалось внутри
                                                                                        //  $database.
                                                                                        $text = $handler.PrettyPrint();
                                                                                        //  Информируем соответствующий View о том, что пользователю нужно
                                                                                        //  показать страничку с нашими внутренностями.
                                                                                        $erro_view.Show( $text );
                                                                                      }
                                                                                      
                                                                        0
                                                                        Настоятельно вас прошу указать под какой лицензией распространяются данные исходные коды.
                                                                        Так же смею настоять на AS-IS?
                                                                          0
                                                                          2Offenso. Достаточно будет, если я напишу это в комментарии?
                                                                          Код распространяется под «лицензией» AS-IS. Вы можете копировать, модифицировать код без указания автора и без уведомления автора. Можно использовать в коммерческих проектах. Хотя сейчас, мне этот код не кажется идеальным и я бы не использовал его в коммерческих проектах.

                                                                          Мнения автора могут не совпадать с его точкой зрения (с)
                                                                            0
                                                                            Да, благодарю. Мне удобно использовать ваш код при разработке, ибо моя система не подразумевает обработку таких ошибок. А вместе очень удобно. В релизе конечно будет работать только моя.

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