Pull to refresh

Comments 60

а бы добавил error_reporting:
set_error_handler(create_function('$c, $m, $f, $l', 'if (error_reporting())throw new MyException($m, $c, $f, $l);'), E_ALL);
для обработчки @ собачки и вообще, display errors
display_errors не обрабатывает собачку, а лишь скрывает ее присутствие.
Сверху есть ссылка, на пост с обсуждением проблем, связанных с употреблением собаки (смешно звучит). В моей статье не обсуждается возможность или невозможность применения собаки, а изыскиваются пути избавления от нее с наибольшей выгодой.
Мне, например, нравится возможность, обрабатывать ошибки, а не решать выводить или не выводить их на экран. Основной плюс в том, что конструктор созданного класса может выполнять любые действия: писать в лог, отсылать на мыло, выводить красивое сообщение об ошибке, да и много чего еще.
Есть проблема — в случае @ перед include/require error_reporting будет нулевой для всех ошибок, возникающих при выполнении подключаемого файла.
Неплохо!) Я, как любитель таких вот универсальных штуковин давно хотел придумать себе что-то подобное для обработки ошибок и исключений. Теперь отталкиваться буду от Вашего метода.
Не мешало бы добавить в статью об пользовательском обработчике исключений - set_exception_handler(functName), котрый вызывает functName, если исключение не перехвачено.
Да, конечно. Нужно будет добавить, но это уже когда вернусь с работы домой.
Все-таки нехорошо подрывать капиталистическую экономику так нагло :)
спасибо за интересный пост.
это все хорошо, но php 5 еще дааалеко не на всех хостингах к сожалению.
а вообще рекомендуется писать с включенными
ini_set("display_errors", "1");
error_reporting(E_ALL);
чтобы отсекать все нотисы и ворнинги. фатал ерроры в таких случаях чаще всего будут возникать из за внешних факторов, обусловленных проблемами с базой или хостингом.
1. если на хостинге php4 - повод задуматься о смене хостера
2. Включенное отображение об ошибках на работающем сайте может быть очень полезно при взломе сайта. Так что лучше либо отключать вовсе, либо логировать.
Лучше отлаживать на одном сервере, а продакшн вести на другом.
мой случай - скрипты на заказ. которые могут приобрести разные люди, и устанавливать их на свои разные хостинги. отдавать скрипты с ошибками в таком случае недопустимо.
Я уже видел довольно много даже бесплатных хостингов с PHP5.
Обратите внимание, что поддержка PHP4 закончилась 31 декабря 2007г. Дальше будут только обновления безопасности.
все верно, было бы хорошо, если бы вторую часть вашего комментария услышали все хостеры мира:)
Вы хотите размещаться одновременно на всех хостерах мира?
Мне тоже (как и вашим друзьям-разработчикам) кажется, что это не самый удачный пример использования исключений.
Обычно, рекомендуют использовать исключения только в нештатных ситуациях, ни в коем случае не применяя их для управления логикой исполнения. И этому есть разумные объяснения. Исключения очень хороши во первых тем, что позволяют снять с кода бросающего исключение ответственность по его обработке, давая возможность среагировать на ошибку коду, который знает как это сделать. Исключения так же хороши тем, что в отличие от подхода с возвратом кода ошибок их невозможно игнорировать (это как раз к тому, почему они хороши для обработки нештатных ситуаций). Плюс вы получаете преимущества связанные с уменьшением дублирования и улучшения читаемости кода обработки ошибок.
Если же использовать исключения для организации логики, то в этом они подобны goto, если даже не хуже. Из одного места кода вы вдруг переходите в другое. Повсеместное использование исключений разрушает код. Конечно, грань очень тонкая. Неверные данные, введённые пользователем это штатная или не штатная ситуация? Это зависит от точки зрения разработчика и уровня кода работающего с данными (скажем, на уровне представления неверно введённые данные это нормально, если же вы пытаетесь сохранить объект с этими неверными данными в базу то наверное уже нет.)
В вашем случае без исключений вполне можно было бы обойтись. Было бы вполне достаточно вернуть из функции валидации объект с данными об ошибках. Я не вижу, как в этом конкретном примере полезны исключения.
Вот поэтому я и написал, что это пример, который не несет особой смысловой нагрузки.
В то же время я указал, что можно использовать разные классы исключений и несколько блоков catch, а если и так не хотите, то можно (даже нужно) определить отдельный класс для исключений приложения в целом и указывать соответствующий тип при обработке в блоке catch.

  1. class Application_Exception extends Exception {}
  2. class Validation_Exception extends Exception {}
  3.  
  4. try {
  5.    if ($validationError) {
  6.       throw new Validation_Exception('Validation error');
  7.    }
  8.    if ($applicationError) {
  9.       throw new Application_Exception('Application error');
  10.    }
  11. }
  12. catch (Validation_Exception $e) {
  13.    //Обрабатываем только ошибки валидации
  14. }
* This source code was highlighted with Source Code Highlighter.

В этом случае, если возникает ошибка валидации, то она обрабатывается "на месте", а ошибка приложения проскакивает дальше.
пример, не несущий смысловой нагрузки, — это не пример, а сферический конь в вакууме. и если опытным это очевидно, то новички пойдут клепать валидаторы через исключения
Ого, тут еще кто-то пишет, через 2 года после публикации.
Во-первых, я уже и не помню, почему привел именно такой пример. Наверное, потому что лень было выдумывать что-то реальное для иллюстрации своей мысли.
Во-вторых, у каждого разработчика должна быть определенная эволюция. Я и сам через 2 года, написал бы уже по-другому где-то, но новичок должен пройти все этапы большого пути, включая быдлокод, чтобы понимать «что такое хорошо, а что такое плохо».
я как-то перепрыгнул говнокод и совсем от этого не страдаю.
foreach ($error as $e) {
echo $e.'<br />';
}

==

echo implode('<br />', $error);
Очень ждал материал по исключениям, спасибо. Ибо до сих пор не понимаю их и интерфейсы еще :)
более корректный вариант для обработки значения error_reporting:

function my_error_handler($c, $m, $f, $l) {
if (error_reporting() & $c)throw new MyException($m, $c, $f, $l);
}
set_error_handler('my_error_handler');
Тогда еще короче:

function my_error_handler($c, $m, $f, $l) {
  throw new MyException($m, $c, $f, $l);
}
set_error_handler('my_error_handler', error_reporting());

* This source code was highlighted with Source Code Highlighter.
Это не то же самое. Ваш вариант не учитывает возможные изменения настройки error_reporting во время работы скрипта.
Итерировать по исключению не есть гуд. Не нагружайте инструмент лишним функционалом. FormValidator - отдельно, FormValidationException - отдельно, с кодом ошибки и сообщением (const FIRST_FIELD_ERROR, const LAST_FIELD_ERROR ....).

Несколько скомканный пример, а в целом хорошо. Исключения - очень хорошая практика ИМХО.
Данный пример я привел, чтобы показать как, порой необычно, можно применить стандартные инструменты языка (сам когда-то подсмотрел это на одном из форумов, теперь уже не помню где). В конце я указал, что это всего лишь пример.
А я для себя просто реализовал перехват ошибок и запись в лог-файл. Если кому интересно - вот этот класс: http://www.phpclasses.org/browse/package/4552.html
и вы каждый раз в этот лог заглядываете?
имхо, в вашем случае нужно сделать крон который будет отсылать лог файл на мейл каждые допустим 10-30 минут
Можно сделать даже проще, в случае ошибок при завершении скрипта отсылать лог на почту.
... и если к вам на страницу с ошибкой придет 1000 человек - вашей почте будет тяжко :)
Это точно, но класс скорее для отладки, в продакшн нет смысла его включать. :)
25. echo 'Not validation error! '.$error->getMessage(); — это на каком языке? :) Ладно еще, если в каком-то тестовом примере такой корявый инглиш, но не в публичных же статьях!
UFO just landed and posted this here
UFO just landed and posted this here
"Не ошибка валидации" писать нельзя даже по-русски: это бред. Это значит "не ошибка", т.е. нет ошибки. Как минимум тогда уж "ошибка не валидации", но это тоже бред. В вашем примере стоило просто написать "другая ошибка" и все было бы понятно.
"валидации не ошибка" (с) Мастер Йода
Как-то раз я решил стать "настоящим программистом" (с) и переписать код своей маленькой, но очень милой сердцу cms-ки на эксепшны. Немного погуглив, почитал мысли людей, принципы и подходы которых мне очень близки (не только по исключениям, а вообще - "be simple, we don't need rocket science in code") - Раймонда Чена и Джоэля Спольски:

Exceptions by Joel Spolsky
http://www.joelonsoftware.com/items/2003…

Cleaner, more elegant, and harder to recognize by Raymond Chen
http://blogs.msdn.com/oldnewthing/archiv…

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

Хотя у exception-powered подхода есть свои преимущества, безусловно. Просто для меня они пока не перевесили главный недостаток - некоторого ухудшения читабельности и "прямолинейности" кода.
Странно, а я нашел там не прямо противоположное, а абсолютно такое же мнение...
Хороший пример — это реакция на нехватку памяти. У самого Джоэла притчей во языцех стало то, что нерадивые программисты забывают проверять значение, возвращенное из malloc(). Ну хорошо, проверили мы, что malloc вернул "памяти нет". Что делать? Только отказываться от дальнейшей работы этой функции, что тут другое придумать? Вот исключения и делают это автоматически и надежно, не полагаясь на то, что каждая из цепочки в 50 вызывающих функций не забудет передать наверх каждую ошибку, возникшую в самой глубине менеджера памяти.
Ксате интересный пример использования исключений, правда я все таки считаю что не для такого они предназначены (все таки неправильно введеные данные пользователем - это нормальная ситуация), и в реальной жизни применять их в этой ситуации не стоит... Но я бы еще добавил Countable, и строчку if((bool)$e->current()) { заменил бы на if(count($e)) {
В целом эксепшны юзать хорошо. Но переиспользовать их вредно.

Два момента в которых вы имхо ошибаетесь:

- Нет смысла заворачивать fatal errors в исключения. Вы хотите отлавливать их в try .. catch и красиво завершать? Красиво завершать и так получится если юзать set_error_handler. Или же греет мысль что скажем "division by zero" вы сможете отлавливать и продолжать далее как будто так и надо? И это вместо того чтобы вставить (когда заметите) проверку делителя до деления? Мрак.

- Нотисы и варнинги заворачивать и подавно глупо, ведь сакральный смысл их в том, что программа в продакшн режиме сможет продолжать работу, паралельно залогав эту ошибку в error_logе (и/или отослав мыло), а девелопер поправит как только сможет, нестрашно.

Один момент в котором вы имхо придете к неэффективному/сложному коду:
- Использование исключений для штатных ситуаций (емэйл невалиден) лишь плодит try .. catch блоки, а следить за ними (если их много) ненамного легче чем за глобальными переменными (если их много).
Нет смысла заворачивать fatal errors в исключения. Вы хотите отлавливать их в try .. catch и красиво завершать? Красиво завершать и так получится если юзать set_error_handler.

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

А я ведь не генерировал много try...catch блоков... Я накапливал сообщения об ошибках в теле объекта исключения, а бросал его только один раз, соответственно и обрабатывать его нужно только один раз.
Смысл в том, что для разных типов исключений я бы рекомендовал заводить разные классы, тогда некоторые блоки try...catch будут пропускать исключения наверх - вот и получим необходимую гибкость.
try {
validateForm(...)
} catch ($e) {
...
}
не более удобно чем:
if (!validateForm(..., &$errors) {
...
}
А помимо неудобства это, хотя выглядит оопэшно, но нарушает инкапсуляцию: код который использует третью библиотеку будет должен знать имена эксепшнов этой библиотеки. Это конечно если есть эксепшны которые не ловятся внутри библиотеки. Иначе нчего особенного, кроме try ... catch которые не по назначению.

Exception - исключительная ситуация, а не рядовая бизнес логика (как валидация емэйла). Сложно представить когда нужно чтобы преимущества эксепшнов (например эскалация эксепшна вверх) были нужны при обнаружении невалидного емэйла. Если ничего кроме возвращения массива ошибок не нужно, то и не нужен всего-то return.
UFO just landed and posted this here
А помимо неудобства это, хотя выглядит оопэшно, но нарушает инкапсуляцию: код который использует третью библиотеку будет должен знать имена эксепшнов этой библиотеки.

Если вы используете какую-то библиотеку, то вы, во-первых, должны прочитать API и вам желательно знать имена классов, в нее входящих.
Во-вторых, все классы исключений наследуются от Exception, поэтому если вы не хотите учить имена классов можете просто писать catch(Exception $e).
Как это нарушает инкапсуляцию я вообще не понял... Вы же не выносите код класса за его пределы. Все внутриклассовые ошибки обрабатываются в классе, а ошибки связанные с неправильными параметрами, переданными извне должны генерировать исключение. Лично я тут проблемы не вижу.
Насчет первого момента, Fatal Error-ы в пыхе не перехватываются. Нет, конечно перехватить их можно, но... это делается через такую жопу, что это никому и ненужно.

Насчет варнингов в пхп все очень тонко. К примеру по варнингу от mysql_connect, можно выкидывать эксепшн - потому, как варнинг нам сообщит о том, что мы не смогли приконнектица к бд, в том же foreach-е если он будет выкидываца - тоже будет хорошо, потому как сразу заставит посмотреть почему же у нас иногда не те данные имеюца (и такое поведение имхо хорошо и для варнингов с киданием заголовков и кук)... но вот в случае с DOMDocument::loadHTML - такое поведение будет неочень хорошо, потому как он же html нормально распарсит, но кинет варинг, а использовать @ или пустой catch - не очень красиво.
> К примеру по варнингу от mysql_connect, можно выкидывать эксепшн.
Кидать эксепшн здесь нужно, зачем только для этого перехватывать варнинги
потому что я тогда не буду писать сам три строчки в виде
if(!$connection) {
  throw new ModelConnectionException('я нисмог приконектицо к базеданых')
}

А эксепнш будет кинут по варнингу.
Сомнительное удовольствие
Да, насчет Fatal Error, это я протупил, признаю. Почему-то показалось, что такие ошибки, как Division By Zero должны быть Fatal Error...
Не, это ворнинг. Что наводит на мысль, что ворнинг в пхп это нечто большее чем просто ворнинг...
А можно как то вывести дерево функции в порядке их вызова? То есть ошибка была в функции A() которая была вызвана из функции B() которая была вызвана из функции C(). В standalone языках обычно это дебагер сразу показывает + показывает что было передано в параметрах функции. Есть что то такое в php?
debug_print_backtrace()
Работает в любой функции, а не только при ошибке.

В самом классе Exception есть член trace (содержит всю информацию по стеку функций в виде массива), а таже методы getTrace() и getTraceAsString().

PS: Для PHP тоже есть отладчики.
Довольно просто и функционально, такой ООПшный способ навешивания обработчика мне понравился. Но соглашусь с писавшими выше, что валидатор форм лучше делать отдельно.
Простите за «небольшой» некропостинг, но поскольку статья на втором месте в яндексе, то хотелось бы указать небольшой АПДЕЙТ:
Уже довольно давно существует класс ErrorException, так что идея с MyException давно реализована в ядре пхп, и свой велосипед не нужен. Ну и с валидатором тоже конечно кошмарно, но вы это сами указали.
ПС: я понимаю что 8 лет прошло, просто статья по прежнему в топе)
Лол, не знал, что статья в топе. Может, стоит закрыть?
Я бы написал апдейт и жирненько в начале о том, что статья устарела и осталась для истории. Мне например было интересно прочитать такую древность. Кто-то может узнает про ErrorException, ведь наверняка много таких велосипедов до сих пор гуляет в сети…
Sign up to leave a comment.

Articles