Исключения (exceptions) PHP
В этом уроке вы узнаете, как генерировать и перехватывать исключения (exceptions) в PHP.
Что такое исключение?
Как и в других языках, PHP имеет механизм исключений для обработки пользовательских ошибок. Исключение — это сигнал, указывающий на то, что что-то в процессе отличается от предполагаемого хода событий. Исключения могут быть вызваны различными причинами, обычно в случае ошибки, которые проявляются или могут быть специально созданы программой. В большинстве случаев подобные ситуации требуют выполнения специальных инструкций, чтобы предотвратить неконтролируемое завершение процесса.
Многие функции и классы PHP создают исключения.
Пользовательские функции и классы также могут вызывать исключения.
Выброс исключения
Исключения могут быть брошены throw
и пойманы catch
. Если исключение брошено, значит произошло что-то выходящее за рамки нормального течения процесса работы сценария, и необходимо выполнение какой-то другой функции. Подхват исключения происходит в специальной функции, которая сообщает остальной программе о том, что она готова обработать исключение.
Функция try-catch
может самостоятельно выполнить необходимые действия или перебросить исключение другой функции.
Исключение может быть вызвано функцией, которую мы вызываем, или мы можем использовать ключевое слово throw
для выбрасывания исключения вручную. Например, мы можем проверить некоторый ввод перед выполнением любой операции и выбросить исключение, если данные недействительны.
Здесь важно отметить, что если мы выбрасываем исключение, но не определили блок catch
, который должен обрабатывать это исключение, это приведет к фатальной ошибке. Поэтому важно убедиться, что мы определили блок catch
, если бросаем исключения в своем приложении.
Если исключение не обнаружено, произойдет фатальная ошибка с сообщением "Uncaught Exception" (Неперехваченное исключение).
Попробуем сгенерировать исключение, не улавливая его:
Пример
Попробуй сам »<?php
function divide($dividend, $divisor) {
// Выбросить исключение, если делитель равен нулю
if($divisor == 0) {
throw new Exception("Деление на ноль");
}
return $dividend / $divisor;
}
echo divide(5, 0);
?>
Результат выполнения кода:
Конструкция try ... catch
Исключение генерируется оператором throw new Exception()
, а ловится операторами try
и catch
.
Чтобы избежать ошибки из приведенного выше примера, мы можем использовать оператор try...catch
для перехвата исключений и продолжения процесса.
Синтаксис
try { код, который может бросать (throw) исключения } catch(Исключение $e) { код, который запускается при обнаружении исключения }
При подходе, основанном на исключениях, программный код записывается в блоке try
, исключение может быть создано с помощью оператора throw
, когда во время выполнения кода в блоке try
происходит исключительное событие. Затем он вылавливается и разрешается одним или несколькими блоками catch
.
В следующем примере покажем сообщение при возникновении исключения:
Пример
Попробуй сам »<?php
function divide($dividend, $divisor) {
if($divisor == 0) {
throw new Exception("Деление на ноль");
}
return $dividend / $divisor;
}
try {
echo divide(5, 0);
} catch(Exception $e) {
echo "Невозможно разделить!";
}
?>
Когда исключение попадает в блок catch
, объект Exception
содержит сообщение об ошибке, которое было выбрано с использованием ключевого слова throw
. Переменная $e
, в приведенном выше примере, является экземпляром класса Exception
, поэтому она имеет доступ ко всем методам этого класса. В этом блоке мы должны определить свою собственную логику обработки исключений — что именно мы хотим сделать с ошибкой, которую вы поймаете.
Конструкция try…catch…finally
Иногда возникает необходимость выполнить часть кода независимо от того, было ли вызвано исключение в блоке try
. Вот где мы можем использовать блок finally
, поскольку код, который мы размещаем в блоке finally
, всегда будет выполняться после выполнения блоков try
и catch
независимо от того, было ли выбрано исключение.
Важно понять, что catch
выполнится в любом случае перед выходом из конструкции try...catch...finally
, то есть эта особенность пригодится для обработки ситуаций, когда блок catch
не отработал, а исключение возникло. Например, если блока catch
нет вообще или в подобной ситуации, когда ожидается исключение определенного вида, а вызывается другое:
try { echo "В try вызвано исключение\n"; throw new StrangeException(); } catch (SomeException $e) { echo "Вызвано исключение SomeException \n"; } catch (AnotherException $e) { echo "Вызвано исключение AnotherException \n"; } finally { echo "Этот блок кода выполнится всегда\n"; }
В следующем примере покажем сообщение при возникновении исключения, а затем укажем, что процесс завершен:
Пример
Попробуй сам »<?php
function divide($dividend, $divisor) {
if($divisor == 0) {
throw new Exception("Деление на ноль");
}
return $dividend / $divisor;
}
try {
echo divide(5, 0);
} catch(Exception $e) {
echo "Невозможно разделить! ";
} finally {
echo "Процесс завершен.";
}
?>
Результат выполнения кода:
В следующем примере смоделируем ситуацию, когда блока catch
нет вообще, т.е. выведем строку "Процесс завершен", даже если исключение не было обнаружено:
Пример
Попробуй сам »<?php
function divide($dividend, $divisor) {
if($divisor == 0) {
throw new Exception("Деление на ноль");
}
return $dividend / $divisor;
}
try {
echo divide(5, 0);
} finally {
echo "Процесс завершен.";
}
?>
Объект исключения (Exception)
Объект Exception
содержит информацию об ошибке или непредвиденном поведении, с которым столкнулась функция.
Синтаксис
Параметры
Параметр | Описание |
---|---|
message |
Необязательный. Строка, описывающая, почему было сгенерировано исключение. |
code |
Необязательный. Целое число, которое можно использовать, чтобы легко отличить это исключение от других того же типа. |
previous |
Необязательный. Если это исключение было сгенерировано в блоке catch другого исключения, рекомендуется передать это исключение в этот параметр. |
Методы класса Exception РНР
Класс Exception
РНР также предоставляет методы, которые могут быть использованы для создания подробной информации отладки.
В следующей таблице показаны некоторые методы, которые можно использовать для получения информации при перехвате исключения:
Метод | Описание |
---|---|
getMessage() |
Возвращает строку, описывающую, почему было создано исключение. |
getPrevious() |
Если это исключение было вызвано другим, этот метод возвращает предыдущее исключение. Если нет, то возвращается null |
getCode() |
Возвращает код исключения |
getFile() |
Возвращает полный путь к файлу, в котором возникло исключение. |
getLine() |
Возвращает номер строки кода, вызвавшего исключение. |
Используем приведенные методы для для создания подробной информации о сгенерированном исключении:
Пример
Попробуй сам »<?php
function divide($dividend, $divisor) {
if($divisor == 0) {
throw new Exception("Деление на ноль", 1);
}
return $dividend / $divisor;
}
try {
echo divide(5, 0);
} catch(Exception $ex) {
$code = $ex->getCode();
$message = $ex->getMessage();
$file = $ex->getFile();
$line = $ex->getLine();
echo "Исключение добавлено в $file в строке $line: [Код $code]
$message";
}
?>
Примечание: Исключения следует использовать только для обозначения исключительных условий. Не рекомендуем использовать их для управления обычным потоком программы, например, для перехода в другое место в скрипте в определенной точке, т.к. это отрицательно скажется на производительности вашей прграммы.
Определение пользовательских исключений
Мы можем определить свои собственные обработчики исключений, чтобы по-своему обрабатывать различные типы исключений. Это позволяет нам использовать для каждого типа исключения отдельный блок catch
.
Учитывая, что класс Exception является базовым классом для всех исключений, мы можем, расширив класс Exception, определить свое настраиваемое исключение. Пользовательский класс исключений наследует все свойства и методы класса Exception PHP. Давайте посмотрим на следующий пример, в котором добавим свои собственные методы в собственный класс исключений:
Пример
Попробуй сам »<?php
// Расширение класса Exception
class EmptyEmailException extends Exception {}
class InvalidEmailException extends Exception {}
$email = "someuser@example..com";
try{
// Выбрасывать исключение, если электронная почта пуста
if($email == ""){
throw new EmptyEmailException("Пожалуйста, введите ваш E-mail адрес!
");
}
// Выбрасывать исключение, если адрес электронной почты недействителен
if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
throw new InvalidEmailException("$email - это невалидный E-mail адрес!
");
}
// Отобразить сообщение, если адрес электронной почты действителен
echo "SUCCESS: Email проверку прошел успешно
";
} catch(EmptyEmailException $e){
echo $e->getMessage();
} catch(InvalidEmailException $e){
echo $e->getMessage();
}
?>
В приведенном выше примере мы создали два новых класса исключений: EmptyEmailException и InvalidEmailException из базового класса Exception
. Блоки catch
используются для отображения различных сообщений об ошибках в зависимости от типа созданного исключения.
Созданные пользовательские классы исключений, как отмечалось выше, наследуют свойства и методы класса Exception
, поэтому мы использовали методы класса Exception
для получения информации об ошибке из объекта исключения.
Установка глобального обработчика исключений
Как уже отмечалось ранее, если исключение не перехвачено, PHP генерирует фатальную ошибку с сообщением "Uncaught Exception" (Неперехваченное исключение). Это сообщение об ошибке может содержать конфиденциальную информацию, такую как имя файла и номер строки, в которой возникает проблема. Если, по какой-либо причине, вам необходимо срыть эту информацию от пользователя, вы можете создать настраиваемую функцию и зарегистрировать ее в функции set_exception_handler()
для обработки всех неперехваченных исключений.
Пример
Попробуй сам »<?php
function handleUncaughtException($e){
// Отображение общего сообщения об ошибке для пользователя
echo "Оппс! Что-то пошло не так. Повторите попытку или свяжитесь с нами, если проблема не исчезнет.";
// Создадим строку ошибки
$error = "Неперехваченное исключение: " . $message = date("Y-m-d H:i:s - ");
$error .= $e->getMessage() . " в файле " . $e->getFile() . " на строке " . $e->getLine() . "\n";
// Записать сведения об ошибке в файл
error_log($error, 3, "var/log/exceptionLog.log");
}
// Зарегистрировать пользовательский обработчик исключений
set_exception_handler("handleUncaughtException");
// Бросить исключение
throw new Exception("Тестовое исключение!");
?>
Результат выполнения кода:
Примечание: Неперехваченное исключение всегда приводит к завершению работы скрипта. Поэтому, если вам нужно, чтобы сценарий продолжал выполняться после точки остановки, в которой произошло исключение, у вас должен быть хотя бы один соответствующий блок catch
для каждого блока try
.