Ви не повинні ловити виняток, якщо не маєте наміру зробити щось значиме .
"Щось значуще" може бути одним із таких:
Обробка винятку
Найбільш очевидною змістовною дією є обробка винятку, наприклад, відображення повідомлення про помилку та переривання операції:
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();
}
і в результаті виникла помилка виключення "Каталог цілей не вдалося створити", ви мали б право заплутатися. Обгортання цього "основного" винятку в шари інших винятків, які надають контекст, значно полегшить помилку ("Створення копії профілю не вдалося" -> "Операція копіювання файлу не вдалася" -> "Не вдалося створити цільовий каталог").