PHP
1 May 2009

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

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

Изучив синтаксис конструкции try{...}catch(Exception $e){...}, узнав о возможности создавать собственные классы исключений, наследуя их от класса Exception и поверхностно осознав мощь оказавшегося в ваших руках механизма, дальше вы можете пойти по одному из двух путей:
  • Начать тут же использовать исключения. Скажем, прикручивать их к системе, в которой они никогда не использовались. Или приколачивать их к проекту, в котором ООП и не пахнет. Или, что самое ужасное, пытаться использовать их повсеместно, особенно там, где это не нужно.
  • Попытаться понять, где их применять, как делать это правильно, и зачем они нужны.


1. Нужно уметь чётко отличать в программе исключительную ситуацию от рядовой ошибки, например: неправильно введённые логин или пароль — это не исключительная ситуация! Это ошибка пользователя, а не программы, и она может происходить оченьчасто. С другой стороны, сбой в работе функции mysql_connect() из-за недоступности сервера БД — это исключительная ситуация, исключение нужно бросать! Подробнее об этом дальше.

2. Методы класса не должны перехватывать исключения, сгенерированные другими методами этого же класса. Библиотека вообще ничего не должна знать о том, что делать в случае возникновения исключительной ситуации в её методах, так как в зависимости от системы, где она используется, это поведение может сильно различаться. Её дело — сгенерировать исключение. К примеру, если файла, требующегося для корректной работы всего вашего класса, не существует, вы не должны с этим разбираться. Это дело вышестоящих уровней приложения.

С другой стороны, перехватывать исключения, брошенные нижестоящими уровнями, Вы вполне можете (если хотите). Если не хотите, они будут переданы по стеку в ближайший catch{} (точнее в catch{}, соответствующий ближайшему try{} ). Допустим, у вас есть класс-обёртка для работы с БД, с методами типа connect(), query() и так далее. Разумеется, если к серверу подключиться не удаётся, необходимо сгенерировать исключение в методе connect().

Однако перехватывать его не должен ни сам метод connect(), ни даже метод query(), который мог вызвать connect() автоматически! Данное исключение должно быть перехвачено на вышестоящем уровне, который работает с методами этого класса, и там должно быть принято решение — попробовать подключиться к другому серверу, использовать другой тип источника данных, просто вывести на экран ошибку, или передать исключение ещё выше (в разных системах по-разному, пускай сами решают, что делать). Надеюсь, мысль понятна.

3. В крупном приложении необходимо использовать свои собственные классы исключений (унаследованные от встроенного Exception). Это позволяет выстроить иерархию классов ошибок и разделять их по важности, типу и так далее. Допустим, у нас есть класс Application_Exception:

class Application_Exception extends Exception{...}
и несколько его потомков:


class Logic_Exception extends Application_Exception{...}
-- логические ошибки, например, нарушение связности таблиц в БД или уникальности ключей.


class Data_Access_Exception extends Application_Exception{...}
-- ошибки доступа к необходимым данным, вроде отсутствия файла, или устройства, или связи для доступа к какому-то источнику.


class Security_Exception extends Application_Exception{...}
-- ошибки с безопасностью, например человек присылает куку, которой у него просто не может быть ;)


Все эти типы ошибок должны обрабатываться по-разному, например, про ошибки с безопасностью не стоит кричать на весь интернет, а вот в случае отказа сервера БД можно вывести интеллигентную табличку с извинениями (в общем, на вкус и цвет).

4. Для получения максимального количества информации об ошибке, используйте возможности встроенного класса Exception, не изобретайте велосипедов! Если вам недостаточно имени файла и строки, где было брошено исключение, к вашим услугам методы getTrace() и getPrevious(), которые уж точно выложат перед вами всю «картину» произошедшего (подробнее в документации).

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

+96
13.5k 167
Comments 98
Top of the day