Обработка ошибок и исключений php
Обработка ошибочных ситуаций в PHP
«Если человек может сделать ошибку, он ее сделает. Если же вы полагаете, что ошибку в данной ситуации совершить невозможно, то вы ошибаетесь».
В PHP все виды ошибок и предупреждений разбиты на несколько категорий:
Константа | Описание | ||||||||||||||||||||||
E_ALLВсе типы ошибок | E_ERROR | Фатальная ошибка на этапе исполнения | E_WARNING | Предупреждение на этапе исполнения (нефатальная ошибка) | E_PARSE | Ошибка синтаксиса | E_NOTICE | Замечание на этапе исполнения (менее серьезное, чем предупреждение) | E_CORE_ERROR | Фатальная ошибка на этапе инициализации PHP-машины | E_CORE_WARNING | Предупреждение на этапе инициализации PHP-машины (нефатальная ошибка) | E_COMPILE_ERROR | Фатальная ошибка на этапе компиляции | E_COMPILE_WARNING | Предупреждение на этапе компиляции (нефатальная ошибка) | E_USER_ERROR | Генерируемое пользователем сообщение об ошибке | E_USER_WARNING | Генерируемое пользователем предупреждение | E_USER_NOTICE | Генерируемое пользователем замечание | |
Стандартное значение режима генерации сообщений об ошибках равно E_ALL &
E_NOTICE, что соответствует выводу всех сообщений, не относящихся к категории E_NOTICE.
Функция настройки системного журнала error_reporting() устанавливает уровень выводимых соообщений об ошибках. Этой функции передаются константы, описанные выше.
Обработка исключений (exception)
Обработать возникающие исключения можно с помощью конструкции:
Методы исключений
PHP позволяет использовать свой обработчик исключений. Для этого необходимо объявить собственную функцию обработки и зарегистрировать её при помощи функции set_exception_handler().
После этого все возникающие исключения, не обрамлённые конструкцией try. catch, будут передаваться в объявленную вами функцию. Например, можно изменить функцию, чтобы она писала все исключения в файл и выдавала пользователю в браузер "красивое" сообщение об ошибке сервера:
Эта функция очень полезна в процессе отладки скриптов непосредственно на сервере.
Исключения PHP: Try Catch для обработки ошибок
Что такое исключение?
Здесь важно отметить, что обработка исключений отличается от обработки ошибок. При обработке ошибок мы можем использовать функцию set_error_handler для установки нашей настраиваемой функции обработки ошибок, чтобы всякий раз, когда срабатывает ошибка, она вызывала нашу функцию обработки ошибок. Таким образом, вы можете управлять ошибками. Однако, как правило, некоторые виды ошибок не восстанавливаются и прекращают выполнение программы.
Поток управления обработкой исключений
Давайте рассмотрим следующую диаграмму, которая показывает общий поток управления обработкой исключений.
Выброс исключения
Исключение может быть вызвано функцией, которую вы вызываете, или вы можете использовать ключевое слово throw для выбрасывания исключения вручную. Например, вы можете проверить некоторый ввод перед выполнением любой операции и выбросить исключение, если данные недействительны.
В следующем разделе мы рассмотрим пример из реального мира, чтобы понять, как работает обработка исключений.
Пример из реального мира
В этом разделе мы построим реальный пример для демонстрации обработки исключений в PHP.
Предположим, что вы создали приложение, которое загружает конфигурацию приложения из файла config.php. Теперь важно, чтобы файл config.php присутствовал, когда ваше приложение загружается. Таким образом, ваше приложение не может работать, если файл config.php отсутствует. Так что это идеальный случай, чтобы выбросить исключение и сообщить пользователю, что им необходимо исправить проблему.
Как вы можете видеть в приведенном выше примере, мы проверяем наличие файла config.php в начале фазы начальной загрузки. Если файл config.php найден, выполнение продолжается в обычном режиме. С другой стороны, мы выбросим исключение, если файл config.php не существует. Кроме того, мы хотели бы прекратить выполнение, если есть исключение!
Таким образом, это был пример обработки исключений с использованием класса Exception по умолчанию. В следующем разделе мы рассмотрим, как вы можете расширить основной класс Exception и создать свои собственные пользовательские исключения в своем приложении.
Как создавать пользовательские исключения
В этом разделе мы обсудим, как вы можете создавать пользовательские исключения в своих приложениях. Фактически, мы расширим пример, который мы только что обсуждали в предыдущем разделе, чтобы продемонстрировать пользовательские исключения.
В предыдущем примере мы выбрали исключение конфигурации, используя класс Exception по умолчанию. Это прекрасно, если вы просто хотите иметь дело с сообщением об ошибке. Однако иногда вы хотите сделать немного больше в зависимости от типа исключения, которое бросается. Вот почему пользовательские исключения полезны.
Перейдем к предыдущему примеру, как показано в следующем фрагменте.
Блок Finally
Попробуем понять это, используя следующий пример.
Заключение
Правильная работа с исключениями в PHP
class baseException extends Exception
Таким образом, все исключения вашего кода можно будет отличить от исключений не вашего кода.
2. Исключения должны быть иерархичны. У вас должен быть базовый класс исключений, от которого наследуются все исключения, бросаемые в вашем коде. Например, у вас в коде есть модуль для работы с файлами fileModule, объявите исключение, которое будет бросаться только этим модулем
class fileModuleException extends baseException
class fileNotFoundException extends fileModuleException < >
3. Не обрабатывайте исключения, если в данном контексте не понятно, как его обработать. Например, если вы следуете паттерну MVC, то в методе модели может быть не понятно, как обработать ошибку — как ее вывести, потому как за логику отвечает control, а за вывод view. Если не понятно, что делать с исключением, то «пробросьте» его дальше.
Тут очень важный момент — не разрывать цепь исключений. Третьим параметром передается изначальное исключение. Этот код нативно работает в 5.3 и с доработкой в 5.2. При таком подходе стек вызовов будет «цельным» от самого первого броска исключения.
4. У вас должен быть глобальный обработчик исключений. Это может быть или try. catch на самом верхнем уровне или ExceptionHandler. Все исключения, которые добрались до глобального обработчика, считаются критическими, так как не были правильно обработаны ранее. Их надо залогировать.
5. Исключение это объект, соответственно его можно расширять под свои потребности. Допустим у вас многоязычное приложение и текст ошибки в бросаемом исключении нужно выводить пользователю. Соответственно это сообщение нужно переводить. Это не сложно, если сообщение без переменных частей, например, «Ошибка при выполнении операции». Но что делать, если в сообщение входят переменные части, например, «У вас недостаточно денег на балансе (1000). Нужно 2000». Тогда можно отдельно передать шаблон текста ошибки и отдельно сами переменные. Пример кода Старый пример кода.
6. Преобразуйте все ошибки утверждений (assertion fail) и не фатальные ошибки в исключения (см. мою предыдущую статью)
7. Никогда не глушите исключения без какой либо обработки
8. Документируйте исключения. Указывайте в докблоке, какие исключения выбрасывает метод (таг @throws, можно указывать больше одного). Это упростит всем жизнь.
Вот в принципе и все, что нужно знать про исключения. Еще один интересный факт напоследок — исключения можно ловить по интерфейсу:
UPD исправлены замечания в комментариях:1, 2 и 3 (спасибо всем, кто поучаствовал в обсуждении).
Отдельное спасибо, хабраюзеру ckopobapkuh за активное участие
Обработка исключений
Конструкция try catch finally
В процессе работы программы могут возникать различные ошибки, которые могут прервать работу программы. Например, рассмотрим следующую ситуацию:
Программа выводит результат деления. Поскольку делитель равен 0, а на ноль делить нельзя, то при выполнении деления программа завершится, и в браузере мы увидим что-то типа следующего:
Браузер отобразит нам произошедшую ошибку, причем дальше после строки с делением программа даже не будет выполняться.
Кто-то может сказать, что ситуация искуственная, так как мы сами определили делитель равный нулю. Но данные могут передаваться извне. Кроме того, кроме деления на ноль есть различные ситуации, при которых могут происходить ошибки. Но PHP предоставляет ряд возможностей для обработки подобных ситуаций.
Для обработки исключений в PHP применяется конструкция try-catch :
Например, обработаем ошибку с делением на ноль:
В итоге при выполнении программа выведет следующее:
Как видно из вывода программы, она не завершается аварийно при делении на ноль, а продолжает работу.
Типы ошибок и исключений
В PHP для разных ситуаций есть множество типов, которые описывают ошибки. Все эти встроенные типы применяют интерфейс Throwable :
Блок catch
Класс DivisionByZeroError унаследован от ArithmeticError, который, в свою очередь, унаследован от Error, реализующего интерфейс Throwable. Поэтому класс DivisionByZeroError представляет более конкретный тип и представляемые им ошибки должны обрабатываться в первую очередь. А тип Throwable представляет наиболее общий тип, так как ему соответствуют все возможные ошибки и исключения, поэтому блоки catch с таким типом должны идти в конце.
Если нам надо обрабатывать в принципе все ошибки и исключения, то мы можем определить только обработку общего для всех них типа Throwable:
Начиная с версии PHP 8.0 в блоке catch можно просто указать тип обрабатываемого исключения, не определяя переменную:
Получение информации об ошибках и исключениях
Интерфейс Throwable предоставляет ряд методов, которые позволяют получить некоторую информацию о возникшем исключении:
getMessage() : возвращает сообщение об ошибке
getCode() : возвращает код исключения
getFile() : возвращает название файла, в котором возникла ошибка
getLine() : возвращает номер строки, в которой возникла ошибка
getTrace() : возвращает трассировку стека
getTraceAsString() : возвращает трассировку стека в виде строки
Применим некоторые из этих методов:
Блок finally
Исключения
Содержание
Начиная с PHP 8.0.0, ключевое слово throw является выражением и может использоваться в контексте других выражений. В более ранних версиях оно являлось оператором и требовало размещения в отдельной строке.
catch
Начиная с PHP 7.1.0, блок catch может принимать несколько типов исключений с помощью символа ( | ). Это полезно, когда разные исключения из разных иерархий классов обрабатываются одинаково.
Начиная с PHP 8.0.0, задание переменной для пойманного исключения опционально. Если она не задана, блок catch будет исполняться, но не будет иметь доступа к объекту исключения.
finally
Глобальный обработчик исключений
Примечания
Внутренние функции PHP в основном используют сообщения об ошибках, и только новые объектно-ориентированные модули используют исключения. Однако, ошибки можно легко преобразовать в исключения с помощью класса ErrorException. Однако это не сработает для фатальных ошибок.
Пример #3 Преобразование сообщения об ошибках в исключение
Примеры
Пример #4 Выбрасывание исключений
// Продолжение выполнения
echo "Привет, мир\n" ;
?>
Результат выполнения данного примера:
Пример #5 Обработка исключений с помощью блока finally
// Продолжение нормального выполнения
echo "Привет, мир\n" ;
?>
Результат выполнения данного примера:
Пример #6 Взаимодействие между блоками finally и return
Результат выполнения данного примера:
Пример #7 Вложенные исключения
Результат выполнения данного примера:
Пример #8 Обработка нескольких исключений в одном блоке catch
class MyOtherException extends Exception
Результат выполнения данного примера:
Пример #9 Пример блока catch без указания переменной
Допустимо начиная с PHP 8.0.0
class SpecificException extends Exception <>
function test () <
throw new SpecificException ( 'Ой!' );
>
try <
test ();
> catch ( SpecificException ) <
print "Было поймано исключение SpecificException, но нам безразлично, что у него внутри." ;
>
?>
Пример #10 Throw как выражение
Допустимо начиная с PHP 8.0.0
class SpecificException extends Exception <>
function test () <
do_something_risky () or throw new Exception ( 'Всё сломалось' );
>
User Contributed Notes 12 notes
If you intend on creating a lot of custom exceptions, you may find this code useful. I've created an interface and an abstract exception class that ensures that all parts of the built-in Exception class are preserved in child classes. It also properly pushes all information back to the parent constructor ensuring that nothing is lost. This allows you to quickly create new exceptions on the fly. It also overrides the default __toString method with a more thorough one.
interface IException
<
/* Protected methods inherited from Exception class */
public function getMessage (); // Exception message
public function getCode (); // User-defined Exception code
public function getFile (); // Source filename
public function getLine (); // Source line
public function getTrace (); // An array of the backtrace()
public function getTraceAsString (); // Formated string of trace
class TestException extends CustomException <>
?>
Here's a test that shows that all information is properly preserved throughout the backtrace.
echo '' ;
?>
Here's a sample output: