Зачем нужны throw Exception?

sverel

Новичок
Зачем нужны throw Exception?

С момента выхода ПХП-5 пытаюсь перейти от set_error_handler('myErrorHandler'); к этой модной технике, но каждый раз меня останавливает мысль: нафига? Ведь и так всё отлично работает. Моя ф-ция myErrorHandler() универсальна(php4-5), выводит подробные сообщения, дампит $_REQUEST, выводит трассировку и в зависимости от сотояния константы DEBUG_MODE либо посылает сообщение на мыло и показывает белую страницу "извините, сайт не доступен", либо сразу отображает. Ах да, она ещё решает: нужно ли на этом остановить выполнение скрипта, либо только уведомить и продолжить работу. А что ещё нужно?
Развесив везде try/catch, всё равно придётся вешать set_error_hendler, ведь обычные notice/warning - тоже надо красиво показывать. А там где некоторые пишут throw, я пишу trigger_error(...);
Ещё однин минус, то что блок try/catch невозможно вынести в ф-цию или отдельный файл. У меня есть несколько точек входа в систему (публичная часть, админка, flshApi) и везде надо будет копипастить ОДИН И ТОТ ЖЕ КУСОК КОДА! Это уже противоречит всем моим низкоуровневым принципам.

PHP:
<?php
try {

  $controller->go(); // или $adminController->go(); или $flashApiController->go();

} catch (DatabaseException $e) {
  print "Невозможно создать соединение с базой данных. Причина: " . $e->getMessage();
} catch (UserNotFoundException $e) {
  print "Пользователя не существует";
} catch (MailServiceException $e) {
  print "Ошибка отправки письма: " . $e->getMessage();
} catch (Exception $e) {
  print "Общая ошибка: " . $e->getMessage();
} 
?>
А свой set_error_handler('myErrorHandler'); я вынес в core.php - он же во всех точках входа подключается.

Единственное почему я ещё не забил на try/catch - так это потому что всегда когда пытаешся прикрутить какую-либо библиотечку (PEAR, ZF) - все они кидаются throw Exception-ами.

Может кто-нибудь толково объяснить, чем try/catch реально ВЫГОДНЕЕ/УДОБНЕЕ set_error_handler-а ?
Ну кроме того, что в статье написано: "Exceptions (исключения) - это неотъемлемая часть любого СОВРЕМЕННОГО языка" - не аргумент.

P.S.> админам: наверное это должно быть в "теории программирования", но правила я читаю.
 

sverel

Новичок
Внимательно прочитал. Повышение надёжности - это аргумент. Но я не понял как throw exception позволит мне его добится? Самый первый пример в статье с переподключением к резервному серверу БД в случае если первый не доступен. Я так и не понял, как это реализуют с помощью try/catch? Если выбросить throw exception, то программа дальше уже не выполнится.
PHP:
try {
    firstConnect();
    throw new Exception(); // аналог goto:
    secondConnect(); // это уже никогда не выполнится!
    doSomthingGood(); // и это тоже
}
Т.е. при помощи этого механизма, мы даже уведомление создать не можем без того что бы не остановить выполнение основной программы.
Я же в своих скриптакх вызываю trigger_error() с уровнем E_NOTICE - при такой ошибке ф-ция myErrorHandler только уведомляет программиста, но не прерывает ход выполнения программы и программа выполняется дальше.
 

Фанат

oncle terrible
Команда форума
дальше не выполняется не вся программа, а только блок до catch
это, пожалуй, и есть основное применение исключений - маленькая смерть. обработка некритических ошибок.
с критическими-то все ясно - чемодан, вокзал, 500 error
мы даже уведомление создать не можем без того что бы не остановить выполнение основной программы.
никто не заставляет переписывать error handler на обработку исключениев
обычные notice/warning - тоже надо красиво показывать.
вот это я не понимаю. куда показывать? зачем? перед кем им в логе красоваться?
 

sverel

Новичок
Да. Но try ставится в самом начале - в точке входа в программу. А в самом конце стоит catch которые собирает все возможные исключения.

В вышеуказанной статье рассказывается про бОльшую стабильность за счет того что некоторые ошибки можно "вылечить". Например, я подключаю PEAR-класс QuickForm.
PHP:
try {
   $form = new QuickForm2();
   $form->addElement('myDate'); // не существующий тип элемента
   $form->addElement('submit');
   $form->display();
} catch(...) { ... }
Здесь я пытаюсь вставить элемент которого не суествует 'myDate'. При этом QuickForm выплюнет throw new HTML_QuickForm2_NotFoundException();
Вообще-то эту ошибку можно вылечить, заменив навороченный date-picker на обычный инпут - это не так юзабельно, но зато стабильность программы будет высокой. Однако, механизм try/catch не позволяет продолжить выполнение программы дальше и субмит не будет добавлен. И даже $form->display() не будет выполнен.
Так как же их лечить? Или каждую строку своего кода вставлять в try/catch?
PHP:
try {
  $form = new QuickForm();
} catch (...) {...}
try {
  $form->addElement('myDate');
} catch (...) {...}
try {
  $form->addElement('submit');
} catch (...) {...}
try {
  $form->display();
} catch (...) {...}
Бред.
 

sverel

Новичок
Автор оригинала: ***** вот это я не понимаю. куда показывать? зачем? перед кем им в логе красоваться?
PHP:
function myErrorHandler(...) {
 $html = ''; // Cюда в красивом виде собираем всю информацию об ошибке
 if (DEBUG_MODE===true) 
   echo $html;
 } else {
   mail('[email protected]', 'Хозяина, я выполнила ошибку!', $html);
 }

 if ($errorType !== E_NOTICE) { // Если ошибка критическая, то останавливаем выполнение программы.
    if (DEBUG_MODE === false) {// Обычные юзеры увидят на белом листе единственную надпись: "извените, сайт по техническим причинам не работает. Программистам уже пропесочили мозги и в ближайшее время они всё починят."
       include('error.tpl'); 
    }
    exit;
 }
 

cDLEON

Онанист РНРСlub
а вообще - исключения это же ведь не панацея от всех бед.
Это всего лишь ещё один инструмент. Который, кстати, я считаю очень удобным...
 

sverel

Новичок
Вот я и пытаюсь понять чем удобен. Что в нём есть такого, что нельзя реализовать на set_error_handler? Может быть я реально чего-то не понимаю?
Но см. выше пример с QuickForm - если бы он вместо throw выплёвывал бы trigger_error(...), то было бы то же самое, даже ещё проще:
1. Я в своём обработчике могу влиять на дальнейшее выполнение программы: остановить или продолжить.
2. Я могу внутри myErrorHandler написать обработчик этой исключительной ситауции и подставлять обычный инпут вместо навороченного date-picker - т.е. "вылечить ошибку" и не останавливать дальнейшего выполнения программы.
3. Не надо везде писать кучу try/catch - достаточно в core.php написать одну строку: set_error_handler('myErrorHandler');
 

Фанат

oncle terrible
Команда форума
мне в свое время на подобный вопрос написали

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

Плюс ко всему, отличие исклчений от die() в том, что исключение на каком-то промежуточном этапе можно поймать, и, на усмотрение, полностью обработать (например, делать insert, а при DbUniqueError делать update; это если нету replace или insert on duplicate update как в MySQL), перевкинуть другое исклоючение (например, заменить DbUniqueError на EmailALreadyUsedError), или сделать окаточный код (например, rollback) и кинуть исходное исключение.
 

Splurov

Новичок
Автор оригинала: sverel
...
Если выбросить throw exception, то программа дальше уже не выполнится.
PHP:
try {
    firstConnect();
    throw new Exception(); // аналог goto:
    secondConnect(); // это уже никогда не выполнится!
    doSomthingGood(); // и это тоже
}
...
Всё-таки не очень внимательно:
PHP:
try {
  $conn = Connection::connect('127.0.0.1:8080'); // если не удаётся законектится, внутри функции выбрасывается исключение, например ServerUnavailableException
}
catch(ServerUnavailableException $e) {
  Logger::log($e);
  $conn = Connection::connect('127.0.0.1:8081');
}
$conn->doSomething();
На счёт твоего примера с QuickForm.
Если ты знаешь, что контрола myDate нету, нафига ты вообще пытаешься его создать?
Исключение может помочь в таком случае:
PHP:
try {
  $form->addElement($customDateUncontrolledElement);
}
catch (HTML_QuickForm2_NotFoundException $e) {
  Logger::log($e);
  $form->addElement('date');
}
Посмотри как исключения используются в ZF.
 

iceman

говнокодер
sverel

исключения не для перехвата ошибки, а для создания ЛОГИКИ программы с учетом ошибки... на pl/sql я хорошо использую этот механизм...

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

ну пхп уже испоганен НОТИСАМИ...
PHP:
try {
   $form = new QuickForm();
   $form->addElement('myDate'); // не существующий тип элемента
   $form->addElement('submit');
   $form->display(); 
} 
catch (Exception1 $e) {...}
catch (Exception2 $e) {...}
catch (Exception3 $e) {...}
catch (Exception4 $e) {...}
читай мануал, Экзепшины могут наследоваться... просто программитст писавший Класс не должен тупо кидать Экзепшен от стандартного
PHP:
throw new Exception...
 

sverel

Новичок
Автор оригинала: *****
И тогда throw делается в одном месте (где ошибка выявлена), а catch -- где-то далеко-далеко, где эту ошибку могут обработать.
Это же самое делает set_error_handler ! Написал его в самом начале программы, а потом крути верти своими паттернами, и там где возникнет ошибка вместо "throw" напиши "trigger_error(...)" и вот тут вызовется твой обработчик который далеко-далеко. И обработчик один для всех точек входа, для любой конструкции для любого куска кода. И внутри этого обработчика ты уже можешь сделать обработку исключительных ситуаций:
PHP:
if ($type==='...') {
  исправитьПодключениеБД();
} elseif () {
  заменитьDatePickerНаInput();
}
-~{}~ 09.06.10 12:53:

Splurov
Вот и получается на каждую строчку кода писать свой блок try/catch. Ведь $form = new QuickForm() тоже может выплюнуть ошибку, вот и получается уже код:

PHP:
try {
   $form = new QuickForm();
} catch (....) {
   Logger::log($e);
   doSomthing();
}


try {
  $form->addElement($customDateUncontrolledElement);
}
catch (HTML_QuickForm2_NotFoundException $e) {
  Logger::log($e);
  $form->addElement('date');
}
Разве нет?
Ваш пример с подключением к сервер не корректен т.к. не обладает гибкостью. Я не говорю про то что одну строчку кода надо брать в try/catch Я подозреваю, что если у нас есть резервный сервер, то в скором будующем, может появится третий и четвёртый. Вы будете делать бесконечный каскад из try/catch? Я бы написал так:

PHP:
$DB = null;
$i=0;
while ( ! is_object($DB) && $i < count($serversSetting)) { // Будет выполнятся пока не подключится, либо пока не сервера не закончатся
 $DB = new DB($serversSetting[$i]);
  if ( ! is_object($DB)) {
    trigger_error(...); // Для каждой неудачной попытки подключения, будет сообщать мне на мыло.
    $i++;
  }
}
 

sverel

Новичок
iceman
Вы этим пользуетесь в pl/sql - значит ли эта фраза, что Вы не пользуетесь этим в PHP? Разговор именно про PHP. При генерации формы я и так сделаю обработку, что бы вместо пустой формы были значения по умолчанию. Нет смысла эту часть логики выносить дааалеко-дааалеко от основного скрипта генерирующего форму. Иначе, так всю программу можно вынести внутрь catch :)))


> ну пхп уже испоганен НОТИСАМИ...
и ещё if-ами и foreach-ами :))) Нотис - это полезная для программера штука.
 

iceman

говнокодер
$form->addElement('date5454');

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

-~{}~ 09.06.10 13:04:

sverel
> и ещё if-ами и foreach-ами )) Нотис - это полезная для программера штука.

интересно в каких случаях?
 

sverel

Новичок
iceman
> исключения не для перехвата ошибки, а для создания ЛОГИКИ программы с учетом ошибки

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

newARTix

Новичок
В процессе работы приложения не должно возникать notice/warning, так как это именно ошибка в коде. А не исключительная ситуация.
Такие ошибки должны быть устранены в процессе отладки. И пользователь их точно увидеть, и даже почувствовать, в идеале, не должен.
 

newARTix

Новичок
sverel
никто никому ничего не должен. это зависит от архитектуры. делаешь как лучше. А в случае с еррор-хендлером ты подставляешь дефолтные данные в нем чтоле? :)

-~{}~ 09.06.10 13:10:

Вурдалак
ну да. разве нельзя проверить файл на доступность к чтению-записи? Это явно нештатная ситуация.
 
Сверху