Название реферата: Обработка ошибок в коде программ РНР
Раздел: Программирование, компьютеры и кибернетика
Скачано с сайта: www.refsru.com
Дата размещения: 28.10.2012

Обработка ошибок в коде программ РНР

Содержание

ВВЕДЕНИЕ

1. КОНТРОЛЬ ОШИБОК

1.1 РОЛИ ОШИБОК

1.2 ВИДЫ ОШИБОК

1.2.1 НЕСЕРЬЕЗНЫЕ ОШИБКИ

1.2.2 СЕРЬЕЗНЫЕ ОШИБКИ

1.2.2.1 Прекращение выполнения программы

1.2.2.2Возврат недопустимого значения

1.2.2.3 Ненормальное состояние программы

1.2.2.4 Вызов функции-обработчика

1.3 ДИРЕКТИВЫ РНР КОНТРОЛЯ ОШИБОК

1.3.1 ДИРЕКТИВА error_reporting

1.3.2 ДИРЕКТИВА display_errors

1.3.3 ДИРЕКТИВА error_log

1.4 УСТАНОВКА РЕЖИМА ВЫВОДА ОШИБОК

1.5 ОПЕРАТОР ОТКЛЮЧЕНИЯ ОШИБОК

1.5.1 ПРИМЕР ИСПОЛЬЗОВАНИЯ ОПЕРАТОРА @

1.5.2 ПРЕДОСТЕРИЖЕНИЯ ПО ПРИМЕНЕНИЮ ОПЕРАТОРА ОТКЛЮЧЕНИЯ ОШИБОК @

2 ПЕРЕХВАТ ОШИБОК. МЕТОД РЕГИСТРАЦИИ ОБРАБОТЧИКА ОШИБОК

2.1 ФУНКЦИЯ set_error_handler

2.2 ФУНКЦИЯ restore_error_handler()

2.3 ПРОБЛЕМЫ С ОПЕРАТОРОМ @

2.4 ГЕНЕРАЦИЯ ОШИБОК

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

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

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

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

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

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

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

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

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

3.6 КЛАССИФИКАЦИЯ И НАСЛЕДОВАНИЕ

3.7 БАЗОВЫЙ КЛАСС Exception

3.8 ИСПОЛЬЗОВАНИЕ ИНТЕРФЕЙСОВ

3.9 БЛОКИ-ФИНАЛИЗАТОРЫ

3.9.1 Неподдерживаемая конструкция try .finally

3.9.2 "Выделение ресурса есть инициализация"

3.9.3 Перехват всех исключений

3.10 ТРАНСФОРМАЦИЯ ОШИБОК

3.10.1 Серьезность "несерьезных" ошибок

3.10.2 Преобразование ошибок в исключения

3.10.3 Код библиотеки PHP_Exceptionizer

3.10.4 Иерархия исключений

3.10.5 Фильтрация по типам ошибок

3.10.6 Перспективы

ЗАКЛЮЧЕНИЕ

ЛИТЕРАТУРА

ВВЕДЕНИЕ

Имеется мнение: "В любой программе есть хотя бы одна ошибка". На практике "хотя бы одна" означает "много" или даже "очень много".

Фаза "избавления" программы от ошибок (фаза отладки) является наиболее длительной и трудоемкой. Основное времяпровождение программиста (и не только) — это борьба с ошибками.

Одна из самых сильных черт РНР — возможность отображения сообщений об ошибках прямо в браузере. В зависимости от состояния интерпретатора сообщения будут выводиться в браузер или подавляться.

Для успешной борьбы с ошибками нужно научиться управлять настройками РНР, узнать о его тонких местах и о возможностях основных директив. Отдельное внимание следует уделять методикам отладки скриптов, а точнее — обработке сообщений об ошибках и предупреждений, которые могут возникнуть во время работы программы, а также выводу стека вызовов процедур (подобного тому, что существует в языках Java и Perl). Следует с осторожностью использовать оператор отключения предупреждений об ошибках.

Задача обработки ошибок в коде программы - одна из самых важных и популярных при программировании. Для ее успешного решения требуется уточнить понятие термина "ошибка" и определить его роль в программировании, а также изучить различные классификации ошибочных ситуаций. Эта задача может быть эффективно решена при использовании понятия "исключение" и способов применения конструкции try .catch. Использование механизма наследования и классификации исключений может сильно сократить код программы и сделать его универсальным. Существуют коды библиотек, позволяющих обрабатывать многочисленные ошибки и предупреждения, генерируемые функциями РНР, как обыкновенные исключения.

Грамотный перехват ошибок с самого зарождения программирования считался трудной задачей. Механизм обработки исключений, хотя и упрощает ее, но все равно остается весьма сложным.

1. КОНТРОЛЬ ОШИБОК

Термин "ошибка" имеет три различных значений:

1. Ошибочная ситуация — факт наличия ошибки в программе. Это может быть, например, синтаксическая ошибка (пропущенная скобка), или же ошибка семантическая — смысловая (использование переменной, которая ранее не была определена).

2. Внутреннее сообщение об ошибке ("внутренняя ошибка"), которую выдает РНР в ответ на различные неверные действия программы (например, открытие несуществующего файла).

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

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

1.1 РОЛИ ОШИБОК

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

Для записи сообщений об ошибках в журнал в РНР существуют специальные средства: директивы log errors, error log, а также функция error log () (подробнее см.п.п. 1.3.2, 1.3.3).

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

● внутреннее сообщение: ответ SQL-сервера, дата и время ошибки, номер строки в программе и т. д.;

● пользовательское сообщение: например, текст "Ошибка соединения с SQL-сервером, попробуйте зайти позже".

1.2 ВИДЫ ОШИБОК

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

$f = @fopen("spoon.txt", "r");

if (!$f) return;

В этом примере код восстановления — это инструкция if, которая явно обрабатывает ситуацию невозможности открытия файла. Обратите внимание, что используется оператор @ перед fopen(), чтобы не получать диагностическое сообщение от самого РНР — оно не нужно, у нас же собственный обработчик ошибочной ситуации (код восстановления).

В данной терминологии диагностические сообщения, которые выдает РНР, также можно назвать кодом восстановления.

Ошибки по своей "серьезности" можно подразделить на два больших класса:

● серьезные ошибки с невозможностью автоматического восстановления. Например, если вы пытаетесь открыть несуществующий файл, то далее обязательно должны указать, что делать, если это не удастся: ведь записывать или считывать данные из неоткрытого файла нельзя;

● несерьезные (нефатальные) ошибки, восстановление после которых не требуется, например, предупреждения (warnings), уведомления (notices), а также отладочные сообщения (debug notices). Обычно в случае возникновения такого рода ошибочных ситуаций нет необходимости предпринимать что-то особенное и нестандартное, вполне достаточно просто сохранить где-нибудь информацию об ошибке (например, в файле журнала).

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

1.2.1 НЕСЕРЬЕЗНЫЕ ОШИБКИ

Для обработки нефатальных ошибок, после которых не требуется "персональное" восстановление, в РНР имеется инструмент, называемый установкой обработчика ошибок (или перехватом ошибок; подробнее см. п. 2).

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

1.2.2 СЕРЬЕЗНЫЕ ОШИБКИ

Серьезные ошибки в общем случае невозможно обработать с использованием set_error_handler(), потому что в каждом конкретном случае нужно писать "персональный" код восстановления.

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

Главный вопрос при работе с серьезными ошибками — написание кода восстановления. Он должен иметь достаточный контроль над ходом выполнения программы (например, мог выполнять инструкции return или break, а не только лишь завершал программу по exit ()).

1.2.2.1 Прекращение выполнения программы

В случае возникновения серьезной ошибки программа завершает работу по exit() или die().

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

1.2.2.2 Возврат недопустимого значения

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

Этот метод имеет три недостатка.

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

● Любое значение, возвращаемое функцией, может быть по смыслу допустимым. Например, стандартная функция unserialize() распаковывает некоторую переменную из ее строкового представления (сгенерированного вызовом serialize()) и возвращает ее исходное значение. Что должна вернуть функция в случае ошибки? Если, например, NULL, то где гарантия, что действительно произошла ошибка, а исходная переменная не содержала просто значение NULL до упаковки?

● Код восстановления приходится постоянно дублировать. Например, если в программе нужно открыть 10 разных файлов, мы будем вынуждены 10 раз проверить возвращаемое функцией fopen() значение. Легко представить, как сильно это "раздует" программу. Еще хуже вы себя почувствуете, если вспомните, что в будущем может понадобиться модифицировать код восстановления: придется делать это в 10 местах.

1.2.2.3 Ненормальное состояние программы

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

Так работают, например, инструменты для доступа к MySQL в PHP: mysql_connect(), mysql_query() и т. д. Вы можете проверить, не вернули ли функции "ложное" значение, а потом использовать mysql_error() или mysql_errno() для получения дополнительной информации. Состояние программы, в котором когда-то ранее произошла ошибка, требующая дополнительного анализа, называют ненормальным. Главный недостаток ненормального состояния в том, что если вдруг произойдет еще одна ошибка, то информация о ней "затрет" предыдущее сообщение. Таким образом, мы вынуждены периодически проверять, не находится ли программа в ненормальном состоянии, и предпринимать дополнительные действия. Это сильно увеличивает код, кроме того, программист может забыть вставить в программу требуемые проверки.

1.2.2.4 Вызов функции-обработчика

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

1.3 ДИРЕКТИВЫ РНР КОНТРОЛЯ ОШИБОК

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

1.3.1 ДИРЕКТИВА error_reporting

error_reporting

● Возможные значения: числовая константа (по умолчанию — E_ALL~E_NOTICE.

● Где устанавливается: php.ini, .htaccess, ini_set ().

Устанавливает "уровень строгости" для системы контроля ошибок РНР. Значение этого параметра должно быть целым числом, которое интерпретируется как десятичное представление двоичной битовой маски. Установленные в 1 биты задают, насколько детальным должен быть контроль. Можно также не возиться с битами, а использовать константы. В табл. 1.1 приведены некоторые константы, которые на практике применяются чаще всего.

Таблица 1.1. Биты, управляющие контролем ошибок

Бит

Константа PHP

Назначение

1

E_ERROR

Фатальные ошибки

2

E_WARNING

Общие предупреждения

4

E_PARSE

Ошибки трансляции

8

E_NOTICE

Предупреждения

16

E_CORRE_ ERROR

Глобальные предупреждения (почти не используется)

32

E_CORRE_STRING

Глобальные ошибки (не используется)

2048

E_STRICT

Различные "рекомендации" РНР по улучшению кода (на­пример, замечания насчет вызова устаревших функций)

2047

E_ALL

Все перечисленные флаги, за исключением E_STRICT

Чаще всего встречается значение 7 (1+2 + 4), или, что то же самое, E_ALL ~ E_NOTICE (здесь оператор ~ означает побитовое "исключающее ИЛИ"). Оно задает полный контроль, кроме некритичных предупреждений интерпретатора (таких, например, как обращение к неинициализированной переменной). Часто это значение задается по умолчанию при установке РНР.

Если вы разрабатываете скрипты на РНР, первое, что вам стоит сделать, — это устанавливать значение error_reporting равным E_ALL или даже E_ALL | E_STRICT, т.е. включить абсолютно все сообщения об ошибках.

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

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

Лучше всего держать в файле php.ini максимально возможный режим контроля ошибок —E_ALL или даже E_ALL E_STRICT, т.е. включить абсолютно все сообщения об ошибках, а в случае крайней необходимости отключать его в скриптах в персональном порядке. Для этого существует три способа.

Способы отключения режима контроля ошибок:

● использовать функцию error_reporting(E_ALL~E_NOTICE);

● запустить функцию ini_set("error_reporting", E_ALL~E_NOTICE);

● использовать оператор @ для локального отключения ошибок.

1.3.2 ДИРЕКТИВА display_errors

display_errors

log_errors

● Возможные значения: on или off.

● Где устанавливается: php.ini, .htaccess, iniseto.

Если директива display_errors установлена в значение on, все сообщения об ошибках и предупреждения выводятся в браузер пользователя, запустившего скрипт.

Если же установлен параметр log_errors, то сообщения дополнительно попадают и в файл журнала (см. ниже директиву error_log).

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

1.3.3 ДИРЕКТИВА error_log

error_log

● Возможные значения: абсолютный путь к файлу (по умолчанию — не задан).

● Где устанавливается: php.ini, .htaccess, ini_set().

В PHP существуют два метода вывода сообщений об ошибках: печать ошибок в браузер и запись их в файл журнала (log-файл). Директива error_log задает путь к журналу.

1.4 УСТАНОВКА РЕЖИМА ВЫВОДА ОШИБОК

Для установки режима вывода ошибок во время работы программы служит функция error_reporting().

int error_reporting([int $level])

Эта функция устанавливает "уровень строгости" для системы контроля ошибок РНР, т. е. величину параметра error_reporting в конфигурации РНР.

Рекомендуется первой строкой сценария ставить вызов:

error_reporting(E_ALL);

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

1.5 ОПЕРАТОР ОТКЛЮЧЕНИЯ ОШИБОК

Есть и еще один аргумент в пользу того, чтобы всегда включать полный контроль ошибок. Это — существование в РНР оператора @. Если этот оператор поставить перед любым выражением, то все ошибки, которые там возникнут, будут проигнорированы.

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

Например:

if (!@filemtime("notextst.txt") )

echo "Файл не существует!";

Попробуйте убрать оператор @ — тут же получите сообщение: "Файл не найден", а только после этого — вывод оператора echo. Однако с оператором @ предупреждение будет подавлено, что и требовалось.

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

//Обновить файл, если он не существует или очень старый

if ( ! file_exists($fname) | | filemtime ($fname) <time () -60*60)

myFunctionForUpdateFile($fname);

Сравните со следующим фрагментом:

// Обновить файл, если он не существует или очень старый

if (@filemtime($fname)<time()-60*60)

myFunctionForUpdateFile($fname);

Всегда помните об операторе @. Он удобен. Рекомендуется не рисковать, задавая слабый контроль ошибок при помощи функции error_reporting(), если такой контроль и так можно локально установить при помощи оператора @?

Оператор отключения ошибок @ ценен еще и тем, что он блокирует не только вывод ошибок в браузер, но также и в log-файл. Пример из листинга 1.1 иллюстрирует ситуацию.

Листинг 1.1. Файл er.php

<?php ## Отключение ошибок: логи не модифицируются.

error_reporting(E_ALL);

ini_set("error_log", "log.txt");

ini_set("log_errors", true);

@filemtime("spoon");

?>

Запустив приведенный скрипт, вы заметите, что файл журнала log.txt даже не создался. Попробуйте теперь убрать оператор @ — вы- получите предупреждение "stat failed for spoon", и оно же запишется в log.txt.

1.5.1 ПРИМЕР ИСПОЛЬЗОВАНИЯ ОПЕРАТОРА @

Пусть имеется форма с submit-кнопкой, и нужно в сценарии определить, нажата ли она. Можно сделать это так:

<?php

if (!empty($submit)) echo "Кнопка нажата!";

?>

Согласитесь, код листинга 1.2 элегантнее.

Листинг 1.2. Файл submit.php

<?php ## Удобство оператора @.

if (@$_REQUEST['submit']) echo "Кнопка нажата!"

?>

<form action="<?=$_SERVER['SCRIPT_NAME']?>">

<input type="submit" name="submit" value="Go!">

</form>

1.5.2 ПРЕДОСТЕРИЖЕНИЯ ПО ПРИМЕНЕНИЮ ОПЕРАТОРА ОТКЛЮЧЕНИЯ ОШИБОК @

Оператор @ следует применять с осторожностью. Например, следующий код никуда не годится — постарайтесь не повторять его в своих программах.

// Не подавляйте сообщения об ошибках во включаемых файлах — иначе

// отладка превратится в кромешный ад!

@include "mistake.php";

//Не используйте оператор @ перед функциями, написанными на РНР,

// если только нет 100%-й уверенности в том, что они работают

// корректно в любой ситуации!

@myOwnBigFunction() ;

Рекомендации, в каких случаях применение оператора подавления ошибок оправдано и относительно безопасно:

● в конструкциях if (@$_REQUEST[' key' ]) для проверки существования (и ненулевого значения) элемента массива;

● перед стандартными функциями РНР вроде fopen(), filemtime(), mysql_connect() и т. д., если далее идет проверка кода возврата и вывод сообщения об ошибке;

● в HTML-файлах со вставками PHP-кода, если очень лень писать много кавычек: <?=@$resuit [element] [field] ?> (такой вызов не породит ошибок, несмотря на отсутствие кавычек).

Во всех остальных случаях лучше несколько раз подумать, прежде чем применять оператор @. Чем меньше область кода, в которой он будет действовать, тем более надежной окажется программа. Поэтому не рекомендуется использовать @ перед include — это заблокирует проверку ошибок для очень большого фрагмента программы.

2. ПЕРЕХВАТ ОШИБОК. МЕТОД РЕГИСТРАЦИИ ОБРАБОТЧИКА ОШИБОК

В РНР версии 5 существуют два метода перехвата ошибок во время выполнения программы:

● регистрация обработчика ошибки.

● исключений;

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

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

Метод исключений, который рассмотрен в п. 3, лишен этого недостатка, он достаточно сложен и практически не реализован на уровне стандартных функций РНР.

Пример использования обработчика ошибок приведен в листинге 2.1.

Листинг 2.1. Файл handler1.php

<?php ## Перехват ошибок и предупреждений.

// Определяем новую функцию-обработчик.

function myErrorHandler($errno, $msg, $file, $line) {

// Если используется @, ничего не делать.

if (error_reporting() == 0) return;

// Иначе - выводим сообщение.

echo '<div >