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

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

catch

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

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

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

finally

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

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

Пример #1

Выбрасывание исключений:

function inverse($x) {
    if (!$x) {
        throw new Exception('Деление на ноль.');
    }
	
    return 1/$x;
}

try {
    echo inverse(5);
    echo inverse(0);
} catch (Exception $e) {
    echo 'Выброшено исключение: ', $e->getMessage();
}

// Продолжение выполнения
echo "Привет, мир";

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

Пример #2

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

function inverse($x) {
    if (!$x) {
        throw new Exception('Деление на ноль.');
    }
	
    return 1/$x;
}

try {
    echo inverse(5);
} catch (Exception $e) {
    echo 'Поймано исключение: ', $e->getMessage();
} finally {
    echo "Первый блок finally";
}

try {
    echo inverse(0);
} catch (Exception $e) {
    echo 'Поймано исключение: ', $e->getMessage();
} finally {
    echo "Второй блок finally";
}

// Продолжение нормального выполнения
echo "Привет, мир";

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

Пример #3

Взаимодействие между блоками finally и return:

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

echo test();
// finally

Пример #4

Вложенные исключения:

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!"

Пример #5

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

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"