ПЕРЕХВАТ ОШИБОК. МЕТОД ИСКЛЮЧЕНИЙ
Поможем в ✍️ написании учебной работы
Поможем с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой

 

Механизм обработки исключений или просто "исключения" (exceptions) — это технология, позволяющая писать код восстановления после серьезной ошибки в удобном для программиста виде. С применением исключений перехват и обработка ошибок, наиболее слабая часть в большинстве программных систем, значительно упрощается.

Концепция исключений базируется на общей идее объектно-ориентированного программирования: данные должны обрабатываться в том участке программы, который имеет максимум сведений о том, как это делать. Если в некотором месте еще не до конца известно, какое именно преобразование должно быть выполнено, лучше отложить работу "на потом". С использованием исключений код обработки ошибки явно отделяется от кода, в котором ошибка может возникнуть.

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

 

БАЗОВЫЙ СИНТАКСИС

 

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

Любое исключение в программе представляет собой объект некоторого класса, создаваемый, как обычно, оператором new. Этот объект может содержать различную информацию, например, текст диагностического сообщения, а также номер строки и имя файла, в которых произошла генерация исключения. Допустимо добавлять и любые другие параметры.

Прежде чем идти дальше, давайте рассмотрим простейший пример вызова обработчика (листинг 3.1). Заодно получим представление о синтаксисе исключений.

Листинг.3.1. Файл simple.php

<?php ## Простой пример использования исключений.

echo "Начало программы.<br>";

try {

 // Код, в котором перехватываются исключения.

 echo "Все, что имеет начало...<br>";

 // Генерируем ("выбрасываем") исключение.

 throw new Exception("Hello!");

 echo "...имеет и конец.<br>";

} catch (Exception $e) {

 // Код обработчика.

 echo " Исключение: {$e->getMessage()}<br>";

}

echo "Конец программы.<br>";

?>

В листинге 3.1 приведен пример базового синтаксиса конструкции try...catch, применяемой для работы с исключениями.

Рассмотрим эту инструкцию подробнее:

● Код обработчика исключения помещается в блок инструкции catch (в переводе с английского — "ловить").

● Блок try (в переводе с английского — "попытаться") используется для того, чтобы указать в программе область перехвата. Любые исключения, сгенерированные внутри нее (и только они), будут переданы соответствующему обработчику.

● Инструкция throw используется для генерации исключения. Генерацию также называют возбуждением или даже выбрасыванием (или "вбрасыванием") исключения (от англ. throw — бросать). Как было замечено ранее, любое исключение представляет собой обычный объект РНР, который мы и создаем в операторе new.

● Обратите внимание на аргумент блока catch. В нем указано, в какую переменную должен быть записан "пойманный" объект-исключение перед запуском кода обработчика. Также обязательно задается тип исключения — имя класса. Обработчик будет вызван только для тех объектов-исключений, которые совместимы с указанным типом (например, для объектов данного типа).

Работа инструкции try...catch заключается в том, что одна часть программы "бросает" (throw) исключение, а другая — его "ловит" (catch).

 

ИНСТРУКЦИЯ throw

 

Инструкция throw не просто генерирует объект-исключение и передает его обработчику блока catch. Она также немедленно завершает работу текущего try-блока. Именно поэтому результат работы сценария из листинга 3.1 выглядит так:

Начало программы.

Все, что имеет начало...

Исключение: Hello!

Конец программы.

Как видите, за счет особенности инструкции throw наша программа подвергает серьезному скепсису тезис "Все, что имеет начало, имеет и конец" — она просто не выводит окончание фразы.

В этом отношении инструкция throw очень похожа на инструкции return, break и continue: они тоже приводят к немедленному завершению работы текущей функции или итерации цикла.



РАСКРУТКА СТЕКА

 

Самой важной и полезной особенностью инструкции throw является то, что ее можно использовать не только непосредственно в try-блоке, но и внутри любой функции, которая оттуда вызывается. При этом производится выход не только из функции, содержащей throw, но также и из всех промежуточных процедур. Пример — в листинге 3.2.

Листинг3.2.Файл stack.php

<?php ## Инструкция try во вложенных функциях.

echo "Начало программы.<br>";

try {

 echo "Начало try-блока.<br>";

 outer();

 echo "Конец try-блока.<br>";

} catch (Exception $e) {

 echo " Исключение: {$e->getMessage()}<br>";

}

echo "Конец программы.<br>";

function outer() {

 echo "Вошли в функцию ".__METHOD__."<br>";

 inner();

 echo "Вышли из функции ".__METHOD__."<br>";

}

function inner() {

 echo "Вошли в функцию ".__METHOD__."<br>";

 throw new Exception("Hello!");

 echo "Вышли из функции ".__METHOD__."<br>";

}

?>

Результат работы данного кода выглядит так:

Начало программы.

Начало try-блока.

Вошли в функцию outer

Вошли в функцию inner

Исключение: Hello!

Конец программы.

Мы убеждаемся, что ни один из операторов echo, вызываемых после инструкции throw, не "сработал". По сути, программа даже не дошла до них: управление было мгновенно передано в catch-блок, а после этого — в следующую за try...catch строку программы.

Данное поведение инструкции throw называют раскруткой стека вызовов функций, потому что объект-исключение последовательно передается из одной функции в другую, каждый раз приводя к ее завершению — как бы "отматывает" стек.

Можно заметить, что инструкция throw очень похожа на команду return, однако она вызывает "вылет" потока исполнения не только из текущей функции, но также и из тех, которые ее вызвали (до ближайшего соответствующего catch-блока).

 

ИСКЛЮЧЕНИЯ И ДЕСТРУКТОРЫ

 

Деструктор любого объекта вызывается всякий раз, когда последняя ссылка на этот объект оказывается потерянной, например, программа выходит за границу области видимости переменной. Применительно к механизму обработки исключений это дает мощный инструмент — корректное уничтожение всех объектов, созданных до вызова throw. Листинг 3.3 иллюстрирует ситуацию.

Листинг 3.3. Файл destr.php

<?php ## Деструкторы и исключения.

// Класс, комментирующий операции со своим объектом.

class Orator {

 private $name;

 function __construct($name) {

 $this->name = $name;

 echo "Создан объект {$this->name}.<br>";

 }

 function __destruct() {

 echo "Уничтожен объект {$this->name}.<br>";

 }

}

function outer() {

 $obj = new Orator(__METHOD__);

 inner();

}

function inner() {

 $obj = new Orator(__METHOD__);

 echo "Внимание, вбрасывание!<br>";

 throw new Exception("Hello!");

}

// Основная программа.

echo "Начало программы.<br>";

try {

 echo "Начало try-блока.<br>";

 outer();

 echo "Конец try-блока.<br>";

} catch (Exception $e) {

 echo " Исключение: {$e->getMessage()}<br>";

}

echo "Конец программы.<br>";

?>

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

Результат работы программы выглядит так:

Начало программы.

Начало try-блока.

Создан объект outer.

Создан объект inner.

Внимание, вбрасывание!

Уничтожен объект inner.

Уничтожен объект outer.

Исключение: Hello!

Конец программы.

При вызове throw вначале произошел корректный выход из вложенных функций (с уничтожением всех локальных объектов и вызовом деструкторов), и уж только после этого запустился catch-обработчик. Данное поведение также называют раскруткой стека.

 

3.5 ИСКЛЮЧЕНИЯ И set_error_handler()

 

В п.2 рассматривали подход к обработке нефатальных ошибок, а именно установку функции-обработчика посредством вызова функции set_error_handler(). В РНР версии 4 он являлся единственно допустимым методом.

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

Сравним явно механизм обработки исключений и метод перехвата ошибок. Рассмотрим пример, похожий на скрипт из листинга 3.1, иллюстрирующий суть проблемы (листинг 3.4).

Листинг 3.4. Файл seh.php

<?php ## Недостатки set_error_handler().

echo "Начало программы.<br>";

set_error_handler("handler");

{

 // Код, в котором перехватываются исключения.

 echo "Все, что имеет начало...<br>";

 // Генерируем ("выбрасываем") исключение.

 trigger_error("Hello!");

 echo "...имеет и конец.<br>";

}

echo "Конец программы.<br>";

// Функция-обработчик.

function handler($num, $str) {

 // Код обработчика.

 echo "Ошибка: $str<br>";

// exit();

}

?>

Первое, что бросается в глаза, — это излишняя многословность кода. Но давайте пойдем дальше и посмотрим, какой результат выдает данная программа:

Начало программы.

Все, что имеет начало...

Ошибка: Hello!

За счет использования exit () в функции handler()новая программа не только подвергает сомнению известный тезис (см. операторы echo), но также и утверждает, что любая, даже малейшая, ошибка является фатальной.

Что ж, раз проблема в команде exit(), попробуем ее убрать из скрипта и увидим следующий результат:

Начало программы.

Все, что имеет начало...

Ошибка: Hello!

...имеет и конец.

Конец программы.

И снова мы получили не то, что нужно: ошибка теперь уже не является "чересчур фатальной", как раньше, у нее противоположная проблема: она, наоборот, недостаточно фатальна.

Мы-то хотели разрушать идиому о конечности всего, что имеет начало, а получили — просто робкое замечание, произнесенное шепотом из-за кулис.

 

Дата: 2019-05-28, просмотров: 168.