Русские документы
Ежедневные компьютерные новости RSS rusdoc.ru  Найти :
Новости
Последние поступления
Книжный магазин
  Hardware:
Видеоустройства
Системные платы
Процессоры
Мобильные устройства
Аудиосистема
Охлаждение системы
Накопители информации
КПК и ноутбуки
Телефоны и связь
Периферия
Система
Сети
Разные устройства
 
  Programming:
Web-разработка
Языки программирования
Технологии и теория
Разработка игр
Программная инженерия
 
  Software:
Операционные системы
Windows 7
Базы данных
Обзоры программ
Графика и дизайн
   
  Life:
Компьютерная жизнь
Разные материалы
   
Партнеры
Публикация
Правовая информация
Реклама на сайте
Обратная связь
Экспорт в RSS Экспорт в RSS2.0
    Читать в Яндекс.Ленте



Обработка ошибок и исключений в PHP

Раздел: Programming / PHP @ 31.07.2008 | Ключевые слова: php exception исключение ошибка error версия для печати

Автор: Виктор
Источник: habrahabr

Эта «небольшая» статейка является развитием темы затронутой в этой статье.
Как известно, PHP зародился довольно давно и уже тогда возник вопрос, что делать с возникающими ошибками. Perl, который является несомненным прародителем PHP по умолчанию не имел какой-либо системы обработки ошибок. При возникновении любой ошибки сервер выбрасывал 500-ю ошибку и на этом все заканчивалось. Поэтому Warnings, Fatal Errors и Notices были настоящим прорывом в облегчении и без того нелегкого труда программиста. Однако время шло, механизмы PHP не менялись, а технологии, как известно, на месте стоять не любят.

И вот в PHP 5.0, наконец-то, в арсенале программистов появилось такое мощное средство как исключение или Exception. Достоинств у Exception много, опишу лишь некоторые (возможно, я выражаюсь неточно или даже безграмотно, но мне было просто лень выискивать научные термины для описания преимуществ, потому описаны они «своими словами»):

  • Сквозная генерация. Это означает, что возникновение исключения где либо в коде будет приводит к последовательному выходу из управляющих конструкций и функций до первого блока catch либо до функции main (с выдачей соответствующей ошибки в поток) основного скрипта
  • Возможность переопределения основного класса Exception через наследование
  • Возможность обработки нескольких типов исключений одновременно

Возможности обработки стандартных ошибок PHP крайне ограничены:

  • Можно заблокировать при помощи @
  • Можно установить свой обработчик при помощи set_error_handler
  • Можно сгенерировать свою ошибку при помощи trigger_error

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

Основная идея: ставим свой обработчик для стандартных ошибок и "бросаем" исключение в нем:

  1. <?php
  2. class MyException extends Exception {
  3.    public function __construct($message, $errorLevel = 0, $errorFile = ``, $errorLine = 0) {
  4.       parent::__construct($message, $errorLevel);
  5.       $this->file = $errorFile;
  6.       $this->line = $errorLine;
  7.    }
  8. }
  9. set_error_handler(create_function(`$c, $m, $f, $l`, `throw new MyException($m, $c, $f, $l);`), E_ALL);
  10. ?>
* This source code was highlighted with Source Code Highlighter.

Этот код необходимо вынести в отдельный файл и подключать его только один раз. Класс MyException расширяет стандартный класс Exception добавлением двух дополнительных параметров в конструктор: файла и номера строки с ошибкой.

Функция set_error_handler устанавливает в качестве обработчика ошибок динамически созданную lambda-функцию (callback), которая и генерирует исключение в случае возникновения ошибки. Особенно прошу обратить внимание на второй параметр функции set_error_handler. Этот параметр очень важен, так как он определяет для каких типов ошибок будет вызываться пользовательский (то есть наш) обработчик, а для каких стандартный. В данном примере, я установил значение E_ALL, что означает, что обработчик будет вызываться для всех типов ошибок.

Если мы не хотим обрабатывать некоторые типы ошибок, например, Notice, то мы можем запросто указать это:

set_error_handler(create_function(`$c, $m, $f, $l`, `throw new MyException($m, $c, $f, $l);`), E_ALL & ~E_NOTICE); * This source code was highlighted with Source Code Highlighter.

Однако идеальным подходом, как мне кажется будет все таки обработка всех ошибок, но для некоторых типов, в частности, notice, было бы целесообразно не выкидывать exception, а просто выводить информацию на экран:

set_error_handler(create_function(`$c, $m, $f, $l`, `if ($c === E_NOTICE) {echo `This is notice: `.$m} else {throw new MyException($m, $c, $f, $l);}`), E_ALL); * This source code was highlighted with Source Code Highlighter.

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

Сложности здесь собственно две:

  1. Выводить все ошибки одновременно, а не по одной
  2. Отделить обработку ошибок валидации от обработки прочих исключений

Решение:

Главной сложностью здесь для нас будет то самое пресловутое преимущество Exception, которое заключается в том, что при "бросании" исключения происходит выход из управляющих конструкций до первого блока catch (или до конца скрипта). Для того, чтобы обойти этот подводный камень определим новый класс-потомок FormFieldsListException, в котором реализуем механизм накопления ошибок, а "бросать" исключение будем только после валидации всех полей. В классе FormFieldsListException определяем защищенный (protected) член $_list, в котором будем хранить данные. Для упрощения работы с массивом $_list указываем, что класс будет реализовывать два интерфейса: ArrayAccess для доступа к элементам массива и Iterator для работы в цикле. При инициализации метода проверки валидации создаем объект FormFieldsListException, а затем по мере определения ошибок добавляем их в объект FormFieldsListException, как в обычный массив.

  1. <?php
  2. class FormFieldsListException extends Exception implements ArrayAccess, Iterator {
  3.   protected $_list = array();
  4.   
  5.   public function __construct() {
  6.   }
  7.   
  8.   public function offsetExists($index) {
  9.     return isset($this->_list[$index]);
  10.   }
  11.   
  12.   public function offsetGet($index) {
  13.     return $this->_list[$index];
  14.   }
  15.   
  16.   public function offsetSet($index, $value) {
  17.     if (isset($index)) {
  18.       $this->_list[$index] = $value;
  19.     }
  20.     else {
  21.       $this->_list[] = $value;
  22.     }
  23.   }
  24.   
  25.   public function offsetUnset($index) {
  26.     unset($this->_list[$index]);
  27.   }
  28.   
  29.   public function current() {
  30.     return current($this->_list);
  31.   }
  32.   
  33.   public function key() {
  34.     return key($this->_list);
  35.   }
  36.   
  37.   public function next() {
  38.     return next($this->_list);
  39.   }
  40.   
  41.   public function rewind() {
  42.     return reset($this->_list);
  43.   }
  44.   
  45.   public function valid() {
  46.     return (bool) $this->current();
  47.   }
  48. }
  49. ?>
* This source code was highlighted with Source Code Highlighter.

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

Для отлова исключения используем два блока catch: для FormFieldsListException и для всех остальных исключений. Это позволяет задать различные виды действий при возникновении различных типов исключений.

  1. <?php
  2. function validateForm() {
  3.    $e = new FormFieldsListException();
  4.    if ($errorInFirstField) {
  5.       $e[] = `Error in first field`;
  6.    }
  7.    if ($errorInSecondField) {
  8.       $e[] = `Error in second field`;
  9.    }
  10.    if ((bool)$e->current()) {
  11.       throw $e;
  12.    }
  13. }
  14.  
  15. try {
  16.    validateForm();
  17. }
  18. catch (FormFieldsListException $error) {
  19.    echo `<b>Errors in the fields</b>:<br />`;
  20.    foreach ($error as $e) {
  21.       echo $e.`<br />`;
  22.    }
  23. }
  24. catch (Exception $error) {
  25.    echo `Not validation error! `.$error->getMessage();
  26. }
  27. ?>
* This source code was highlighted with Source Code Highlighter.

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

P.S.: Некоторые из программистов, которым я показывал данную статью, считают использование исключений для валидации форм, мягко говоря, не самым лучшим вариантом (кстати, я бы попросил читателей, которые "в теме" высказаться по этому поводу), поэтому прошу считать приведенный пример всего лишь учебным примером, а не руководством к действию.

P.P.S.: Огромное спасибо товарищу посмотреть профиль ashofthedream, спор с которым и натолкнул меня на мысль изучить исключения поподробнее.

Это интересно:








версия для печатиРаспечатать статью


Вернуться в раздел: Programming / PHP


Реклама:
Читать наc на:

Add to Google
Читать в Яндекс.Ленте






Rambler's Top100
© Copyright 1998-2012 Александр Томов. All rights reserved.