Смекни!
smekni.com

Обработка ошибок в коде программ РНР (стр. 4 из 8)

● $type = = 0

Записывает сообщение в системный файл журнала или в файл, заданный в директиве error_log.

● $type = = 1

Отправляет сообщение по почте адресату, чей адрес указан в $dest. При этом $extra_headers используется в качестве дополнительных почтовых заголовков.

● $type == 3

Сообщение добавляется в конец файла с именем $dest.

2.5 СТЕК ВЫЗОВОВ ФУНКЦИЙ

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

Функция

list debug_backtrace()

возвращает большой список, в котором содержится информация о "родительских" функциях и их аргументах. Результат работы листинга 2.4 говорит сам за себя.

Листинг2.4. Файл trace.php

<?php ## Вывод дерева вызовов функции.

function inner($a) {

// Внутренняяфункция.

echo "<pre>"; print_r(debug_backtrace()); echo "</pre>";

}

function outer($x) {

// Родительская функция.

inner($x*$x);

}

// Главная программа.

outer(3);

?>

После запуска этого скрипта будет напечатан примерно следующий результат (его чуть сжали):

Array (

[0] => Array (

[file] => z:&bsol;home&bsol;book&bsol;original&bsol;src&bsol;interpreter&bsol;trace.php

[line] => 6

[function] => inner

[args] => Array ([0] => 9)

)

[1] => Array (

[file] => z:&bsol;home&bsol;book&bsol;original&bsol;src&bsol;interpreter&bsol;trace.php

[line] => 8

[function] => outer

[args] => Array ([0] => 3)

)

)

Как видите, в массиве оказалась все информация о промежуточных вызовах функций.

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

2.6 ПРИНУДИТЕЛЬНОЕ ЗАВЕРШЕНИЕ ПРОГРАММЫ

Функция

void exit ()

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

void die(string $message)

Функция делает почти то же самое, что и exit (), только перед завершением работы выводит строку, заданную в параметре $message). Чаще всего ее применяют, если нужно напечатать сообщение об ошибке и аварийно завершить программу.

Полезным примером использования die () может служить такой код:

$filename = '/path/to/data-file';

$file = @fopen($filename, 'r')

or die("не могу открыть файл $ filename!");

Здесь мы ориентируемся на специфику оператора or — "выполнять" второй операнд только тогда, когда первый "ложен". Заметьте, что оператор || здесь применять нельзя — он имеет более высокий приоритет, чем =.

С использованием || последний пример нужно было бы переписать следующим образом:

$filename = '/path/to/data-file';

($file = fopen($filename, 'r'))

|| die("немогуоткрытьфайл $filename!");

Согласитесь, громоздкость последнего примера практически полностью исключает возможность применения || в подобных конструкциях.

2.7 ФИНАЛИЗАТОРЫ

Разработчики РНР предусмотрели возможность указать в программе функцию-финализатор, которая будет автоматически вызвана, как только работа сценария завершится — неважно, из-за ошибки или легально. В такой функции мы можем, например, записать информацию в кэш или обновить какой-нибудь файл журнала работы программы. Что же нужно для этого сделать?

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

Во-вторых, зарегистрировать ее как финализатор, передав ее имя стандартной функции register_shutdown_function().

int register_shutdown_function(string $func)

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

Конечно, можно зарегистрировать несколько финальных функций, которые будут вызываться в том же порядке, в котором они регистрировались.

Правда, есть одно "но". Финальная функция вызывается уже после закрытия соединения с браузером клиента. Поэтому все данные, выведенные в ней через echo, теряются (во всяком случае, так происходит в Unix-версии РНР, а под Windows CGI-версия РНР и echo работают прекрасно). Так что лучше не выводить никаких данных в такой функции, а ограничиться работой с файлами и другими вызовами, которые ничего не направляют в браузер.

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


3. ПЕРЕХВАТ ОШИБОК. МЕТОД ИСКЛЮЧЕНИЙ

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

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

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

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

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

Любое исключение в программе представляет собой объект некоторого класса, создаваемый, как обычно, оператором 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).

3.2 ИНСТРУКЦИЯ throw

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

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

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

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

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

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

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


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

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

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

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

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

try {

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