Почему стоит использовать механизм обработки исключений в PHP::Журнал СА 2.2009
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Наука и технологии
Подписка
Где купить
Авторам
Рекламодателям
Архив номеров
Контакты
   

  Опросы
  Статьи

Электронный документооборот  

5 способов повысить безопасность электронной подписи

Область применения технологий электронной подписи с каждым годом расширяется. Все больше задач

 Читать далее...

Рынок труда  

Системные администраторы по-прежнему востребованы и незаменимы

Системные администраторы, практически, есть везде. Порой их не видно и не слышно,

 Читать далее...

Учебные центры  

Карьерные мечты нужно воплощать! А мы поможем

Школа Bell Integrator открывает свои двери для всех, кто хочет освоить перспективную

 Читать далее...

Гость номера  

Дмитрий Галов: «Нельзя сказать, что люди становятся доверчивее, скорее эволюционирует ландшафт киберугроз»

Использование мобильных устройств растет. А вместе с ними быстро растет количество мобильных

 Читать далее...

Прошу слова  

Твердая рука в бархатной перчатке: принципы soft skills

Лауреат Нобелевской премии, специалист по рынку труда, профессор Лондонской школы экономики Кристофер

 Читать далее...

1001 и 1 книга  
19.03.2018г.
Просмотров: 9890
Комментарии: 0
Потоковая обработка данных

 Читать далее...

19.03.2018г.
Просмотров: 8106
Комментарии: 0
Релевантный поиск с использованием Elasticsearch и Solr

 Читать далее...

19.03.2018г.
Просмотров: 8204
Комментарии: 0
Конкурентное программирование на SCALA

 Читать далее...

19.03.2018г.
Просмотров: 5195
Комментарии: 0
Машинное обучение с использованием библиотеки Н2О

 Читать далее...

12.03.2018г.
Просмотров: 5873
Комментарии: 0
Особенности киберпреступлений в России: инструменты нападения и защита информации

 Читать далее...

Друзья сайта  

 Почему стоит использовать механизм обработки исключений в PHP

Архив номеров / 2009 / Выпуск №2 (75) / Почему стоит использовать механизм обработки исключений в PHP

Рубрика: Программирование /  Программирование

 АНТОН ГРИШАН

Почему стоит использовать
механизм обработки исключений в PHP

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

Пятая версия PHP предлагает объектно-ориентированный подход к разработке веб-приложений. Одно из удобных новшеств – внедрение обработки исключительных ситуаций, подобно тому, как это реализовано во многих других объектно-ориентированных языках программирования, с помощью конструкции try-catch.

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

Пример 1: прекращение выполнения программы в случае возникновения ошибки

<?php

/* Метод осуществляет подключение к серверу MySQL, в случае ошибки завершает работу программы */

public function connect($dbName, $host, $port, $login, $password, $charset) {

.......

$db = new mysqli($host, $login, $password, $dbName, $port);

// Проверяем, возникла ли ошибка в ходе подключения к БД

if(($errorCode = mysqli_connect_errno()) != 0) {

die("Can't connect to DB: [".$errorCode."] ".mysqli_connect_error());

}

.......

// Устанавливаем кодировку, которую будем использовать для общения с БД

if(!$db->set_charset($charset)) {

// Установить нужную кодировку не удалось

die("Сan't set charset [".$charset."]");

}

.......

}

?>

 Первый способ реакции на нестандартную ситуацию – вывести сообщение об ошибке и прекратить работу приложения. Такой подход имеет два существенных недостатка:

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

Пример 2: возврат специального значения

<?php

/* Метод осуществляет подключение к серверу MySQL, в случае ошибки возвращает FALSE */

public function connect($dbName, $host, $port, $login, $password, $charset) {

.......

$db = new mysqli($host, $login, $password, $dbName, $port);

// Проверяем, возникла ли ошибка в ходе подключения к БД

if(($errorCode = mysqli_connect_errno()) != 0) {

return(false);

}

.......

// Устанавливаем кодировку, которую будем использовать для общения с БД

if(!$db->set_charset($charset)) {

// Установить нужную кодировку не удалось

return(false);

}

.......

return(true);

}

?>

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

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

Пример 3: возврат кода ошибки

<?php

/* Метод осуществляет подключение к серверу MySQL, в случае ошибки, возвращает код ошибки */

public function connect($dbName, $host, $port, $login, $password, $charset) {

 

define('SUCCESS', 0);

define('ERROR_DB_CONNECTION', 1);

define('ERROR_DB_CHARSET', 2);

 

.......

$db = new mysqli($host, $login, $password, $dbName, $port);

// Проверяем, возникла ли ошибка в ходе подключения к БД

if(($errorCode = mysqli_connect_errno()) != 0) {

return(ERROR_DB_CONNECTION);

}

.......

// Устанавливаем кодировку, которую будем использовать для общения с БД

if(!$db->set_charset($charset)) {

// Установить нужную кодировку не удалось

return(ERROR_DB_CHARSET);

}

.......

return(SUCCESS);

}

?>

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

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

Пример 4: генерация исключения

<?php

/* Метод осуществляет подключение к серверу MySQL, в случае ошибки, бросает исключение */

public function connect($dbName, $host, $port, $login, $password, $charset) {

 

.......

$db = new mysqli($host, $login, $password, $dbName, $port);

// Проверяем, возникла ли ошибка в ходе подключения к БД

if(($errorCode = mysqli_connect_errno()) != 0) {

throw new Exception("Can't connect to DB: [".$errorCode."] ".mysqli_connect_error());

}

.......

// Устанавливаем кодировку, которую будем использовать для общения с БД

if(!$db->set_charset($charset)) {

// Установить нужную кодировку не удалось

throw new Exception("Сan't set charset [".$charset."]");

}

.......

}

?>

В случае возникновения ошибки метод генерирует исключение (объект класса Exception) и прерывает выполнение основного алгоритма с помощью оператора throw. Возникшее исключение должно быть перехвачено с помощью конструкции try-catch в вызывающем коде. Вызов метода, приведенного в примере 4, может выглядеть следующим образом:

<?php

try {

/* Внутри блока try находится код, исполнение которого

* может привести к возникновению исключения

*/

 

$db = new MyDBClass ();

 

// Код метода connect() см. в примере 4

$db-> connect('main_db', 'localhost', 3306, 'root', 'parol', 'utf8');

 

} catch (Exception $e) {

/* Здесь мы перехватываем и обрабатываем исключения,

* возникшие в ходе выполнения кода в блоке try

*/

echo 'Error: '. $e->getMessage().' Line: '. ?

$e->getLine();

}

 

?>

Данный подход свободен от недостатков предыдущих способов. Рассмотрим подробнее особенности его работы в следующих разделах.

Использование исключений: «за» и «против»

Рассмотрим критерии «за»:

  •  Упрощение логики работы приложения. Механизм обработки исключений предполагает, что функции необходимо возвращать значение, только если значение используется в дальнейших вычислениях. Отсутствие необходимости проверять значения на ошибки упрощает логику работы приложения и сокращает длину кода.
  •  Повышение читабельности кода. Механизм обработки исключений принуждает разделять алгоритм на основной, идущий в блоке try{…}, и код обработки ошибок, находящийся в блоке catch{…}. Такое разделение существенно повышает читабельность кода.
  •  Информативные сообщения об ошибках. Исключение позволяет четко локализовать возникшую ошибку и получить:
    •  текстовое описание ошибки;
    •  код ошибки;
    •  имя файла, в котором произошла ошибка;
    •  номер строки, где было сгенерировано исключение;
    •  стек вызова с указанием параметров вызванных функций.

Подробная информация об ошибке значительно упрощает процесс отладки и сопровождения приложения.

  •  Большая гибкость при выборе способа обработки ошибок. При написании класса разработчику не требуется думать о том, как должна быть обработана каждая из возможных ошибок, все, что необходимо – бросить исключение с подробным описанием проблемы. Способ обработки ошибки должен определить программист, использующий класс. В зависимости от ситуации реакция на одни и те же ошибки в разных приложениях может быть разной. Кроме того, можно создавать собственные классы исключений и выполнять определенные действия в момент возникновения чрезвычайной ситуации, например, записать информацию об ошибке в лог-файл или оповестить администратора по e-mail.
  •  Исключения невозможно игнорировать. Каждое исключение должно быть обработано в соответствующем месте программы, иначе возникает фатальная ошибка, приводящая к завершению работы всего приложения. Данное обстоятельство вынуждает программиста писать код обработки для всех возможных ошибок, что положительно влияет на качество разрабатываемого программного продукта.

 Критерии «против»:

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

Работа с исключениями в PHP

Работа с исключениями в PHP появилась начиная с пятой версии. В обработке исключений участвуют три компонента.

Объект класса Exception

Для описания исключительной ситуации в PHP существует класс – Exception. Конструктор класса имеет два необязательных аргумента – сообщение об ошибке (строка) и код ошибки (число). Генерации исключения (т.е. создание объекта класса Exception) не приводит к остановке выполнения основного алгоритма. Рассмотрим несколько примеров создания объектов, описывающих исключительную ситуацию:

<?php

$error1 = new Exception();

$error2 = new Exception('File data.dat is not find!');

$error3 = new Exception('File data.dat is not find!', 1234);

?>

В момент создания объекта класса Exception PHP автоматически собирает дополнительную информацию, такую как имя файла, в котором создан объект, номер строки, стек вызова функций, и сохраняет эту информацию в свойствах объекта. Рассмотрим пример извлечения подробных данных об ошибке.

<?php

// Создадим объект Exception и попробуем извлечь максимум информации

$error = new Exception('Test PHP exceptions', 2009);

 

// Сообщение об ошибке

echo $error->getMessage();

 

// Код ошибки

echo $error->getCode();

 

// Файл, где произошла ошибка

echo $error->getFile();

 

// Номер строки, где было сгенерировано исключение

echo $error->getLine();

 

// Стек вызова функций до момента генерации исключения в виде строки

echo $error->getTraceAsString();

 

// Стек вызова функций до момента генерации исключения в виде массива

var_dump($error->getTrace());

?>

Подробное описание класса Exception можно найти в документации по PHP (см. http://ru2.php.net/manual/en/class.exception.php).

В некоторых ситуациях необходимо сгенерировать исключение не в том месте, где произошла ошибка. Для таких случаев существует встроенный класс ErrorException (потомок класса Exception), позволяющий самостоятельно задать имя файла, номер строки, уровень ошибки (см. http://ru2.php.net/manual/en/class.errorexception.php). Примером использования этого класса может служить код, конвертирующий внутренние PHP-ошибки в исключения. Основная идея – генерировать исключение внутри функции обработчика стандартных ошибок:

<?php

// Объявим функцию, которой PHP будет передавать данные о возникших ошибках

function exception_error_handler($errno, $errstr, $errfile, $errline ) {

throw new ErrorException($errstr, 0, $errno, $errfile, $errline);

}

// Назначим нашу функцию в качестве обработчика ошибок

set_error_handler("exception_error_handler");

 

// Попробуем вызвать функцию с неправильным количеством аргументов

strpos();

?>

Результатом работы скрипта будет генерация исключения типа ErrorException, содержащая описание возникшей проблемы (Wrong parameter count for strpos()).

Если в приведенном выше примере для документирования ошибки вместо класса ErrorException использовать Exception, то метод getFile() вернет имя файла, где объявлена функция – перехватчик ошибок, а getLine() – номер строки, отвечающей за генерацию исключения, т.е. всегда одни и те же значения. Для сохранения реальных данных об ошибке необходимо самостоятельно указать файл и номер строки, что позволяет сделать конструктор класса ErrorException.

Дополнительные типы исключений, такие как BadFunctionCallException, BadMethodCallException, DomainException, InvalidArgumentException, LengthException, LogicException, OutOfBoundsException, OutOfRangeException, OverflowException, RangeException, RuntimeException, UnderflowException, UnexpectedValueException, можно найти в библиотеке SPL (Standard PHP Library, см. http://ru.php.net/manual/en/spl.exceptions.php).

Оператор throw

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

Конструкция try-catch

Конструкция try-catch состоит из одного блока try и одного или более блоков catch. В блоке try располагается код, исполнение которого может привести к выбросу исключения, а в блоках catch код обработки ошибок. Пример:

<?php

try {

// Некая функция, исполнение которой может привести к генерации исключения

runMyFunction();

} catch (DataBaseException $e){

// Обработаем исключения, принадлежащие к классу DataBaseException

} catch (ErrorException $e){

// Обработаем исключения, принадлежащие к классу ErrorException

} catch (Exception $e){

// Обработаем исключения, принадлежащие Exception, т.е. любое исключение,

// не пойманное в предыдущих блоках catch

}

?>

Общие рекомендации по использованию исключений

  •  Используйте исключения только для действительно исключительных ситуаций. Если в результате возникновения ошибки продолжение исполнения основного алгоритма невозможно – возбуждение исключения является оправданным шагом.
  •  Максимально подробно описывайте произошедшую ошибку. Всегда указывайте максимально подробную информацию о произошедшей ошибке, если ошибка произошла из-за отсутствия файла, обязательно укажите полный путь к файлу, который не удалось обнаружить. Чем подробнее вы опишете возникшую ошибку, тем проще отлаживать и сопровождать приложение.
  •   Документируйте возможные исключения. Если метод в определенных ситуациях генерирует исключение, обязательно укажите это в документации, чтобы предупредить разработчиков о необходимости использования конструкции try-catch.
  •  Сохраняйте информацию об ошибках. Крайне полезным может оказаться сохранение информации об исключительных ситуациях в логах системы. В случае некорректной работы приложения данные записи помогут быстро локализовать и устранить причину.
  •  Избегайте пустых обработчиков ошибок. Отсутствие кода обработки в блоке catch означает игнорирование ошибки, что не всегда является правильной реакцией на ошибку.
  • Генерации исключения и обработка ошибок должны находиться на разных уровнях. Если код приложения выглядит следующим образом:

 <?php

define(DEFAULT_DB_CHARSET, ‘utf8’);

……

public function connect($dbName, $host, $port, $login, $password, $charset) {

 

$supportedCharset = array('cp1251', 'latin1', 'utf8');

// часть кода пропущена

try {

// Проверяем возможность использования кодировки, переданной в параметре

if(!in_array($charset, $supportedCharset)) {

// Кодировка, переданная в параметре $charset, не поддерживается

throw new Exception("Кодировка [".$charset."] не поддерживается");

}

} catch(Exception $e) {

// Используем стандартную кодировку

$charset = DEFAULT_DB_CHARSET;

}

}

?>

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

 <?php

define(DEFAULT_DB_CHARSET, ‘utf8’);

……

public function connect($dbName, $host, $port, $login, $password, $charset) {

 

// часть кода пропущена

 

$supportedCharset = array('cp1251', 'latin1', 'utf8');

 

// Проверяем возможность использования кодировки, переданной в параметре

if(!in_array($charset, $supportedCharset)) {

// Используем стандартную кодировку

$charset = DEFAULT_DB_CHARSET;

}

 

}

?>

Создание собственных исключений

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

Если для каждой подсистемы определить свой класс исключений (например DataBaseException, NetworkException, FileSystemException и т.д.), то очень просто назначить свой обработчик для каждого типа исключений.

PHP позволяет создать собственный класс для работы с исключениями путем наследования от базового класса – Exception (или ErrorException).

<?php

class DataBaseException extends Exception {

 

public function __construct($message, $code = 0) {

// Вызываем конструктор родительского класса

parent:: __construct($message, $code);

 

// Сообщаем о произошедшей ошибке

$this->report();

}

 

private function report(){

// посылаем письмо администратору

}

}

?>

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

  • текстовое описание ошибки – обязательный параметр (в стандартном классе этот параметр можно опустить);
  • в момент возникновения ошибки класс DataBase Exception вызывает метод report(), который должен отправлять письмо администратору или сохранять информацию об ошибке иным способом.

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

Каплю дегтя добавляет ухудшение переносимости кода. Например, если класс для работы с БД (например, DBaccess) генерирует исключение типа DataBaseException, то при необходимости перенести класс Dbaccess в другой проект, возникает необходимость переноса класса DataBaseException. Решение данной проблемы лежит в плоскости поиска разумного компромисса между удобством использования собственных исключений и переносимостью кода.

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

  1. Работа с исключениями в PHP5 – http://ru2.php.net/manual/en/class.exception.php.
  2. Общие сведения о механизме обработки исключений – http://ru.wikipedia.org/wiki/Обработка_исключений.
  3. Несколько примеров использования исключений – http://phpclub.ru/faq/PHP5/Exception.
  4. Дополнительные типы исключений (Standard PHP Library) – http://ru.php.net/manual/en/spl.exceptions.php.

Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru