Почему и как вы используете Исключения в этом примере кода PHP?

Я задавался вопросом, зачем использовать Исключения в моем PHP. Давайте рассмотрим простой пример:

class Worker { public function goToWork() { return $isInThatMood ? // Okay, I'll do it. true : // In your dreams... false; } } $worker = new Worker; if (!$worker->goToWork()) { if (date('l',time()) == 'Sunday') echo "Fine, you don't have to work on Sundays..."; else echo "Get your a** back to work!"; } else echo "Good."; 

Есть ли причина для использования Исключения для такого кода? Зачем? Как будет строиться код?

А как насчет кода, который может вызвать ошибки:

 class FileOutputter { public function outputFile($file) { if (!file_exists($file)) return false; return file_get_contents($file); } } 

Почему я должен использовать Исключения в приведенном выше случае? У меня такое ощущение, что Исключения помогают вам распознать тип проблемы, которая произошла, правда?

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

 class FileOutputter { public function outputFile($file) { if (!file_exists($file)) return throw new Exception("File not found.",123); try { $contents = file_get_contents($file); } catch (Exception $e) { return $e; } return $contents; } } 

Или это бедно? Теперь базовый код может сделать это:

 $fo = new FileOutputter; try { $fo->outputFile("File.extension"); } catch (Exception $e) { // Something happened, we could either display the error/problem directly echo $e->getMessage(); // Or use the info to make alternative execution flows if ($e->getCode() == 123) // The one we specified earlier // Do something else now, create "an exception" } 

Или я полностью потерялся здесь?

Когда следует использовать исключение?

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

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

Как создать пользовательские исключения?

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

 class DatabaseException extends Exception {} 

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

Когда я не должен использовать исключение?

Рассмотрим еще один пример; метод, который проверяет существование файла. Вероятно, это не должно вызывать исключения, если файл не существует, поскольку цель метода заключалась в выполнении указанной проверки. Однако метод, который открывает файл и выполняет некоторую обработку, может вызвать исключение, поскольку файл должен был существовать и т. Д.

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

Зачем использовать исключения вместо возврата специальных кодов ошибок и т. Д.?

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

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

Я не программист на PHP, но это похоже на C #.

Как правило, вы хотите выбросить ошибки, если это точка возврата. Тогда вы сможете записать что-то, чтобы показать, что это невозможно.

Если вы можете сказать, что файл не существует, вы можете просто сказать это. Никакой реальной потребности в моем уме, чтобы также исключить.

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

Я бы сказал, что это хорошая практика проектирования, чтобы избежать уловов всех исключений «catch (Exception $ e)» и дизайна по контракту вместо этого, как вы, кажется, делаете в предыдущем примере. Сначала я был бы более конкретным с типом исключения, который был бы выброшен, а затем работал оттуда, если это необходимо. В противном случае держитесь подальше от try-> catch и исключений.

Вы никак не можете предположить, что файл существует только потому, что вы вызвали file_exists() ! Функция file_exists не открывает файл, поэтому файл может быть удален или переименован или перемещен в любое время!

 class FileOutputter { public function outputFile($file) { if (!file_exists($file)) return false; ///<--- file may be deleted right here without you knowing it return file_get_contents($file);//<-- then this will throw an error //if you don't catch it, you're program may halt. } } 

Я считаю, что это лучше:

 class FileOutputter { public function outputFile($file) { try{ if (!file_exists($file)) return false; return file_get_contents($file); }catch(Exception e){ check_what_went_wrong_and_go_to_plan_B(); } } } 

( Edit : И, вероятно, даже лучше попытаться открыть файл с самого начала. Если вам это удастся, тогда у вас есть «блокировка» в файле, и он не просто исчезнет. Если нет, то поймайте исключение и посмотрите, что пошло не так.

Опять же, вы можете почувствовать, что этот уровень перераспределения просто глупо. В этом случае я не думаю, что вам нужно беспокоиться о try/catch вообще 🙂

На самом деле, ваш код для вывода файлов неверен, так как file_get_contents ($ file) не генерирует исключение. Однако он будет вызывать предупреждение, если файл не существует или недоступен по какой-либо причине. Кроме того, вы возвращаете исключение из outputFile, когда вам, вероятно, следует просто позволить ошибке распространять стек вызовов.

Однако вы можете зарегистрировать обработчик ошибок в php для исключения исключений при возникновении ошибки:

 function exception_error_handler($errno, $errstr, $errfile, $errline ) { throw new ErrorException($errstr, 0, $errno, $errfile, $errline); } set_error_handler("exception_error_handler"); 

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

Итак, я бы изменил FileOutputter на это (с добавленным выше фрагментом):

 class FileOutputter { public function outputFile($file) { if (!file_exists($file)) throw new Exception("File not found.",123); return file_get_contents($file); } } 

Вызывающий код тогда в основном тот же.

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