В PHP5, следует ли использовать Исключения или trigger_error / set_error_handler?

Каковы плюсы и минусы в любом случае. Есть ли один правильный путь ™?

Если вы хотите использовать исключения вместо ошибок для всего вашего приложения, вы можете сделать это с помощью ErrorException и настраиваемого обработчика ошибок (см. Страницу ErrorException для примера обработчика ошибок). Единственным недостатком этого метода является то, что ошибки, не связанные со смертельным исходом, все равно будут вызывать исключения, которые всегда являются фатальными, если не будут обнаружены. В принципе, даже E_NOTICE остановит все ваше приложение, если ваши параметры error_reporting не будут их подавлять.

На мой взгляд, существует несколько преимуществ использования ErrorException:

  1. Пользовательский обработчик исключений позволит вам отображать хорошие сообщения даже для ошибок, используя set_exception_handler .
  2. Это никак не нарушает существующий код … trigger_error и другие функции ошибок будут работать нормально.
  3. Это очень трудно игнорировать глупые ошибки кодирования, которые вызывают E_NOTICE s и E_WARNING .
  4. Вы можете использовать try / catch для переноса кода, который может генерировать ошибку PHP (а не только исключения), что является хорошим способом избежать использования взлома подавления ошибок @ :

     try { $foo = $_GET['foo']; } catch (ErrorException $e) { $foo = NULL; } 
  5. Вы можете обернуть весь свой скрипт в один блок try / catch если вы хотите отобразить дружественное сообщение вашим пользователям, когда происходит какая-либо нечеткая ошибка. (Делайте это осторожно, потому что регистрируются только неперехваченные ошибки и исключения).

Вы должны использовать исключения в «Исключительных обстоятельствах», то есть когда вы вызываете метод doFoo (), вы должны ожидать его выполнения, если по какой-то причине doFoo не может выполнить свою работу, тогда он должен вызвать исключение.

Многие старые php-коды использовали бы подход, возвращающий false или null, когда произошел сбой, но это затрудняет отладку, исключения делают эту отладку намного проще.

Например, вы сказали, что у вас есть метод с именем getDogFood (), который возвратил массив объектов DogFood, если вы вызвали этот метод, и он возвращает null, когда что-то пойдет не так, как ваш код вызова сможет определить, был ли возвращен null, поскольку произошла ошибка или просто нет корма для собак?

Что касается работы с устаревшими библиотеками кодов, которые используют встроенное ведение журнала ошибок php, вы можете переопределить регистрацию ошибок с помощью функции set_error_handler (), которую вы могли бы использовать, чтобы затем восстановить общее исключение.

Теперь, когда у вас есть весь код, который бросает подробные исключения, вы можете сами решить, что с ними делать, в некоторых частях вашего кода вы можете поймать их и попробовать альтернативные методы, или вы можете их зарегистрировать, используя свои собственные функции ведения журнала, которые может регистрироваться в базе данных, файле, электронной почте – в зависимости от того, что вы предпочитаете. Короче говоря – Исключения более гибкие.

Мне нравится идея использования исключений, но у меня часто есть сторонние библиотеки, а затем, если они не используют исключения, вы получаете 3-4 различных подхода к проблеме! Zend использует исключения. CakePHP использует собственный обработчик ошибок, и большинство библиотек PEAR используют объект PEAR :: Error.

Я, который там был единственным истинным способом в этом отношении. В этой ситуации, вероятно, наиболее гибким является маршрут настраиваемых обработчиков ошибок. Исключения – отличная идея, хотя если вы используете только свой собственный код или используете библиотеки, которые их используют.

К сожалению, в мире PHP мы по-прежнему страдаем от отказа умереть от PHP4, поэтому такие вещи, как исключения, в то время как они могут представлять лучшую практику, были невероятно медленными, пока все еще пишут вещи, чтобы иметь возможность работать как в 4 и 5. Надеюсь, этот фиаско заканчивается, хотя к тому времени, когда это произойдет, у нас будет напряжение между 6 и 5 вместо этого …

/ я держит голову в руках …

Это зависит от ситуации. Я предпочитаю использовать исключения, когда я пишу бизнес-логику / внутреннее приложение, и trigger_error для валидатора и вещи такого типа.

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

Профессионалы, использующие trigger_error для валидатора и такие вещи, например,

 try { $user->login(); } catch (AuthenticationFailureException $e) { set_error_handler("my_login_form_handler"); trigger_error("User could not be logged in. Please check username and password and try again!"); } catch (PersistenceException $pe) { // database unavailable set_error_handler("my_login_form_handler"); trigger_error("Internal system error. Please contact the administrator."); } 

где my_login_form_handler предает строку и помещает элемент в видимую область над формой входа.

Очевидно, что нет «одного правильного пути», но в этом есть множество мнений. 😉

Лично я использую trigger_error для вещей, которые исключения не могут сделать, а именно уведомления и предупреждения (т. Е. Материал, который вы хотите зарегистрировать, но не останавливайте поток приложения так же, как и ошибки / исключения (даже если вы поймаете их на некотором уровне )).

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

Итак, мое эмпирическое правило таково:

  • Если не найти что-то, это разумный результат, я нахожу гораздо проще возвращать и проверять нулевые значения (или какое-то другое значение по умолчанию), чем обрабатывать его, используя предложение try-catch.
  • Если, с другой стороны, не обнаружив серьезную ошибку, которая не входит в сферу действия вызывающего абонента, я бы все же выбросил исключение.

Причина исключения исключений в последнем случае (в отличие от ошибок запуска) заключается в том, что исключения гораздо более выразительны, учитывая, что вы используете правильно названные подклассы «Исключение». Я считаю, что использование исключений стандартной библиотеки PHP является хорошей отправной точкой при принятии решения о том, какие исключения использовать: http://www.php.net/~helly/php/ext/spl/classException.html

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

вступление

В моем личном опыте, как правило, я предпочитаю использовать Исключения в моем коде вместо trigger_error. Это происходит главным образом потому, что использование исключений является более гибким, чем запуск ошибок. И, ИМХО, это также полезно не только для меня, как для стороннего разработчика.

  1. Я могу расширить класс Exception (или использовать коды исключений), чтобы явно различать состояния моей библиотеки. Это помогает мне и сторонним разработчикам в обработке и отладке кода. Это также раскрывает, где и почему он может выйти из строя, без необходимости просмотра исходного кода.
  2. Я могу эффективно остановить выполнение моей библиотеки, не останавливая выполнение скрипта.
  3. Сторонний разработчик может связать мои исключения (в PHP> 5.3. *) Очень полезно для отладки и может быть удобно в ситуациях, когда моя библиотека может выйти из строя из-за разрозненных причин.

И я могу сделать все это, не навязывая, как он должен справляться с моими библиотечными сбоями. (т. е. создание сложных функций обработки ошибок). Он может использовать блок catch try или просто использовать общий обработчик исключений

Заметка:

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


пример

Я думаю, что это пример может проиллюстрировать мою точку зрения:

 class HTMLParser { protected $doc; protected $source = null; public $parsedHtml; protected $parseErrors = array(); public function __construct($doc) { if (!$doc instanceof DOMDocument) { // My Object is unusable without a valid DOMDOcument object // so I throw a CriticalException throw new CriticalException("Could not create Object Foo. You must pass a valid DOMDOcument object as parameter in the constructor"); } $this->doc = $doc; } public function setSource($source) { if (!is_string($source)) { // I expect $source to be a string but was passed something else so I throw an exception throw new InvalidArgumentException("I expected a string but got " . gettype($source) . " instead"); } $this->source = trim($source); return $this; } public function parse() { if (is_null($this->source) || $this->source == '') { throw new EmptyStringException("Source is empty"); } libxml_use_internal_errors(true); $this->doc->loadHTML($this->source); $this->parsedHtml = $this->doc->saveHTML(); $errors = libxml_get_errors(); if (count($errors) > 0) { $this->parseErrors = $errors; throw new HtmlParsingException($errors[0]->message,$errors[0]->code,null, $errors[0]->level,$errors[0]->column,$errors[0]->file,$errors[0]->line); } return $this; } public function getParseErrors() { return $this->parseErrors; } public function getDOMObj() { return clone $this->doc; } } 

объяснение

В конструкторе я бросаю CriticalException если парам прошел не из типа DOMDocument потому что без него моя библиотека не будет работать вообще.

(Примечание: я мог бы просто написать __construct(DOMDocument $doc) но это только пример).

В setsource() я бросаю InvalidArgumentException если парам передан как нечто отличное от строки. Я предпочитаю останавливать выполнение библиотеки здесь, потому что свойство source является основным свойством моего класса, а недопустимое значение будет распространять ошибку в моей библиотеке.

Метод parse() обычно является последним методом, вызываемым в цикле. Несмотря на то, что я бросаю XmlParsingException если libXML находит неверный документ, сначала выполняется синтаксический анализ, а результаты можно использовать (в некоторой степени).


Обработка библиотеки примеров

Вот пример, как обрабатывать эту заполненную библиотеку:

 $source = file_get_contents('http://www.somehost.com/some_page.html'); try { $parser = new HTMLParser(new DOMDocument()); $parser->setSource($source) ->parse(); } catch (CriticalException $e) { // Library failed miserably, no recover is possible for it. // In this case, it's prorably my fault because I didn't pass // a DOMDocument object. print 'Sorry. I made a mistake. Please send me feedback!'; } catch (InvalidArgumentException $e) { // the source passed is not a string, again probably my fault. // But I have a working parser object. // Maybe I can try again by typecasting the argument to string var_dump($parser); } catch (EmptyStringException $e) { // The source string was empty. Maybe there was an error // retrieving the HTML? Maybe the remote server is down? // Maybe the website does not exist anymore? In this case, // it isn't my fault it failed. Maybe I can use a cached // version? var_dump($parser); } catch (HtmlParsingException $e) { // The html suplied is malformed. I got it from the interwebs // so it's not my fault. I can use $e or getParseErrors() // to see if the html (and DOM Object) is usable // I also have a full functioning HTMLParser Object and can // retrieve a "loaded" functioning DOMDocument Object var_dump($parser->getParseErrors()); var_dump($parser->getDOMObj()); } $var = 'this will print wether an exception was previously thrown or not'; print $var; 

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

Как замечание, использование Exceptions не означает, что выполнение PROGRAM будет остановлено, это просто означает, что код, зависящий от моего объекта, будет обходить. Это зависит от меня или стороннего разработчика, как ему нравится.

Идея исключения элегантна и делает процесс обработки ошибок настолько плавным. но это применимо только тогда, когда у вас есть соответствующие классы исключений и в развитии команды, еще одна важная вещь – это «стандартные» исключения. поэтому, если вы планируете использовать исключения, лучше сначала стандартизировать свои типы исключений, или лучший выбор – использовать исключения из некоторой популярной структуры. еще одна вещь, которая относится к PHP (где вы можете написать свой объект кода orienter в сочетании со структурным кодом), заключается в том, что если вы пишете все свое приложение, используя классы. Если вы пишете объектную ориентацию, то исключения лучше. в конце концов, я думаю, что ваш процесс обработки ошибок будет намного более плавным, чем trigger_error и т. д.

Исключения – это современный и надежный способ сигнализации об ошибке / исключительной ситуации. Используй их 🙂

Использование исключений не является хорошей идеей в эпоху интеграции сторонних приложений .

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

ДАЖЕ, если у вас есть средства в вашем приложении, чтобы восполнить ошибку в той библиотеке, которую вы используете .

Например, примером может быть сторонняя социальная логическая библиотека, которая генерирует исключение, потому что социальный логин вернул ошибку и, кстати, убил все ваше приложение без необходимости – hybridauth. Итак, у вас есть все приложение, и там у вас есть библиотека, которая добавляет вам дополнительные функции – в этом случае – социальный логин – и даже если у вас много резервных вещей в случае, если провайдер не аутентифицирует (ваш собственный система входа в систему, плюс примерно 20 или около того других поставщиков социального входа), ваше ПОЛНОЕ приложение остановится. И вам в конечном итоге придется изменить стороннюю библиотеку, чтобы обойти эти проблемы, и точка использования сторонней библиотеки для ускорения разработки будет потеряна.

Это серьезный недостаток дизайна в отношении философии обработки ошибок в PHP. Давайте посмотрим правде в глаза – под другим концом большинства приложений, разработанных сегодня, есть пользователь. Будь то пользователь интрасети, будь то пользователь через Интернет, будь то системный администратор, это не имеет значения – обычно есть пользователь.

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

В результате им придется просто отпустить определенную стороннюю библиотеку (в этом случае hybridauth), а не использовать ее в своем приложении, исключительно по этой причине. Несмотря на то, что hybridauth – очень хорошая библиотека, и, по-видимому, на это потрачено много хороших усилий, с флеторой возможностей.

Поэтому вам следует воздерживаться от использования исключений в вашем коде. ДАЖЕ, если код, который вы сейчас делаете, это код верхнего уровня, который будет запускать ваше приложение, а не библиотеку, возможно, вы захотите включить весь или часть своего кода в другие проекты или интегрировать части или всего его с другим кодом вашего или сторонним кодом. И если вы воспользовались исключениями, вы столкнетесь с одинаковой ситуацией – все приложения / интеграции умирают в вашем лице, даже если у вас есть надлежащие средства для решения любой проблемы, которую предоставляет часть кода.