W3docs

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 down

throw как выражение (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, чтобы увидеть полный цикл обработки исключений.

Практика

Практика
Что делает оператор 'throw' в PHP?
Что делает оператор 'throw' в PHP?
Was this page helpful?