Оглавление

Генерирование и перехват исключений

В модели ООП при возникновении ошибки создается и генерируется (throw) исключение. Это означает, что при генерировании исключения во время выполнения сценария оно будет постоянно передаваться в стек вызовов до тех пор, пока не произойдет одно из следующих событий:

  • Исключение будет перехвачено одной из функций в стеке.
  • Исключение не будет перехвачено и достигнет вершины стека.

Генерирование исключения в сценарии осуществляется с помощью оператора throw и передачи ему экземпляра генерируемого исключения, как показано в листинге 26.

 

Листинг 26

class ThrowExample {
    public function makeError() {
        throw new Exception("Пример исключения");
    }
}
$inst = new ThrowExample();
$inst->makeError();

 

В листинге 26 во время выполнения метода makeError() генерируется исключение, передаваемое в стек вызовов, который в данном случае является одноуровневым стеком. Поскольку для перехвата этого исключения нет соответствующей логики, возникнет ошибка и произойдет останов сценария:

 

Fatal error: Uncaught exception 'exception' with message 'This is an example
Exception' in /listing/26.php:5
Неисправимая ошибка: Неперехваченное исключение 'exception' с сообщением
'Пример исключения' в /listing/26.php:5
Stack trace:
#0 /listing/26.php(9): ThrowExample->makeError()
#1 {main}
thrown in /listing/26.php on line 5

 

Как видите, исключения формируют гораздо более информативные сообщения об ошибках, чем доступные в версии PHP 4. Однако даже с учетом такой степени информативности лучше всего исправлять ошибки незаметно для пользователя. Чтобы сделать это с помощью исключений, используйте новую конструкцию PHP 5 — блок try/catch.
Блок try/catch служит для выполнения сегмента кода, который может генерировать исключение и вернуться к выполнению кода. Синтаксис блока try/catch выглядит следующим образом:

 

try {
    /* код для выполнения */
} catch( ExceptionClass $variable ) {
    /* Код для перехвата исключения */
} [ catch ( AnotherException $variable ) {
} ] ...

 

Обратите внимание, что ExceptionClass и $variable — это просто заполнители. На самом деле ExceptionClass может быть выражен любым классом, унаследованным от класса Exception. Если в PHP-сценарии используется блок try/catch, будут выполнены следующие действия:

  1. Выполняется код в части try блока.
  2. Если в блоке try не было обнаружено ни одного исключения, выполнение сценария продолжается за пределами блока try.
  3. Если возникло исключение, класс исключения сравнивается с исключениями, которые были перехвачены в блоке try.
  4. Если блок try перехватывает сгенерированное исключение, выполняется код в блоке catch, в котором была перехвачена ошибка.
  5. Если блок try не перехватывает исключение, оно помещается в стек вызовов.

При перехвате исключения сгенерированный объект исключения присваивается переменной, имя которой указано в блоке перехвата (в предыдущем синтаксисе — переменная $variable).
В завершение анализа исключений рассмотрим код в листинге 27, основанный на примере из листинга 26.

 

Листинг 27

class MyException extends Exception {
    /* Никакого другого нового кода не нужно,
    просто новый тип исключения */
}
class AnotherException extends Exception {
    /* Класс оболочки для определения другого
    типа исключения */
}
class ThrowExample {
    public function makeMyException() {
        throw new MyException();
    }
    public function makeAnotherException() {
        throw new AnotherException();
    }
    public function makeError() {
        throw new Exception();
    }
}
$inst = new ThrowExample();
try {
    $inst->makeMyException();
} catch( MyException $e ) {
    echo "Перехвачено 'MyException'\n";
}
try {
    $inst->makeAnotherException();
} catch( MyException $e ) {
    echo "Перехвачено 'MyException'\n";
} catch( AnotherException $e ) {
    echo "Caught 'AnotherException'\n";
}
try {
    $inst->makeError();
} catch( MyException $e ) {
    echo "Перехвачено 'MyException'\n";
} catch(AnotherException $e) {
    echo "Перехвачено 'AnotherException'\n";
}

 

Несмотря на большой объем кода, в листинге 27 представлено несколько примеров использования блоков try/catch вместе с исключениями. В этом примере создан простой класс ThrowExample, который генерирует три разновидности класса Exception: собственно класс Exception, класс MyException и класс AnotherException. Затем создаётся экземпляр класса ThrowExample и генерируются все три исключения, используя три отдельных блока try/catch.

При выполнении этого сценария выполняется код в блоке try, генерирующий исключение с типом MyException. Поскольку это исключение перехватывается в блоке try вызова функции, выполняется код в блоке catch. Во втором блоке try процесс повторяется, но на этот раз генерируется исключение AnotherException.

В этом случае определены два типа исключений, которые будут перехватываться, поэтому оба исключения сравниваются со сгенерированным исключением. Так как блок try действительно перехватывает исключение AnotherException, то выполняется его блок catch.

Для третьего блока try генерируется исключение, которое не перехватывается ни одним из блоков catch. Хотя класс Exception является родительским классом по отношению к классам MyException и AnotherException, он не является таким же специфическим классом, поэтому соответствие не может быть установлено и исключение передается в сценарий, что приводит к возникновению ошибки, останавливающей выполнение сценария. Если выполнить код из листинга 27, будет сгенерирован следующий вывод:

 

Перехвачено 'MyException'
Перехвачено 'AnotherException'
Fatal error:  Uncaught exception 'Exception' in /listing/27.php:19
Stack trace:
#0 /listing/27.php(36): ThrowExample->makeError()
#1 {main}
  thrown in /listing/27.php on line 19

 

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

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

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