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

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

Событие  

В банке рассола ждет сисадмина с полей фрактал-кукумбер

Читайте впечатления о слете ДСА 2024, рассказанные волонтером и участником слета

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

Организация бесперебойной работы  

Бесперебойная работа ИТ-инфраструктуры в режиме 24/7 Как обеспечить ее в нынешних условиях?

Год назад ИТ-компания «Крок» провела исследование «Ключевые тренды сервисного рынка 2023». Результаты

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

Книжная полка  

Читайте и познавайте мир технологий!

Издательство «БХВ» продолжает радовать выпуском интересных и полезных, к тому же прекрасно

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

СУБД PostgreSQL  

СУБД Postgres Pro

Сертификация по новым требованиям ФСТЭК и роль администратора без доступа к данным

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

Критическая инфраструктура  

КИИ для оператора связи. Готовы ли компании к повышению уровня кибербезопасности?

Похоже, что провайдеры и операторы связи начали забывать о требованиях законодательства

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

Архитектура ПО  

Архитектурные метрики. Качество архитектуры и способность системы к эволюционированию

Обычно соответствие программного продукта требованиям мы проверяем через скоуп вполне себе понятных

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

Как хорошо вы это знаете  

Что вам известно о разработках компании ARinteg?

Компания ARinteg (ООО «АРинтег») – системный интегратор на российском рынке ИБ –

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

Графические редакторы  

Рисование абстрактных гор в стиле Paper Cut

Векторный графический редактор Inkscape – яркий представитель той прослойки open source, с

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

День сисадмина  

Учите матчасть! Или как стать системным администратором

Лето – время не только отпусков, но и хорошая возможность определиться с профессией

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

День сисадмина  

Живой айтишник – это всегда движение. Остановка смерти подобна

Наши авторы рассказывают о своем опыте и дают советы начинающим системным администраторам.

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

Виртуализация  

Рынок решений для виртуализации

По данным «Обзора российского рынка инфраструктурного ПО и перспектив его развития», сделанного

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

Книжная полка  

Как стать креативным и востребованным

Издательский дом «Питер» предлагает новинки компьютерной литературы, а также книги по бизнесу

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

Книжная полка  

От создания сайтов до разработки и реализации API

В издательстве «БХВ» недавно вышли книги, которые будут интересны системным администраторам, создателям

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

Разбор полетов  

Ошибок опыт трудный

Как часто мы легко повторяем, что не надо бояться совершать ошибки, мол,

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

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

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

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

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

12.03.2018г.
Просмотров: 4179
Комментарии: 0
Глубокое обучение с точки зрения практика

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

12.03.2018г.
Просмотров: 2986
Комментарии: 0
Изучаем pandas

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

12.03.2018г.
Просмотров: 3791
Комментарии: 0
Программирование на языке Rust (Цветное издание)

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

19.12.2017г.
Просмотров: 3801
Комментарии: 0
Глубокое обучение

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

19.12.2017г.
Просмотров: 6295
Комментарии: 0
Анализ социальных медиа на Python

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

19.12.2017г.
Просмотров: 3150
Комментарии: 0
Основы блокчейна

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

19.12.2017г.
Просмотров: 3445
Комментарии: 0
Java 9. Полный обзор нововведений

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

16.02.2017г.
Просмотров: 7261
Комментарии: 0
Опоздавших не бывает, или книга о стеке

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

17.05.2016г.
Просмотров: 10627
Комментарии: 0
Теория вычислений для программистов

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

30.03.2015г.
Просмотров: 12347
Комментарии: 0
От математики к обобщенному программированию

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

18.02.2014г.
Просмотров: 13979
Комментарии: 0
Рецензия на книгу «Читаем Тьюринга»

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

13.02.2014г.
Просмотров: 9109
Комментарии: 0
Читайте, размышляйте, действуйте

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

12.02.2014г.
Просмотров: 7063
Комментарии: 0
Рисуем наши мысли

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

10.02.2014г.
Просмотров: 5373
Комментарии: 3
Страна в цифрах

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

18.12.2013г.
Просмотров: 4603
Комментарии: 0
Большие данные меняют нашу жизнь

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

18.12.2013г.
Просмотров: 3412
Комментарии: 0
Компьютерные технологии – корень зла для точки роста

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

04.12.2013г.
Просмотров: 3142
Комментарии: 0
Паутина в облаках

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

03.12.2013г.
Просмотров: 3388
Комментарии: 0
Рецензия на книгу «MongoDB в действии»

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

02.12.2013г.
Просмотров: 3010
Комментарии: 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-45
E-mail: sa@samag.ru