Этот урок связан с проектом PHP исключения

Исключения PHP. Часть 1 из 2

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

 

Что такое исключения?

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

 

Как работают исключения?

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

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

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

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

Ловля мяча полевым игроком похожа на выполнение функции try-catch для обработки исключения. Функция try-catch  может самостоятельно выполнить нужные действия, или перебросить исключение другой функции.

 

Как использовать исключения

Сформировать обработку исключений в PHP очень просто. Так как исключения являются частью стандартной библиотеки PHP(SPL), они входят в ядро PHP, начиная с версии 5. То есть исключения уже ждут, когда их начнут использовать.

Исключения реализуются также как и любой другой объект:

$exception = new Exception();
throw $exception;

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

Вот список методов исключений:

  • getMessage() – получает сообщение исключения;
  • getCode() – возвращает числовой код, который представляет исключение;
  • getFile() – возвращает файл, в котором произошло исключение;
  • getLine() – возвращает номер строки в файле, где произошло исключение;
  • getTrace() – возвращает массив backtrace() до возникновения исключения;
  • getPrevious() – возвращает исключение, произошедшее перед текущим, если оно было;
  • getTraceAsString() – возвращает массив backtrace() исключения в виде строки;
  • __toString() –возвращает все исключение в виде строки. Данную функцию можно переписать.

Для лучшей иллюстрации методов, создадим класс User, который осуществляет базовое управление записями пользователей:

<?php
class User
{
   protected $_user_id;
   protected $_user_email;
   protected $_user_password;
 
   public function __construct($user_id)
   {
      $user_record = self::_getUserRecord($user_id);
      $this->_user_id = $user_record['id'];
      $this->_user_email = $user_record['email'];
      $this->_user_password = $user_record['password'];
   }
 
   //Данные функции сформируем позже
   public function __get($value) {}
   public function __set($name, $value) {}
 
   private static function _getUserRecord($user_id)
   {
      //Для урока будем использовать модель записи для представления передачи данных в базу
      $user_record = array();
      switch($user_id) {
         case 1:
            $user_record['id'] = 1;
            $user_record['email'] = 'nikko@example.com';
            $user_record['password'] = 'i like croissants';
            break;
 
         case 2:
            $user_record['id'] = 2;
            $user_record['email'] = 'john@example.com';
            $user_record['password'] = 'me too!';
            break;
 
         case 'error':
            //имитируем неизвестное исключение от какой-нибудь используемой библиотеки SQL:
            throw new Exception('Ошибка библиотеки SQL!');
            break;
      }
 
      return $user_record;
   }
}
?>

В данном примере имеется несколько мест, где может проявиться исключение. Для начала,  $user_id в нашей функции  _getUserRecord() должен иметь тип integer. Если события сложатся по другому, то нужно генерировать исключение:

...
...
...
private static function _getUserRecord($user_id)
{
   $user_id = self::_validateUserId($user_id);
   ...
   ...
   ...
}
 
private static function _validateUserId($user_id)
{
   if( !is_numeric($user_id) && $user_id != 'error' ) {
      throw new Exception('Ой! Здесь что-то не так с идентификатором пользователя');
   }
   return $user_id;
}

Теперь попробуем создать пользователя с неправильным идентификатором:

//Сначала создаем пользователя с номером один
$user = new User(1);
 
//Затем намеренно делаем ошибку
$user2 = new User('not numeric');

В итоге мы получим системное сообщение об ошибке.

А теперь позволим полевому игроку поймать исключение и обработать его:

try {
   //Сначала создаем пользователя с номером один
   $user = new User(1);
 
   //Затем намеренно делаем ошибку
   $user2 = new User('not numeric');
} catch( Exception $e ) {
   echo "Полевой игрок поймал исключение: {$e->getMessage()}";
}

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

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

  • Создаем новый объект User в строке $user2 = new User('not numeric');
  • Выполняем функцию __construct() класса User
  • Переходим к функции _getUserRecord() класса User
  • Проверяем $user_id с помощью функции _validateUserId
  • Если $user_id не является числовым значением, выбрасываем объект исключения.

Мы также можем выполнить функцию getTraceAsString() для отслеживания исключения:

...
...
} catch( Exception $e ) {
   echo "Полевой игрок поймал исключение: {$e->getMessage()}";
   echo '<pre>';
   echo $e->getTraceAsString();
   echo '</pre>';
}

То есть, не смотря на то, что исключение произошло гораздо глубже вызова $user2 = new User('not numeric'), оно вскарабкалось наверх, пока блок try-catch не обработал его.

Теперь сформируем наше исключение для использования кода. Соответствие числового кода исключению является хорошим способом скрыть ошибку от клиента. По умолчанию, код исключения 0. Но мы можем изменить его при создании исключения:

...
...
...
if( !is_numeric($user_id) ) {
   throw new Exception('Ой! Здесь что-то не так с идентификатором пользователя', UserErrors::INVALIDID);
}
...
...
...

Обязательно нужно создать класс UserErrors:

class UserErrors
{
   const INVALIDID      = 10001;
   const INVALIDEMAIL      = 10002;
   const INVALIDPW         = 10003;
   const DOESNOTEXIST      = 10004;
   const NOTASETTING    = 10005;
   const UNEXPECTEDERROR   = 10006;
 
   public static function getErrorMessage($code)
   {
      switch($code) {
         case self::INVALIDID:
            return 'Ой! Здесь что-то не так с идентификатором пользователя';
            break;
 
         case self::INVALIDEMAIL:
            return 'Адрес email неправильный!';
            break;
 
         case self::INVALIDPW:
            return 'Пароль меньше 4 символов!';
            break;
 
         case self::DOESNOTEXIST:
            return 'Пользователя не существует!';
            break;
 
         case self::NOTASETTING:
            return 'Таких установок нет!';
            break;
 
         case self::UNEXPECTEDERROR:
         default:
            return 'Какая-то ошибка!';
            break;
      }
   }
}

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

...
...
} catch( Exception $e ) {
   echo "Полевой игрок поймал исключение: #{$e->getCode()}";
}

Теперь будем использовать другие методы исключения для его отслеживания. Сначала создадим класс Logger:

class Logger
{
   public static function newMessage(
      Exception $exception,
      $clear = false,
      $error_file = 'exceptions_log.html'
   ) {
      $message = $exception->getMessage();
      $code = $exception->getCode();
      $file = $exception->getFile();
      $line = $exception->getLine();
      $trace = $exception->getTraceAsString();
      $date = date('M d, Y h:iA');
 
      $log_message = "<h3>Информация об исключении:</h3>
         <p>
            <strong>Дата:</strong> {$date}
         </p>
 
         <p>
            <strong>Сообщение:</strong> {$message}
         </p>
 
         <p>
            <strong>Код:</strong> {$code}
         </p>
 
         <p>
            <strong>Файл:</strong> {$file}
         </p>
 
         <p>
            <strong>Строка:</strong> {$line}
         </p>
 
         <h3>Stack trace:</h3>
         <pre>{$trace}
         </pre>
         <br />
         <hr /><br /><br />";
 
      if( is_file($error_file) === false ) {
         file_put_contents($error_file, '');
      }
 
      if( $clear ) {
         $content = '';
      } else {
         $content = file_get_contents($error_file);
      }
 
      file_put_contents($error_file, $log_message . $content);
   }
}

Модифицируем обработку исключения, чтобы использовать класс Logger:

...
} catch( Exception $e ) {
   echo "Полевой игрок поймал исключение: #{$e->getCode()}";
   Logger::newMessage($e);
}

Теперь при выполнении программы будет выводиться код исключения, а в файле  exceptions_log.html будет представлена информация о месте появления исключения.

 

Заключение

В данном уроке представлены базовые сведения об исключениях PHP. В следующем уроке серии мы рассмотрим расширенное исключение и множественные блоки catch.

Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: http://net.tutsplus.com/tutorials/php/the-ins-and-outs-of-php-exceptions/
Перевел: Сергей Фастунов
Урок создан: 18 Октября 2011
Просмотров: 50143
Правила перепечатки


5 последних уроков рубрики "PHP"

  • Фильтрация данных с помощью zend-filter

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

  • Контекстное экранирование с помощью zend-escaper

    Обеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода.

  • Подключение Zend модулей к Expressive

    Expressive 2 поддерживает возможность подключения других ZF компонент по специальной схеме. Не всем нравится данное решение. В этой статье мы расскажем как улучшили процесс подключение нескольких модулей.

  • Совет: отправка информации в Google Analytics через API

    Предположим, что вам необходимо отправить какую-то информацию в Google Analytics из серверного скрипта. Как это сделать. Ответ в этой заметке.

  • Подборка PHP песочниц

    Подборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.

или авторизуйтесь, чтобы добавлять комментарии, оценивать уроки и сохранять их в личном кабинете
  • 19 Октября 2011 09:39
    Slonoritsar
    мне лично довольно понравилось...
    • 19 Октября 2011 18:53
      murdok1993
      murdok
      • 20 Октября 2011 21:14
        m_hamlet
        И что?
  • 19 Октября 2011 12:58
    Ygreec
    Спасибо, что начали уроки по ПХП, респект
  • 19 Октября 2011 20:30
    rubyx
    Нужный урок по "пхп"!
  • 20 Октября 2011 17:00
    Matrox4u
    есть что прочти новечку
    • 20 Октября 2011 18:28
      WI_Wind
      Новичку сначала надо основы ООП пройти...
      • 20 Октября 2011 19:11
        p1us
        где с нормальными примерами есть основы ООП? потому как не получается нормальный учебник подыскать. а существующие сайты сложноваты для разбора новичку
        • 21 Октября 2011 14:48
          notbot
          "PHP. Профессиональное программирование" М. Кузнецов, И. Симдянов
  • 20 Октября 2011 21:15
    m_hamlet
    Отличный урок! Я об этом не знал!
  • 26 Октября 2011 15:26
    Slonoritsar
    Немного углубившись в это, понял, что это довольно таки не нужная вещь. Максимум где использовать - при отладке скриптов!
  • 21 Января 2012 15:47
    Oxyaction
    Безусловно нужный и полезный урок автору спасибо. Slonoritsar : А что по-твоему нужно делать в случае если у тебя большой проект и ошибка вылазит достаточно редко? Да и как вообще логирование вести ?
  • 8 Февраля 2012 12:20
    TRAHOMOTO
    Хоть и пользуюсь исключениями достаточно давно, но, как всегда, ваш ресурс расширяет кругозор
    • 23 Июля 2012 15:11
      RaduBB
      lpoioopo
  • 27 Декабря 2012 03:12
    Grato
    все в уроке полезно и понятно кроме мути с бейсболом, куча незнакомых слов о неизвестно о чем...
  • 1 Августа 2013 18:05
    dllweb
    таки бредовая статья, можно объяснить как разгрызть семечку во рту и описать как работают все мышцы челюсти языка, а так же где находится эта челюсть и кому принадлежит, а ещё кем был дед на второй мировой этой челюсти. Проще нужно подходить к написанию статьи а не выкручивать и смешивать с ретроспортом, в общем оценочку поставлю 2 твёрдое, запутался к чертям
^ Наверх ^