Исключения

Содержание

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

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

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

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

catch

Блок catch определяет то, как следует реагировать на выброшенное исключение. В блоке catch указывается один или более типов исключений или ошибок(Error), которые он будет обрабатывать. Также указывается и переменная, которой будет присвоено пойманное исключение (начиная с PHP 8.0.0 задавать эту переменную не обязательно). Выброшенное исключение или ошибка будут обработаны первым подходящим блоком catch.

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

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

Начиная с PHP 7.1.0, блок catch может принимать несколько типов исключений с помощью символа (|). Это полезно, когда разные исключения из разных иерархий классов обрабатываются одинаково.

Начиная с PHP 8.0.0, задание переменной для пойманного исключения опционально. Если она не задана, блок catch будет исполняться, но не будет иметь доступа к объекту исключения.

finally

Блок finally также можно использовать после или вместо блока catch. Код в блоке finally всегда будет выполняться после кода в блоках try и catch, независимо от того, было ли выброшено исключение, перед тем как продолжится нормальное выполнение кода.

Одно важное взаимодействие происходит между блоком finally и оператором return. Если оператор return встречается внутри блоков try или catch, блок finally все равно будет выполнен. Кроме того, оператор return выполняется, когда встречается, но результат будет возвращен после выполнения блока finally. Если блок finally также содержит оператор return, возвращается значение, указанное в блоке finally.

Глобальный обработчик исключений

Если исключение дошло по стеку вызовов до глобальной области видимости, оно может быть обработано глобальным обработчиком исключений, если он задан. С помощью функции set_exception_handler() можно задать функцию, которая будет выполнена вместо блока catch, если не нашлось подходящего. Эффект аналогичен тому, как будто мы всю нашу программу обернули в блок try-catch, где за реализацию блока catch отвечает установленная функция.

Примечания

Замечание:

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

Пример #3 Преобразование сообщения об ошибках в исключение

<?php
function exceptions_error_handler($severity$message$filename$lineno) {
    throw new 
ErrorException($message0$severity$filename$lineno);
}

set_error_handler('exceptions_error_handler');
?>

Подсказка

Стандартная библиотека PHP (SPL) предоставляет хороший набор встроенных классов исключений.

Примеры

Пример #4 Выбрасывание исключений

<?php
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 "Привет, мир\n";
?>

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

0.2
Выброшено исключение: Деление на ноль.
Привет, мир

Пример #5 Обработка исключений с помощью блока finally

<?php
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 "Привет, мир\n";
?>

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

0.2
Первый блок finally.
Поймано исключение: Деление на ноль.
Второй блок finally.
Привет, мир

Пример #6 Взаимодействие между блоками finally и return

<?php

function test() {
    try {
        throw new 
Exception('foo');
    } catch (
Exception $e) {
        return 
'catch';
    } finally {
        return 
'finally';
    }
}

echo 
test();
?>

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

finally

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

<?php

class MyException extends Exception { }

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();

?>

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

string(4) "foo!"

Пример #8 Обработка нескольких исключений в одном блоке catch

<?php

class MyException extends Exception { }

class 
MyOtherException extends Exception { }

class 
Test {
    public function 
testing() {
        try {
            throw new 
MyException();
        } catch (
MyException MyOtherException $e) {
            
var_dump(get_class($e));
        }
    }
}

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

?>

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

string(11) "MyException"

Пример #9 Пример блока catch без указания переменной

Допустимо начиная с PHP 8.0.0

<?php

class SpecificException extends Exception {}

function 
test() {
    throw new 
SpecificException('Ой!');
}

try {
    
test();
} catch (
SpecificException) {
    print 
"Было поймано исключение SpecificException, но нам безразлично, что у него внутри.";
}
?>

Пример #10 Throw как выражение

Only permitted in PHP 8.0.0 and later.

<?php

class SpecificException extends Exception {}

function 
test() {
    
do_something_risky() or throw new Exception('Всё сломалось');
}

try {
    
test();
} catch (
Exception $e) {
    print 
$e->getMessage();
}
?>