Обработка ошибок в PHP: try/catch и продвинутые техники

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему обработка ошибок — основа надежного PHP-кода

Каждый разработчик PHP сталкивается с ошибками. Будь то неверный ввод пользователя, недоступная база данных или неожиданный сбой API — без грамотной обработки ошибок приложение превращается в хрупкую конструкцию, которая может "упасть" в любой момент. Обработка ошибок в PHP — это не просто способ избежать белого экрана смерти (WSOD), а фундаментальный принцип создания стабильных, безопасных и поддерживаемых веб-приложений.

В этой статье мы разберем все аспекты обработки ошибок в PHP: от базового механизма try/catch до создания пользовательских исключений и продвинутых стратегий логирования. Вы узнаете, как превратить хаос ошибок в управляемый и предсказуемый процесс.

Основы: try, catch, finally и throw

Блок try/catch — первый рубеж обороны

Механизм исключений в PHP основан на конструкции try/catch. Код, который может вызвать ошибку, помещается в блок try. Если в нем возникает исключение, выполнение немедленно переходит в блок catch, где ошибка обрабатывается.

<?php
try {
    $file = fopen('important_data.txt', 'r');
    if (!$file) {
        throw new Exception('Не удалось открыть файл');
    }
    // Работа с файлом
    fclose($file);
} catch (Exception $e) {
    echo 'Ошибка: ' . $e->getMessage();
}
?>

Важно понимать: не все ошибки в PHP являются исключениями. Стандартные предупреждения (warning) и уведомления (notice) не генерируют исключения автоматически. Для их перехвата требуется специальная настройка (см. раздел "Продвинутые техники").

Блок finally — гарантированное выполнение

Блок finally выполняется всегда, независимо от того, было выброшено исключение или нет. Это идеальное место для освобождения ресурсов: закрытия файлов, соединений с БД или освобождения памяти.

<?php
function processData($data) {
    $connection = null;
    try {
        $connection = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
        // Выполнение запросов
        return $connection->query($data);
    } catch (PDOException $e) {
        // Логируем ошибку
        error_log('DB Error: ' . $e->getMessage());
        return false;
    } finally {
        // Закрываем соединение в любом случае
        if ($connection) {
            $connection = null;
        }
    }
}
?>

Оператор throw — создаем собственные исключения

С помощью throw вы можете генерировать исключения в любой точке кода. Это позволяет создавать понятные и предсказуемые сценарии ошибок.

<?php
function validateAge($age) {
    if (!is_numeric($age)) {
        throw new InvalidArgumentException('Возраст должен быть числом');
    }
    if ($age < 0 || $age > 150) {
        throw new OutOfRangeException('Возраст вне допустимого диапазона');
    }
    return true;
}

try {
    validateAge('abc');
} catch (InvalidArgumentException $e) {
    echo 'Ошибка валидации: ' . $e->getMessage();
} catch (OutOfRangeException $e) {
    echo 'Неверный диапазон: ' . $e->getMessage();
}
?>

Пользовательские классы исключений

Стандартные классы исключений (Exception, RuntimeException, InvalidArgumentException) покрывают многие сценарии, но для сложных проектов лучше создавать собственные иерархии исключений. Это улучшает читаемость кода и упрощает отладку.

Создание собственного класса исключения

<?php
class DatabaseException extends Exception {
    private $queryString;
    
    public function __construct($message, $query = '', $code = 0, Throwable $previous = null) {
        $this->queryString = $query;
        parent::__construct($message, $code, $previous);
    }
    
    public function getQueryString() {
        return $this->queryString;
    }
}

// Использование
try {
    $query = "SELECT * FROM users WHERE id = 'abc'";
    throw new DatabaseException('Ошибка синтаксиса SQL', $query);
} catch (DatabaseException $e) {
    echo 'Ошибка БД: ' . $e->getMessage();
    echo 'Запрос: ' . $e->getQueryString();
}
?>

Иерархия исключений для крупных проектов

Рекомендуется создавать базовый класс исключений для вашего приложения и наследовать от него более конкретные типы:

  • AppException — базовое исключение приложения
  • ValidationException — ошибки валидации данных
  • NotFoundException — ресурс не найден (404)
  • PermissionException — недостаточно прав
  • ExternalServiceException — ошибки внешних API

Такой подход позволяет в catch-блоках ловить исключения на нужном уровне абстракции и обрабатывать их соответствующим образом.

Продвинутые техники обработки ошибок

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

Для перехвата необработанных исключений используйте set_exception_handler(). Это ваш последний рубеж обороны перед тем, как ошибка попадет к пользователю.

<?php
set_exception_handler(function (Throwable $e) {
    // Логируем в файл
    error_log('Необработанное исключение: ' . $e->getMessage());
    
    // Отправляем уведомление разработчикам (email, Telegram, Slack)
    // notifyDevelopers($e);
    
    // Показываем пользователю дружественное сообщение
    if (getenv('APP_ENV') === 'production') {
        echo 'Произошла внутренняя ошибка. Мы уже работаем над её устранением.';
    } else {
        echo 'Ошибка: ' . $e->getMessage();
    }
    
    http_response_code(500);
});

// Теперь любое необработанное исключение попадет в этот обработчик
throw new RuntimeException('Тестовая ошибка');
?>

Преобразование стандартных ошибок в исключения

Чтобы ловить warning и notice через try/catch, используйте set_error_handler():

<?                

Блоги

Рекомендаций по книгам