Вы не должны ловить исключение , если вы не собираетесь делать что-то значимое .
«Что-то значимое» может быть одним из них:
Обработка исключения
Наиболее очевидным значимым действием является обработка исключения, например, отображая сообщение об ошибке и прерывая операцию:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
echo "Error while connecting to database!";
die;
}
Регистрация или частичная очистка
Иногда вы не знаете, как правильно обрабатывать исключение в определенном контексте; возможно, вам не хватает информации о «большой картине», но вы хотите записать сбой как можно ближе к точке, где это произошло. В этом случае вам может понадобиться поймать, зарегистрировать и перебросить:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
logException($e); // does something
throw $e;
}
Связанный сценарий - это то, где вы находитесь в правильном месте, чтобы выполнить некоторую очистку для неудачной операции, но не решить, как следует обрабатывать сбой на верхнем уровне. В более ранних версиях PHP это было бы реализовано как
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
catch (Exception $e) {
$connect->disconnect(); // we don't want to keep the connection open anymore
throw $e; // but we also don't know how to respond to the failure
}
В PHP 5.5 введено ключевое слово finally
, поэтому для сценариев очистки теперь есть другой способ приблизиться к этому. Если код очистки должен выполняться независимо от того, что произошло (то есть как при ошибке, так и при успехе), теперь можно сделать это, прозрачно разрешая распространяться любым сгенерированным исключениям:
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
finally {
$connect->disconnect(); // no matter what
}
Ошибка абстракции (за исключением цепочки)
В третьем случае вы хотите логически сгруппировать множество возможных сбоев под большим зонтиком. Пример для логической группировки:
class ComponentInitException extends Exception {
// public constructors etc as in Exception
}
class Component {
public function __construct() {
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
throw new ComponentInitException($e->getMessage(), $e->getCode(), $e);
}
}
}
В этом случае вы не хотите, чтобы пользователи Component
знали, что это реализовано с использованием соединения с базой данных (возможно, вы хотите оставить свои параметры открытыми и использовать хранилище на основе файлов в будущем). Таким образом, в вашей спецификации для Component
будет сказано, что «в случае сбоя инициализации будет выброшено ComponentInitException
». Это позволяет потребителям Component
перехватывать исключения ожидаемого типа , а также позволяет отладочному коду получать доступ ко всем (зависящим от реализации) деталям .
Обеспечение более богатого контекста (за исключением цепочки)
Наконец, есть случаи, когда вы можете захотеть предоставить больше контекста для исключения. В этом случае имеет смысл заключить исключение в другое, которое содержит больше информации о том, что вы пытались сделать, когда произошла ошибка. Например:
class FileOperation {
public static function copyFiles() {
try {
$copier = new FileCopier(); // the constructor may throw
// this may throw if the files do no not exist
$copier->ensureSourceFilesExist();
// this may throw if the directory cannot be created
$copier->createTargetDirectory();
// this may throw if copying a file fails
$copier->performCopy();
}
catch (Exception $e) {
throw new Exception("Could not perform copy operation.", 0, $e);
}
}
}
Этот случай аналогичен приведенному выше (и пример, вероятно, не самый лучший, который можно придумать), но он иллюстрирует смысл предоставления большего контекста: если выдается исключение, он говорит нам, что копирование файла завершилось неудачно. Но почему не удалось? Эта информация предоставляется в виде обернутых исключений (которых может быть более одного уровня, если пример был намного сложнее).
Значение выполнения этого иллюстрируется, если вы думаете о сценарии, где, например, создание объекта UserProfile
приводит к копированию файлов, поскольку профиль пользователя хранится в файлах, и он поддерживает семантику транзакций: вы можете "отменить" изменения, поскольку они выполняются только для копии профиля, пока вы не подтвердите.
В этом случае, если вы сделали
try {
$profile = UserProfile::getInstance();
}
и в результате возникнет ошибка исключения «Целевой каталог не может быть создан», у вас будет право на путаницу. Обтекание этого «основного» исключения слоями других исключений, предоставляющих контекст, значительно облегчит возникновение ошибки («Ошибка создания копии профиля» -> «Ошибка операции копирования файла» -> «Не удалось создать целевой каталог»).