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


Содержание

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

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

  • try: он представляет собой блок кода, в котором может возникнуть исключение.
  • catch: представляет блок кода, который будет выполняться при возникновении определенного исключения.
  • throw: используется для выброса исключения. Он также используется для перечисления исключений, которые выдает функция, но не обрабатывает себя.
  • напоследок: он используется вместо блока catch или после блока catch, в основном он используется для очистки в коде PHP.

Почему обработка исключений в PHP?
Ниже приведены основные преимущества обработки исключений по сравнению с обработкой ошибок.

  • Отделение кода обработки ошибок от нормального кода: в традиционном коде обработки ошибок всегда есть блок if else для обработки ошибок. Эти условия и код для обработки ошибок были перепутаны, что стало нечитаемым. При попытке Catch блок кода становится читабельным.
  • Группировка типов ошибок: в PHP как базовые типы, так и объекты могут быть выброшены как исключение. Он может создавать иерархию объектов исключений, группировать исключения в пространствах имен или классах, классифицировать их по типам.

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

    Следующий код объясняет поток нормального блока try catch в PHP:

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

Автор: Артемьев Сергей Игоревич
ICQ: 438856621
email: _spin_@bk.ru

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

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

С точки зрения разработчика, основное отличие ошибки от исключения в том, что после возникновения ошибки скрипт может продолжить выполнение, а после возникновения исключения — нет.

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

Исключения — это какие-либо аварийные ситуации, возникающие при выполнении скрипта. В PHP исключение можно сгенерировать («выбросить», «вызвать») и поймать его. Исключение может сенерироваться как интерпретатором, так и разработчиком.

Вызов исключения производится следующим образом:

Перехват исключения осуществляется с помощью конструкции try. catch. В общем виде эта конструкция записывается так:

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

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

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

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

В рассмотренном примере важную роль играет порядок catch, точнее порядок проверки на тип исключения. Если первым поставить Exception, то все остальные исключения никогда не сработают, т.к. конструкция catch(Exception $ex) перехватывает абсолютно все доступные исключения.

PHP позволяет использовать свой обработчик исключений. Для этого необходимо объявить собственную функцию обработки и зарегистрировать её при помощи функции set_exception_handler().

После этого все возникающие исключения, не обрамлённые конструкцией try. except, будут передаваться в объявленную вами функцию. Например, можно изменить функцию, чтобы она писала все исключения в файл и выдавала пользователю в браузер «красивое» сообщение об ошибке сервера:

Иногда эта функция бывает очень удобной, особенно в процессе отладки скриптов непосредственно на сервере. Если выводить исключения как обычно в браузер — их смогут увидеть посетители, а это не прибавит популярности вашему сайту. Гораздо менее болезненно пользователи воспримут сообщение типа «Извините, сервер находится на обслуживании, попробуйте войти позже.». Поэтому все ошибки и исключения необходимо писать в файлы, а лучше ещё и отправлять администратору на email.

Хорошим тоном при создании сайтов считается полное отсутствие ошибок. Если же ошибки возникают — они должны быть обработаны и представлены пользователю и разработчику в понятном виде.

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

Освойте бесплатно наиболее простой, быстрый и гибкий способ создавать адаптивные веб-сайты.

Дизайн лендинга

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

Популярное

  • Главная
  • ->
  • Материалы
  • ->
  • Обработка ошибок в PHP (класс Exception)

Reg.ru: домены и хостинг

Крупнейший регистратор и хостинг-провайдер в России.

Более 2 миллионов доменных имен на обслуживании.

Продвижение, почта для домена, решения для бизнеса.

Более 700 тыс. клиентов по всему миру уже сделали свой выбор.

Бесплатный Курс «Практика HTML5 и CSS3»

Освойте бесплатно пошаговый видеокурс

по основам адаптивной верстки

на HTML5 и CSS3 с полного нуля.

Фреймворк Bootstrap: быстрая адаптивная вёрстка

Пошаговый видеокурс по основам адаптивной верстки в фреймворке Bootstrap.

Научитесь верстать просто, быстро и качественно, используя мощный и практичный инструмент.

Верстайте на заказ и получайте деньги.

Что нужно знать для создания PHP-сайтов?

Ответ здесь. Только самое важное и полезное для начинающего веб-разработчика.

Узнайте, как создавать качественные сайты на PHP всего за 2 часа и 27 минут!

Создайте свой сайт за 3 часа и 30 минут.

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

Вам останется лишь наполнить его нужной информацией и изменить дизайн (по желанию).

Изучите основы HTML и CSS менее чем за 4 часа.

После просмотра данного видеокурса Вы перестанете с ужасом смотреть на HTML-код и будете понимать, как он работает.

Вы сможете создать свои первые HTML-страницы и придать им нужный вид с помощью CSS.

Бесплатный курс «Сайт на WordPress»

Хотите освоить CMS WordPress?

Получите уроки по дизайну и верстке сайта на WordPress.

Научитесь работать с темами и нарезать макет.

Бесплатный видеокурс по рисованию дизайна сайта, его верстке и установке на CMS WordPress!

Хотите изучить JavaScript, но не знаете, как подступиться?

После прохождения видеокурса Вы освоите базовые моменты работы с JavaScript.

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

*Наведите курсор мыши для приостановки прокрутки.

Обработка ошибок в PHP (класс Exception)

Перед изучением данной статьи вы можете прочитать предыдущую статью из этой серии — «Позднее статическое связывание: ключевое слово static».

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


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

Ниже приведено определение простого класса Conf, который сохраняет, извлекает и определяет данные в XML-файле конфигурации.

В классе Conf для доступа к парам «имя — значение» используется расширение PHP SimpleXml. Ниже приведен фрагмент файла конфигурации в формате XML, с которым работает наш класс.

Конструктору класса Conf передается имя файла конфигурации, которое далее передается функции simplexml_load_file(). Полученный от функции объект типа SimpleXmlElement сохраняется в свойстве $xml.

В методе get() для нахождения элемента item с заданные атрибутом name используется метод xpath объекта SimpleXmlElement. Значение найденного элемента возвращается в вызывающий код. Метод set() либо меняет значение существующего элемента, либо создает новый. И, наконец, метод write() сохраняет данные о новой конфигурации в исходном файле на диске.

Как и многие коды, приведенные в качестве примеров, код класса Conf крайне упрощен. В частности, в нем не предусмотрена обработка ситуаций, когда файл конфигурации не существует или в него нельзя записать данные. Этот код также слишком «оптимистичен». В нем предполагается, что XML-документ должен быть правильно отформатирован и содержать ожидаемые элементы.

Провести тестирование подобных ошибок достаточно просто, но мы должны решить, как на них реагировать, если они возникнут. В целом у нас есть две возможности.

1. Мы можем завершить выполнение программы. Это простой, но радикальный выход. В результате наш скромный класс будет виноват в том, что из-за него потерпел неудачу весь сценарий. Хотя такие методы, как __construct() и write(), удачно расположены в коде с целью обнаружения ошибок, у них нет информации, позволяющей решить, как обрабатывать эти ошибки.

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

Во многих PEAR-пакетах сочетаются эти два подхода и возвращается объект ошибок (экземпляр класса PEAR_Error). Наличие этого объекта говорит о том, что произошла ошибка, а подробная информация о ней содержится в самом объекте.

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

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

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

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

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

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

С PHP 5 было введено понятие исключений, представляющих собой совершенно другой способ обработки ошибок. Я хочу сказать — совершенно другой для PHP. Но если у вас есть опыт работы с Java или C++, то исключения покажутся вам знакомыми и близкими. Использование исключений позволяет решить все проблемы, о которых мы говорили ранее.

Итак, исключение — это специальный объект, который является экземпляром встроенного класса Exception (или его производного класса). Объекты типа Exception предназначены для хранения информации об ошибках и выдачи сообщений о них.

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

Общедоступные методы класса Exception

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

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

Совместно с объектом Exception используется ключевое слово throw. Оно останавливает выполнение текущего метода и передает ответственность за обработку ошибок назад в вызывающий код. Давайте подкорректируем метод __construct(), чтобы использовать оператор throw.

Аналогичная конструкция может использоваться и в методе write().

Теперь наши методы __construct() и write() могут тщательно проверять ошибки в файле по мере выполнения своей работы. Однако при этом решение о том, как реагировать на любые ошибки, будет приниматься в клиентском коде.

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

Оператор try состоит из ключевого слова try и следующих за ним фигурный скобок. За оператором try должен следовать по меньшей мере один оператор catch, в котором можно обработать любую ошибку, как показано ниже.

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

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

Создание подклассов класса Exception

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

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

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

Объект типа LibXmlError создается автоматически, когда средства SimpleXml обнаруживают поврежденный XML-файл. У него есть свойства message и code, и он напоминает класс Exception. Мы пользуется преимуществом этого подобия и используем объект LibXmlError в классе XmlException.

Илон Маск рекомендует:  Что такое код mysql_free_result

У классов FileException и ConfException не больше функциональных возможностей, чем у родительского класса Exception. Теперь мы можем использовать эти классы в коде и подкорректировать оба метода __construct() и write().

Метод __construct() генерирует исключение типа XmlException, FileException или ConfException, в зависимости от вида ошибки, которую он обнаружит.

Обратите внимание на то, что методу simplexml_load_file() передается флаг LIBXML_NOERROR. Это блокирует выдачу предупреждений внутри класса и оставляет программисту свободу действий для их последующей обработки с помощью класса XmlException.

Если обнаружится поврежденный XML-файл, то метод simplexml_load_file() уже не возвращает объект типа SimpleXmlElement. Благодаря классу XmlException в клиентском коде можно будет легко узнать причину ошибки, а с помощью метода libxml_get_last_error() — все подробности этой ошибки.

Метод write() генерирует исключение типа FileException, если свойство $file указывает на файл, недоступный для записи.

Итак, мы установили, что метод __construct() может генерировать одно из трех возможных исключений. Как мы можем этим воспользоваться? Ниже приведен пример кода, в котором создается экземпляр объекта Conf().

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

Например, если бы вы разместили оператор catch для обработки исключения типа Exception перед операторами для обработки исключений типа XmlException и ConfException, ни один из них никогда бы не был вызван. Причина в том, что оба исключения относятся к типу Exception и поэтому будут соответствовать первому оператору.

Первый оператор catch (FileException) вызывается, если есть проблема с файлом конфигурации (если этот файл не существует или в него нельзя ничего записать).

Второй оператор catch (XmlException) вызвается, если происходит ошибка при синтаксическом анализе XML-файла (например, если какой-то элемент не закрыт).

Третий оператор catch (ConfException) вызывается, если корректный в плане формата файл XML не содержит ожидаемый корневой элемент conf.

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

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

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

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

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

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

Метод write() в нашем примере «знает», когда попытка сделать запись заканчивается неудачей и почему, но «не знает», что с этим делать. Именно так и должно быть.

Если бы мы сделали класс Conf более «сведущим», чем он есть в настоящее время, он бы потерял свою универсальность и перестал бы быть повторно используемым.

На этом пока всё. В следующем материале мы поговорим о ключевом слове final и о том, как предотвратить наследование в PHP (скоро будет доступен).

Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!

PHP Catch не перехватывает исключения с помощью обработчика пользовательских исключений

Я сталкиваюсь с проблемой, когда мой проект PHP не перехватывает исключения, когда я использую блок Try Catch. Когда выдается исключение, PHP по-прежнему будет отображать сообщение об ошибке исключения и завершит выполнение сценария PHP. Я сузил его до того факта, что у меня есть собственный обработчик ошибок PHP, который я построил. Как только я удаляю свой собственный обработчик PHP, блоки TRY CATCH снова работают нормально. Удаление пользовательского обработчика ошибок, к сожалению, не вариант. Пожалуйста, смотрите код ниже:

Я также включил пример блока catch, где код не перехватывается:

Исключение вызова класса

Решение

Вы находитесь в пространстве имен Base \ Members \ Library.

Блок ловит База \ Члены \ Библиотека \ Исключение

Попробуйте вместо этого перехватить \ Exception в глобальном пространстве имен.

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

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

Это очень просто в PHP для обработки ошибок.

Использование функции die()

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

Попробуйте привести пример без файла / tmp / test.xt и с этим файлом.

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

Определение пользовательской функции обработки ошибок

Вы можете написать свою собственную функцию для обработки любой ошибки. PHP предоставляет вам структуру для определения функции обработки ошибок.

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


# Параметр Описание
1 error_level Обязательный параметр — задает уровень отчета об ошибке для пользовательской ошибки. Значение должно быть числом.
2 error_message Обязательный параметр — указывает сообщение об ошибке для пользовательской ошибки
3 error_file Необязательный. Указывает имя файла, в котором произошла ошибка.
4 error_line Необязательный. Указывает номер строки, в которой произошла ошибка.
5 error_context Необязательный. Указывает массив, содержащий каждую переменную, и их значения, используемые при возникновении ошибки.

Возможные уровни ошибок

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

# Константа Описание Значение
1 .E_ERROR Неустранимая ошибки во время выполнения. Выполнение сценария останавливается 1
2 E_WARNING Нефатальные ошибки во время выполнения. Выполнение скрипта не останавливается 2
3 E_PARSE Ошибки анализа времени компиляции. Ошибки анализа должны генерироваться только парсером. 4
4 E_NOTICE Уведомления о времени выполнения. Скрипт нашел что-то, что может быть ошибкой, но может также произойти при обычном сценарии 8
5 E_CORE_ERROR Неустранимая ошибки, возникающие во время первоначального запуска PHP. 16
6 E_CORE_WARNING Нефатальные ошибки во время выполнения. Это происходит во время первоначального запуска PHP. 32
7 E_USER_ERROR Неустранимая пользовательская ошибка. Это похоже на набор E_ERROR программиста, использующий функцию PHP trigger_error() 256
8 E_USER_WARNING Нефатальное пользовательское предупреждение. Это похоже на E_WARNING, установленный программистом с использованием функции PHP trigger_error() 512
9 E_USER_NOTICE Пользовательское уведомление. Это похоже на набор E_NOTICE, заданный программистом с использованием функции PHP trigger_error() 1024
10 E_STRICT Уведомления о времени выполнения. Включите, чтобы PHP предлагал изменения в вашем коде, что обеспечит лучшую совместимость и передовую совместимость вашего кода. 2048
11 E_RECOVERABLE_ERROR Ловкая ошибка. Это похоже на E_ERROR, но может быть захвачен определяемым пользователем дескриптором (см. Также set_error_handler()) 4096
12 E_ALL Все ошибки и предупреждения, кроме уровня E_STRICT(E_STRICT будет частью E_ALL с PHP 6.0) 8191

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

int error_reporting ([ int $ level])

Ниже приведен способ создания одной функции обработки ошибок —

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

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

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

  • Try — функция, использующая исключение, должна находиться в блоке «try». Если исключение не запускается, код будет продолжен как обычно. Однако, если исключение инициируется, исключение «бросается».
  • Throw — это то , как вы вызываете исключение. Каждый «бросок» должен иметь хотя бы один «улов».
  • Catch. Блок «catch» извлекает исключение и создает объект, содержащий информацию об исключении.
  • Когда генерируется исключение, код, следующий за оператором, не будет выполнен, а PHP попытается найти первый соответствующий блок catch. Если исключение не поймано, PHP Fatal Error будет выпущена с помощью «Uncaught Exception . »
  • Исключение может быть выброшено, и поймал ( « пойманный ») в PHP. Код может быть окружен в блоке try.
  • Каждая попытка должна иметь по крайней мере один соответствующий блок catch. Для блокировки различных классов исключений можно использовать несколько блоков catch.
  • Исключения могут быть сброшены (или повторно выбраны) в блоке catch.

пример

Ниже приведен фрагмент кода, скопируйте и вставьте этот код в файл и проверьте результат.

В приведенном выше примере функция $e->getMessage используется для получения сообщения об ошибке. Существуют следующие функции, которые могут использоваться из класса Exception.

  • GetMessage() — сообщение об исключении
  • GetCode() — код исключения
  • GetFile() — исходное имя файла
  • GetLine() — исходная строка
  • GetTrace() — n массив backtrace()
  • GetTraceAsString() — сформированная строка трассировки

Создание настраиваемого обработчика исключений

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

string set_exception_handler(callback $exception_handler)

Здесь exception_handler — это имя вызываемой функции, когда происходит неперехваченное исключение. Эта функция должна быть определена перед вызовом set_exception_handler().

Исключения

Содержание

Модель исключений (exceptions) в PHP 5 схожа с используемыми в других языках программирования. Исключение можно сгенерировать (как говорят, «выбросить») при помощи оператора throw, и можно перехватить (или, как говорят, «поймать») оператором catch. Код генерирующий исключение, должен быть окружен блоком try, для того чтобы можно было перехватить исключение. Каждый блок try должен иметь как минимум один соответствующий ему блок catch или finally.

Генерируемый объект должен принадлежать классу Exception или наследоваться от Exception. Попытка сгенерировать исключение другого класса приведет к неисправимой ошибке.

catch

Можно использовать несколько блоков catch, перехватывающих различные классы исключений. Нормальное выполнение (когда не генерируются исключения в блоках try или когда класс сгенерированного исключения не совпадает с классами, объявленными в соответствующих блоках catch) будет продолжено за последним блоком catch. Исключения так же могут быть сгенерированы (или вызваны еще раз) оператором throw внутри блока catch.

При генерации исключения код следующий после описываемого выражения исполнен не будет, а PHP предпримет попытку найти первый блок catch, перехватывающий исключение данного класса. Если исключение не будет перехвачено, PHP выдаст сообщение об ошибке: «Uncaught Exception . » (Неперехваченное исключение), если не был определен обработчик ошибок при помощи функции set_exception_handler() .

finally

В PHP 5.5 и более поздних версиях также можно использовать блок finally после или вместо блока catch. Код в блоке finally всегда будет выполняться после кода в блоках try и catch, вне зависимости было ли брошено исключение или нет, перед тем как продолжится нормальное выполнение кода. whether an exception has been thrown, and before normal execution resumes.

Примечания

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

Примеры

Пример #3 Выброс исключений

function inverse ( $x ) <
if (! $x ) <
throw new Exception ( ‘Деление на ноль.’ );
>
return 1 / $x ;
>

try <
echo inverse ( 5 ) . «\n» ;
echo inverse ( 0 ) . «\n» ;
> catch ( Exception $e ) <
echo ‘Выброшено исключение: ‘ , $e -> getMessage (), «\n» ;
>

// Продолжение выполнения
echo «Hello World\n» ;
?>

Результат выполнения данного примера:

Пример #4 Вложенные исключения

function inverse ( $x ) <
if (! $x ) <
throw new Exception ( ‘Деление на ноль.’ );
>
return 1 / $x ;
>

try <
echo inverse ( 5 ) . «\n» ;
> catch ( Exception $e ) <
echo ‘Поймано исключение: ‘ , $e -> getMessage (), «\n» ;
> finally <
echo «Первое finally.\n» ;
>

try <
echo inverse ( 0 ) . «\n» ;
> catch ( Exception $e ) <
echo ‘Поймано исключение: ‘ , $e -> getMessage (), «\n» ;
> finally <
echo «Второе finally.\n» ;
>

// Продолжение нормального выполнения
echo «Hello World\n» ;
?>

Результат выполнения данного примера:

Пример #5 Вложенные исключения

class Test <
public function testing () <
try <
try <
throw new MyException ( ‘foo!’ );
> catch ( MyException $e ) <
// повторный выброс исключения
throw $e ;
>
> catch ( Exception $e ) <
var_dump ( $e -> getMessage ());
>
>
>

$foo = new Test ;
$foo -> testing ();

PHP5 Обработка исключений

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

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

В PHP5 появился новый объектно-ориентированный способ борьбы с ошибками.

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

Это то, что обычно происходит, когда срабатывает исключение:

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

Мы покажем различные методы обработки ошибок:

  • Основное использование исключений
  • Создание пользовательского обработчика исключений
  • Несколько исключений
  • Повторное создание исключения
  • Установка обработчика исключений верхнего уровня

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

PHP Основное использование исключений

При возникновении исключения, следующий за ним код не будет выполнен и PHP будет пытаться найти соответствующий блок «Поймать».

Если исключение не перехватывается, фатальная ошибка будет выдаваться как сообщение «Необработанное исключение».

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

Пример

1) <
throw new Exception(«Значение должно быть 1 или ниже»);
>
return true;
>

//Вызов исключения
checkNum(2);
?>

Код выше получит ошибку, как эта:

PHP Пробовать, забросить и вытащить

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

Правильный код исключения должен включать:

  1. Пробовать — используется исключение в блоке try . Если исключение не срабатывает, код будет работать как обычно. Однако, если исключение срабатывает, исключение будет thrown
  2. Забросить — вызывает исключение. Каждый throw должен иметь хотя бы один catch
  3. Вытащить — Один блок catch получает исключение и создает объект, содержащий сведения об исключении

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

Пример


1) <
throw new Exception(«Значение должно быть 1 или ниже»);
>
return true;
>

//вызвать исключения в блоке «try»
try <
checkNum(2);
//Если исключение выбрасывается, этот текст не будет отображаться
echo ‘Если Вы видите это, значение должно быть 1 или ниже’;
>

//исключение catch
catch(Exception $e) <
echo ‘Сообщение: ‘ .$e->getMessage();
>
?>

Код выше получит ошибку, как эта:

Объяснение примера:

Приведенный выше код создает исключение и перехватывает его:

  1. Функция создает checkNum() . Она проверяет, если число больше, чем 1. Если это так, то исключение throw
  2. функция вызывается checkNum() в блок try
  3. Исключение checkNum() в функции thrown
  4. Блок catch получает исключение и создает объект $е , содержащий сведения об исключении
  5. Сообщение об ошибке из исключения повторяется вызовом $e->getMessage() из объекта исключение

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

PHP Создание пользовательского класса исключений

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

Пользовательский класс исключений наследует свойства от PHP класс исключений и в него можно добавлять пользовательские функции.

Позволяет создать класс исключения:

Пример

getLine().’ в ‘.$this->getFile()
.’: ‘.$this->getMessage().’ не является допустимым адресом электронной почты’;
return $errorMsg;
>
>

$email = «someone@example. com»;

try <
//проверить, если
if(filter_var($email, FILTER_VAL > //исключение throw, если e-mail не является допустим
throw new customException($email);
>
>

catch (customException $e) <
//показать пользовательское сообщение
echo $e->errorMessage();
>
?>

Новый класс является копией старого класса исключений, с добавлением функции errorMessage() . Так как это копия старого класса и она наследует свойства и методы из старого класса, мы можем использовать класс исключений метод вызова getline() , метод getfile() и метод getmessage() .

Объяснение примера:

Приведенный выше код создает исключение и перехватывает его с помощью пользовательского класса исключений:

  1. Класс customException() создается как расширение старого класса исключений. Таким образом, он наследует все методы и свойства из старого класса исключений
  2. Функция errorMessage() создана. Данная функция возвращает сообщение об ошибке, если адрес электронной почты является недействительным
  3. Переменной $email присваивается строка, которая не является допустимым адресом электронной почты
  4. Блок try выполняется и возникает исключение, так как адрес электронной почты является недопустимым
  5. Блок catch вытаскивает исключение и отображает сообщение об ошибке

Несколько исключений

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

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

Пример

getLine().’ в ‘.$this->getFile()
.’: ‘.$this->getMessage().’ не является допустимым адресом электронной почты’;
return $errorMsg;
>
>

try <
//проверить, если
if(filter_var($email, FILTER_VAL > //исключение throw, если e-mail не является допустим
throw new customException($email);
>
//проверить «example» в почтовом адресе
if(strpos($email, «example») !== FALSE) <
throw new Exception(«$email является example e-mail»);
>
>

catch (customException $e) <
echo $e->errorMessage();
>

catch(Exception $e) <
echo $e->getMessage();
>
?>

Объяснение примера:

Приведенный выше код проверяет два условия и создает исключение, если какое-либо из условий не выполняется:

  1. Класс customException() создается как расширение старого класса исключений. Таким образом, он наследует все методы и свойства из старого класса исключений
  2. Создается функция errorMessage() ). Эта функция возвращает сообщение об ошибке, если адрес электронной почты является недействительным
  3. Переменная $email устанавливается в строку, которая является допустимым адресом электронной почты, но содержит строку «example»
  4. Блок try выполняется и исключение не возникает при первом условии
  5. Второе условие вызывает исключение, так как сообщение электронной почты содержит строку «example»
  6. Блок catch вытаскивает исключение и отображает правильное сообщение об ошибке

Если исключение было из класса customException и не было customException поймано, только исключение catch, исключение будет сделано так.

PHP Повторное создание исключений

Иногда, когда возникает исключение, может потребоваться обработать его по-другому, чем стандартный способ. Можно забросить исключение во второй раз в блок «catch».

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

Пример

getMessage().’ не является допустимым адресом электронной почты.’;
return $errorMsg;
>
>

try <
try <
//проверить «example» в почтовом адресе
if(strpos($email, «example») !== FALSE) <
//исключение throw, если электронная почта не является допустимой
throw new Exception($email);
>
>
catch(Exception $e) <
//повторного исключение throw
throw new customException($email);
>
>

catch (customException $e) <
//показать пользовательское сообщение
echo $e->errorMessage();
>
?>

Объяснение примера:

Приведенный выше код проверяет, содержит ли адрес электронной почты строку «example», если содержит, исключение thrown создается повторно:

  1. Класс customException () создается как расширение старого класса исключений. Таким образом, он наследует все методы и свойства из старого класса исключений
  2. Создается функция errorMessage(). Эта функция возвращает сообщение об ошибке, если адрес электронной почты является недействительным
  3. Переменная $email устанавливается в строку, которая является допустимым адресом электронной почты, но содержит строку «example»
  4. В блоке «try» содержится еще один блок «try», чтобы повторно сгенерировать исключение throw
  5. Исключение срабатывает с момента электронной почты, содержит строку «example»
  6. Блок «catch» перехватывает исключение и повторно забрасывает «customException»
  7. «customException» перехватил и отображает сообщение об ошибке

Если исключение не поймано в ее текущей блоке «try», он будет искать блок catch «более высокого уровня».

PHP Установка обработчика исключений верхнего уровня

Функция set_exception_handler() задает пользовательскую функцию для обработки всех необработанных исключений:

Пример

throw new Exception(‘Произошло необработанное исключение’);
?>

Выходные данные приведенного выше, код должны быть примерно такими:

В коде выше нет блока «catch». Вместо этого сработал обработчик исключений верхнего уровня. Эта функция должна использоваться для отслеживания необработанных исключений.

Правила для исключений

  • Код может быть окружен блоком try, чтобы помочь поймать потенциальные исключения
  • Каждый блок try или «throw» должны иметь по крайней мере один соответствующий блок catch
  • Для перехвата различных классов исключений можно использовать несколько блоков catch
  • Исключения могут быть брошены (или повторно брошены) в блоке catch в блоке try

Простое правило: если Вы что-то бросаете, вы должны поймать его.

Исключения в PHP 7

С PHP 7 к нам приходит новая эпоха обработки исключений. Впервые PHP стал выкидывать исключения для фатальных и восстанавливаемых фатальных ошибок. Теперь мы можем их обрабатывать, как захотим. Больше никаких @, include, file_exists и прочей не логичности в коде.

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

Новые исключения Error

Как вы уже смогли заметить из таблицы иерархий, в PHP 7 добавились новые исключения для отлавливания фатальных (E_ERROR) и восстанавливаемых ошибок (E_RECOVERABLE_ERROR).
Но к сожалению, определенные ошибки, например «out of memory», по прежнему приведут к остановке, как их вообще обрабатывать? ��

Error

Все фатальные ошибки будут выбрасывать исключение расширяющие Error исключения. Ошибки при этом, будут выведены как и раньше, если исключение не обработано.

AssertionError

В PHP 7, есть усовершенствование утверждений, используя assert() функцию, с добавлением нулевой стоимостью утверждений, и выбрасыванием исключений. Для включения данной возможности, нужно установить параметр assert.exception = 1 в php.ini (или с использованием ini_set()).

ParseError


Благодаря выкидыванию исключений ParseError при ошибке включения файла, теперь можно обработать поведение:

TypeError

Исключения TypeError выбрасываются, когда аргументы метода или возвращаемое значение не совпадает с объявленным типом.

Отлавливаем фатальные ошибки в PHP7

Еще одно важное изменение PHP7 в отлавливании фатальных ошибок. Раньше, они ловились и обрабатывались с использованием set_error_handler() . Теперь, в PHP 7, они выкидывают Error исключения, и если, исключения не будут обработаны, то выбрасывается реальная фатальная ошибка, которая больше не будет доступна в set_error_handler() .

Это было сделано для обратной совместимости и для работы в PHP 5.x и 7, вы должны использовать both set_error_handler() и try. catch .

Использование Throwable

В PHP 7, у нас есть общий интерфейс для исключений, и мы могли бы создать наши собственные исключения в иерархии исключений для полной настройки исключений (извиняюсь за тавтологию), просто реализуя Throwable интерфейс. Но к сожалению, мы этого сделать не можем, это означает, что мы все еще должны по-прежнему расширять либо Exception, либо Error, и не можем непосредственно реализовывать Throwable в пользовательских классах.

Но это еще не вся история, есть лайфхак, который не рекомендуется к использованию. Вы можете расширить интерфейс Throwable — а уже потом, реализовать свои исключения типов Error или Exception используя свой расширенный интерфейс.

Заключение

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

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

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

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

Программисты, знакомые с такими языками структурного программирования, как C# и Java, по-видимому, давно привыкли использовать различные встроенные объекты, позволяющие справляться с ошибками и исключительными ситуациями. Таким специалистам будет приятно узнать, что теперь в версии PHP 5 впервые появился объект, предназначенный для обработки исключительных ситуаций, и что синтаксические конструкции для работы с этим объектом весьма напоминают существующие языки, такие как Java. В действительности даже после краткого знакомства с этими синтаксическими конструкциями разработчик может приступить к обработке ошибок и исключительных ситуаций в языке PHP во многом по такому же принципу, который применяется в других объектно-ориентированных языках.

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

Ошибки и исключительные ситуации

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

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

Ниже приведен пример кода, который содержит некоторые средства обнаружения ошибок в том виде, в каком они могло быть реализованы в версии PHP 4 или в одной из предыдущих версий. В этом коде осуществляется выборка переменной POST, содержащей идентификатор пользователя. Такой идентификатор должен иметь длину по меньшей мере девять символов и начинаться с префикса «usr»:

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

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

Класс Exception

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

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

Код PHP Использование исключений

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

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

Активизация исключительной ситуации

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

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

Код PHP Получение дополнительной информации об исключении

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

Определение собственных подклассов Exception

Язык PHP позволяет также определять собственные классы, которые наследуют методы класса Exception. Это означает, что можно больше не ограничиваться использованием лишь функции getMessage() для получения информации о том, какой именно тип имеет возникшая ошибка. Подклассы могут быть определены так, как показано в следующем примере:

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

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

Ограничения средств обработки исключений языка PHP

Объект Exception — новое средство, появившееся в версии PHP 5, поэтому этот объект, как таковой, все еще находится на самых ранних этапах разработки. Ко времени написания этих строк язык PHP не поддерживал использование таких конструкций, как finally() и throws(), предусмотренных в языке Java и других языках. Кроме того, в отличие от других языков на исключительные ситуации еще не отображаются ошибки, распознаваемые самим интерпретатором PHP (в том числе ошибки, сообщения о которых обычно появляются в клиентском браузере). Из-за этого, например, возникновение ошибки в операторе SQL, выполняемом в блоке try/catch, не приводит автоматически к активизации исключительной ситуации, которую можно было бы перехватить и заняться ее обработкой. Такие удобные функциональные средства, по всей вероятности, должны быть включены в одну из будущих версий PHP, поэтому для авторов имеет смысл упомянуть о них, а для читателей — следить за их появлением.

PHP для начинающих. Обработка ошибок // PHP

Не совершает ошибок только тот, кто ничего не делает, и мы тому пример – трудимся не покладая рук над созданием рабочих мест для тестировщиков :)

О да, в этой статье я поведу свой рассказа об ошибках в PHP, и том как их обуздать.

Ошибки

Разновидности в семействе ошибок

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

Чтобы ни одна ошибка не ушла незамеченной потребуется включить отслеживание всех ошибок с помощью функции error_reporting(), а с помощью директивы display_errors включить их отображение:

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

Самый грозный вид ошибок – фатальные, они могут возникнуть как при компиляции, так и при работе парсера или PHP-скрипта, выполнение скрипта при этом прерывается.

E_PARSE
Это ошибка появляется, когда вы допускаете грубую ошибку синтаксиса и интерпретатор PHP не понимает, что вы от него хотите, например если не закрыли фигурную или круглую скобочку:

Или написали на непонятном языке:

Лишние скобочки тоже встречаются, и не важно круглые либо фигурные:

Отмечу один важный момент – код файла, в котором вы допустили parse error не будет выполнен, следовательно, если вы попытаетесь включить отображение ошибок в том же файле, где возникла ошибка парсера то это не сработает:

E_ERROR
Это ошибка появляется, когда PHP понял что вы хотите, но сделать сие не получилось ввиду ряда причин, так же прерывает выполнение скрипта, при этом код до появления ошибки сработает:

Не был найден подключаемый файл:

Было брошено исключение (что это за зверь, расскажу немного погодя), но не было обработано:

При попытке вызвать несуществующий метод класса:

Отсутствия свободной памяти (больше, чем прописано в директиве memory_limit) или ещё чего-нить подобного:

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

Рекурсивный вызов функции. В данном примере он закончился на 256-ой итерации, ибо так прописано в настройках xdebug:

Не фатальные

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

E_WARNING
Частенько встречается, когда подключаешь файл с использованием include , а его не оказывается на сервере или ошиблись указывая путь к файлу:

Бывает, если используешь неправильный тип аргументов при вызове функций:

Их очень много, и перечислять все не имеет смысла…

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

Когда обращаются к неопределенной переменной:

Когда обращаются к несуществующему элементу массива:

Когда обращаются к несуществующей константе:

Когда не конвертируют типы данных:

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

E_STRICT
Это ошибки, которые научат вас писать код правильно, чтобы не было стыдно, тем более IDE вам эти ошибки сразу показывают. Вот например, если вызвали не статический метод как статику, то код будет работать, но это как-то неправильно, и возможно появление серьёзных ошибок, если в дальнейшем метод класса будет изменён, и появится обращение к $this :

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

В моём редакторе подобные функции будут зачёркнуты:


Обрабатываемые

Этот вид, которые разводит сам разработчик кода, я их уже давно не встречал, не рекомендую их вам заводить:

  • E_USER_ERROR – критическая ошибка
  • E_USER_WARNING – не критическая ошибка
  • E_USER_NOTICE – сообщения которые не являются ошибками

Отдельно стоит отметить E_USER_DEPRECATED – этот вид всё ещё используется очень часто для того, чтобы напомнить программисту, что метод или функция устарели и пора переписать код без использования оной. Для создания этой и подобных ошибок используется функция trigger_error():

Теперь, когда вы познакомились с большинством видов и типов ошибок, пора озвучить небольшое пояснение по работе директивы display_errors :

  • если display_errors = on , то в случае ошибки браузер получит html c текстом ошибки и кодом 200
  • если же display_errors = off , то для фатальных ошибок код ответа будет 500 и результат не будет возвращён пользователю, для остальных ошибок – код будет работать неправильно, но никому об этом не расскажет

Приручение

Для работы с ошибками в PHP существует 3 функции:

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

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

  • $errno – первый аргумент содержит тип ошибки в виде целого числа
  • $errstr – второй аргумент содержит сообщение об ошибке
  • $errfile – необязательный третий аргумент содержит имя файла, в котором произошла ошибка
  • $errline – необязательный четвертый аргумент содержит номер строки, в которой произошла ошибка
  • $errcontext – необязательный пятый аргумент содержит массив всех переменных, существующих в области видимости, где произошла ошибка

В случае если обработчик вернул true , то ошибка будет считаться обработанной и выполнение скрипта продолжится, иначе — будет вызван стандартный обработчик, который логирует ошибку и в зависимости от её типа продолжит выполнение скрипта или завершит его. Вот пример обработчика:

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

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

Данная функция будет срабатывать всегда!

Но вернёмся к ошибкам, для отслеживания появления в коде ошибки воспользуемся функцией error_get_last(), с её помощью можно получить информацию о последней выявленной ошибке, а поскольку фатальные ошибки прерывают выполнение кода, то они всегда будут выполнять роль “последних”:

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

О прожорливости

Проведём простой тест, и выясним – сколько драгоценных ресурсов кушает самая тривиальная ошибка:

В результате запуска данного скрипта у меня получился вот такой результат:

Теперь добавим ошибку в цикле:

Результат ожидаемо хуже, и на порядок (даже на два порядка!):

Вывод однозначен – ошибки в коде приводят к лишней прожорливости скриптов – так что во время разработки и тестирования приложения включайте отображение всех ошибок!

Тестирование проводил на PHP версии 5.6, в седьмой версии результат лучше – 0.0004 секунды против 0.0050 – разница только на один порядок, но в любом случае результат стоит прикладываемых усилий по исправлению ошибок

Где собака зарыта

В PHP есть спец символ «@» – оператор подавления ошибок, его используют дабы не писать обработку ошибок, а положится на корректное поведение PHP в случае чего:

При этом обработчик ошибок указанный в set_error_handler() всё равно будет вызван, а факт того, что к ошибке было применено подавление можно отследить вызвав функцию error_reporting() внутри обработчика, в этом случае она вернёт 0 .

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

Исключения

В эру PHP4 не было исключений (exceptions), всё было намного сложнее, и разработчики боролись с ошибками как могли, это было сражение не на жизнь, а на смерть… Окунуться в эту увлекательную историю противостояния можете в статье Исключительный код. Часть 1. Стоит ли её читать сейчас? Думаю да, ведь это поможет вам понять эволюцию языка, и раскроет всю прелесть исключений

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

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

Исключение – это объект который наследуется от класса Exception , содержит текст ошибки, статус, а также может содержать ссылку на другое исключение которое стало первопричиной данного. Модель исключений в PHP схожа с используемыми в других языках программирования. Исключение можно инициировать (как говорят, “бросить”) при помощи оператора throw , и можно перехватить (“поймать”) оператором catch . Код генерирующий исключение, должен быть окружен блоком try , для того чтобы можно было перехватить исключение. Каждый блок try должен иметь как минимум один соответствующий ему блок catch или finally :

В каких случаях стоит применять исключения:

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

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

Соответственно ловить данные исключения будем примерно так:

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

Теперь, если использовать эти исключения то можно получить следующий код:

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

Так, а что будет если не поймать исключение? Вы получите “Fatal Error: Uncaught exception …”. Неприятно.
Чтобы избежать подобной ситуации следует использовать функцию set_exception_handler() и установить обработчик для исключений, которые брошены вне блока try-catch и не были обработаны. После вызова такого обработчика выполнение скрипта будет остановлено:

Ещё расскажу про конструкцию с использованием блока finally – этот блок будет выполнен вне зависимости от того, было выброшено исключение или нет:

Для понимания того, что это нам даёт приведу следующий пример использования блока finally :

Т.е. запомните – блок finally будет выполнен даже в том случае, если вы в блоке catch пробрасываете исключение выше (собственно именно так он и задумывался).

Для вводной статьи информации в самый раз, кто жаждет ещё подробностей, то вы их найдёте в статье Исключительный код ;)

Задание
Написать свой обработчик исключений, с выводом текста файла где произошла ошибка, и всё это с подсветкой синтаксиса, так же не забудьте вывести trace в читаемом виде. Для ориентира – посмотрите как это круто выглядит у whoops.

PHP7 – всё не так, как было раньше

Так, вот вы сейчас всю информацию выше усвоили и теперь я буду грузить вас нововведениями в PHP7, т.е. я буду рассказывать о том, с чем вы столкнётесь через год работы PHP разработчиком. Ранее я вам рассказывал и показывал на примерах какой костыль нужно соорудить, чтобы отлавливать критические ошибки, так вот – в PHP7 это решили исправить, но как обычно завязались на обратную совместимость кода, и получили хоть и универсальное решение, но оно далеко от идеала. А теперь по пунктам об изменениях:

  1. при возникновении фатальных ошибок типа E_ERROR или фатальных ошибок с возможностью обработки E_RECOVERABLE_ERROR PHP выбрасывает исключение
  2. эти исключения не наследуют класс Exception (помните я говорил об обратной совместимости, это всё ради неё)
  3. эти исключения наследуют класс Error
  4. оба класса Exception и Error реализуют интерфейс Throwable
  5. вы не можете реализовать интерфейс Throwable в своём коде

Интерфейс Throwable практически полностью повторяет нам Exception :

Сложно? Теперь на примерах, возьмём те, что были выше и слегка модернизируем:

В результате ошибку поймаем и выведем:

Как видите – поймали исключение ParseError, которое является наследником исключения Error , который реализует интерфейс Throwable , в доме который построил Джек. Ещё есть другие, но не буду мучать – для наглядности приведу иерархию исключений:

TypeError – для ошибок, когда тип аргументов функции не совпадает с передаваемым типом:

ArithmeticError – могут возникнуть при математических операциях, к примеру когда результат вычисления превышает лимит выделенный для целого числа:

AssertionError – редкий зверь, появляется когда условие заданное в assert() не выполняется:

При настройках production-серверов, директивы zend.assertions и assert.exception отключают, и это правильно

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

При написании данного раздела были использованы материалы из статьи Throwable Exceptions and Errors in PHP 7

Отладка

Иногда для отладки кода нужно отследить что происходило с переменной или объектом на определённом этапе, для этих целей есть функция debug_backtrace() и debug_print_backtrace() которые вернут историю вызовов функций/методов в обратном порядке:

В результате выполнения функции debug_print_backtrace() будет выведен список вызовов приведших нас к данной точке:

Проверить код на наличие синтаксических ошибок можно с помощью функции php_check_syntax() или же команды php -l [путь к файлу] , но я не встречал использования оных.

Assert

Отдельно хочу рассказать о таком экзотическом звере как assert() в PHP, собственно это кусочек контрактной методологии программирования, и дальше я расскажу вам как я никогда его не использовал :)

Первый случай – это когда вам надо написать TODO прямо в коде, да так, чтобы точно не забыть реализовать заданный функционал:

В результате выполнения данного кода получим E_WARNING :

PHP7 можно переключить в режим exception, и вместо ошибки будет всегда появляться исключение AssertionError :

В результате ожидаемо получаем не пойманный AssertionError . При необходимости, можно выбрасывать произвольное исключение:

Но я бы рекомендовал использовать метки @TODO , современные IDE отлично с ними работают, и вам не нужно будет прикладывать дополнительные усилия и ресурсы для работы с ними

Второй вариант использования – это создание некоего подобия TDD, но помните – это лишь подобие. Хотя, если сильно постараться, то можно получить забавный результат, который поможет в тестировании вашего кода:

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

Никогда не используйте assert() для проверки входных параметров, ведь фактически assert() интерпретирует строковую переменную (ведёт себя как eval() ), а это чревато PHP-инъекцией. И да, это правильное поведение, т.к. просто отключив assert’ы всё что передаётся внутрь будет проигнорировано, а если делать как в примере выше, то код будет выполняться, а внутрь отключенного assert’a будет передан булевый результат выполнения

Если у вас есть живой опыт использования assert() – поделитесь со мной, буду благодарен. И да, вот вам ещё занимательно чтива по этой теме – PHP Assertions, с таким же вопросом в конце :)

В заключение

Я за вас напишу выводы из данной статьи:

  • Ошибкам бой – их не должно быть в вашем коде
  • Используйте исключения – работу с ними нужно правильно организовать и будет счастье
  • Assert – узнали о них, и хорошо
Илон Маск рекомендует:  Что такое код asp logcustompropertyheader
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL