throw
Ключевое слово throw в PHP используется для генерации исключений при обработке ошибок и нештатных ситуаций в коде.
Ключевое слово PHP throw
Ключевое слово throw останавливает нормальное выполнение в точке вызова и генерирует исключение — объект, сигнализирующий о том, что что-то пошло не так. Управление немедленно выходит из текущей функции и поднимается по стеку вызовов до ближайшего подходящего блока catch. Если ни один catch не соответствует, PHP завершает скрипт с фатальной ошибкой.
На этой странице рассматривается синтаксис throw, когда его следует использовать вместо возврата значения-ошибки, как генерировать встроенные и пользовательские исключения, повторная генерация и цепочка исключений, а также возможность PHP 8, позволяющая использовать throw как выражение. Для общего понимания смотрите раздел Исключения в PHP и поток try/catch/finally.
Синтаксис
throw new Exception("Error message here");Оператор throw требует значение, являющееся экземпляром Throwable — на практике Exception (или одного из его подклассов) или Error. Строка, переданная в конструктор, становится сообщением исключения, которое впоследствии можно получить с помощью getMessage().
Поскольку брошенное исключение раскручивает стек, любой код после throw в том же блоке никогда не выполнится:
throw new Exception("stop here");
echo "this line is unreachable"; // never executesПочему throw, а не возврат ошибки?
Возврат специального значения (например, false или -1) для сигнализации об ошибке заставляет каждого вызывающего помнить о необходимости его проверки, а значение легко потерять. throw делает сбой невозможным для игнорирования: исключение автоматически распространяется, пока что-то не обработает его, и несёт в себе сообщение, код, трассировку стека, а также файл и строку, где оно произошло.
Используйте throw для исключительных ситуаций, из которых текущая функция не может разумно восстановиться, — некорректные аргументы, неудачное подключение к базе данных, отсутствующий обязательный файл — и позвольте вышестоящему вызывающему коду решить, что делать.
Базовый пример
Здесь функция divide() генерирует исключение при попытке деления на ноль, а вызывающий код перехватывает его:
<?php
function divide(int $numerator, int $denominator): float
{
if ($denominator === 0) {
throw new InvalidArgumentException("Cannot divide by zero.");
}
return $numerator / $denominator;
}
try {
echo divide(10, 2), PHP_EOL; // 5
echo divide(10, 0), PHP_EOL; // throws before printing
} catch (InvalidArgumentException $e) {
echo "Caught: " . $e->getMessage() . PHP_EOL;
}Вывод:
5
Caught: Cannot divide by zero.Первый вызов завершается успешно и выводит 5. Второй вызов генерирует исключение, поэтому его echo никогда не выполняется, и управление сразу переходит к блоку catch.
Генерация пользовательского исключения
Расширение Exception позволяет дать каждому типу ошибки собственное имя, чтобы вызывающий код мог перехватывать именно те сбои, которые его интересуют, и игнорировать остальные:
<?php
class InsufficientFundsException extends Exception {}
function withdraw(float $balance, float $amount): float
{
if ($amount > $balance) {
throw new InsufficientFundsException(
"Cannot withdraw $amount; balance is only $balance."
);
}
return $balance - $amount;
}
try {
echo withdraw(100, 250), PHP_EOL;
} catch (InsufficientFundsException $e) {
echo "Declined: " . $e->getMessage() . PHP_EOL;
}Вывод:
Declined: Cannot withdraw 250; balance is only 100.Смотрите Пользовательские классы исключений для получения дополнительной информации о расширении Exception.
Сообщение, код и предыдущее исключение
Конструктор Exception принимает три аргумента — message, code и previous (предыдущее исключение). Третий аргумент позволяет обернуть низкоуровневую ошибку в более содержательную, не теряя исходную причину (это называется цепочкой исключений):
<?php
try {
try {
throw new RuntimeException("Disk read failed", 13);
} catch (RuntimeException $low) {
// Re-throw a higher-level exception, keeping the original as the cause.
throw new Exception("Could not load config", 0, $low);
}
} catch (Exception $e) {
echo $e->getMessage() . PHP_EOL; // Could not load config
echo "Caused by: " . $e->getPrevious()->getMessage() . PHP_EOL; // Disk read failed
echo "Original code: " . $e->getPrevious()->getCode() . PHP_EOL; // 13
}Вывод:
Could not load config
Caused by: Disk read failed
Original code: 13Повторная генерация исключения в блоке catch
Вам не обязательно полностью обрабатывать исключение там, где вы его перехватили. Блок catch может выполнить некоторые действия (записать в лог, добавить контекст) и затем снова вызвать throw, чтобы позволить внешнему обработчику завершить работу:
<?php
function loadUser(int $id): array
{
try {
throw new RuntimeException("Database is down");
} catch (RuntimeException $e) {
error_log("loadUser($id) failed: " . $e->getMessage());
throw $e; // pass it on
}
}
try {
loadUser(7);
} catch (RuntimeException $e) {
echo "Handled at top level: " . $e->getMessage() . PHP_EOL;
}Вывод:
Handled at top level: Database is downthrow как выражение (PHP 8+)
Начиная с PHP 8.0, throw является выражением, а не только оператором, поэтому его можно использовать там, где ожидается значение — например, с операторами ?: и ?? или в стрелочной функции:
<?php
function getConfig(array $config, string $key): string
{
// Throw inline when the key is missing.
return $config[$key] ?? throw new InvalidArgumentException("Missing key: $key");
}
echo getConfig(['env' => 'prod'], 'env'), PHP_EOL; // prod
try {
getConfig(['env' => 'prod'], 'region');
} catch (InvalidArgumentException $e) {
echo $e->getMessage() . PHP_EOL;
}Вывод:
prod
Missing key: regionРаспространённые ошибки
- Генерация без
try/catchгде-либо выше по стеку. Непойманное исключение становится фатальной ошибкой и останавливает скрипт. Всегда перехватывайте его где-нибудь или зарегистрируйте обработчикset_exception_handler(). - Генерация строки или массива.
throwтребует объект, реализующийThrowable;throw "oops";является синтаксической ошибкой. - Молчаливое поглощение исключений. Пустой блок
catchскрывает ошибки. Как минимум запишите сообщение в лог или перегенерируйте исключение. - Использование исключений для обычного управления потоком. Генерация при каждой ожидаемой ветке (например, «пользователь не найден» при обычном поиске) медленна и запутывает код — оставьте их для действительно исключительных случаев.
Итоги
throwгенерируетThrowableи немедленно раскручивает стек до ближайшего подходящегоcatch.- Предпочитайте его магическим возвращаемым значениям, чтобы сбои нельзя было молча проигнорировать.
- Создавайте подклассы
Exceptionдля именованных типов ошибок; передавайтеpreviousисключение для цепочки причин. - Перехватывайте, добавляйте контекст и снова вызывайте
throw, чтобы внешний обработчик принял решение. - В PHP 8+,
throwработает как выражение внутри??,?:и стрелочных функций.
Продолжайте с блоком try, блоком catch и finally, чтобы увидеть полный цикл обработки исключений.