Обработка ошибок в PHP: try/catch и не только — полное руководство

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

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

Начать курс

Обработка ошибок в PHP: try/catch и не только

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

1. Основы: конструкция try/catch

Самый распространённый способ обработки ошибок в современном PHP — это конструкция try/catch. Она позволяет «поймать» исключение (объект класса Throwable или его наследников) и выполнить альтернативный код вместо аварийного завершения скрипта.

Базовый синтаксис выглядит так:

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

В блоке try размещается потенциально опасный код. Если внутри него возникает исключение (через throw или из-за внутренней ошибки PHP), выполнение немедленно переходит в блок catch. Переменная $e содержит объект исключения, у которого можно вызвать методы:

  • getMessage() — текст ошибки;
  • getCode() — числовой код ошибки;
  • getFile() — файл, где произошла ошибка;
  • getLine() — строка, вызвавшая ошибку.

2. Блок finally: код, который выполняется всегда

Иногда требуется выполнить определённые действия независимо от того, возникла ошибка или нет — например, закрыть соединение с базой данных или освободить ресурсы. Для этого используется блок finally:

<?php
function processData($data) {
    try {
        // Опасная операция
        if (empty($data)) {
            throw new InvalidArgumentException('Данные не могут быть пустыми');
        }
        echo "Обработка данных...\n";
        return true;
    } catch (InvalidArgumentException $e) {
        echo "Ошибка: " . $e->getMessage() . "\n";
        return false;
    } finally {
        echo "Блок finally выполняется всегда.\n";
    }
}

processData([]); // Выведет: Ошибка: Данные не могут быть пустыми, затем блок finally
processData('test'); // Выведет: Обработка данных..., затем блок finally
?>

Блок finally выполняется даже если в try или catch есть оператор return. Это гарантирует очистку ресурсов.

3. Пользовательские исключения: расширяем класс Exception

В реальных проектах редко обходятся стандартным классом Exception. Гораздо удобнее создавать собственные классы исключений для разных типов ошибок. Это делает код более читаемым и упрощает отлов специфических ситуаций.

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

<?php
class ValidationException extends Exception {}
class DatabaseException extends Exception {}

function validateUser($age) {
    if ($age < 0 || $age > 150) {
        throw new ValidationException('Некорректный возраст');
    }
    return true;
}

function connectToDatabase($dsn) {
    if (empty($dsn)) {
        throw new DatabaseException('DSN не может быть пустым');
    }
    // Симуляция подключения...
}

try {
    validateUser(200);
} catch (ValidationException $e) {
    echo 'Ошибка валидации: ' . $e->getMessage();
}

try {
    connectToDatabase('');
} catch (DatabaseException $e) {
    echo 'Ошибка БД: ' . $e->getMessage();
}
?>

Теперь можно ловить разные исключения отдельно:

<?php
try {
    // Выполняем операции
    validateUser(200);
    connectToDatabase('mysql:host=localhost');
} catch (ValidationException $e) {
    // Логика для ошибок валидации
    echo "Ошибка ввода: " . $e->getMessage();
} catch (DatabaseException $e) {
    // Логика для ошибок БД
    echo "Проблема с БД: " . $e->getMessage();
} catch (Exception $e) {
    // Общий обработчик для всех остальных исключений
    echo "Неизвестная ошибка: " . $e->getMessage();
}
?>

Порядок блоков catch важен: сначала идут более специфичные исключения (наследники), затем — более общие (родители). Если поставить catch (Exception $e) первым, все остальные блоки никогда не сработают.

4. Глобальная обработка ошибок: set_error_handler и set_exception_handler

Не все ошибки в PHP являются исключениями. Старые функции (например, mysql_* или file_get_contents) могут генерировать предупреждения и уведомления, которые не ловятся через try/catch. Для их перехвата существует механизм пользовательских обработчиков.

4.1. set_error_handler — перехват «старых» ошибок

Эта функция позволяет задать собственный обработчик для ошибок уровня E_WARNING, E_NOTICE и других (кроме E_ERROR, E_PARSE).

<?php
function customErrorHandler($severity, $message, $file, $line) {
    // Превращаем предупреждение в исключение
    throw new ErrorException($message, 0, $severity, $file, $line);
}

// Устанавливаем обработчик
set_error_handler('customErrorHandler');

try {
    // Вызов несуществующей переменной вызовет E_NOTICE
    echo $undefinedVariable;
} catch (ErrorException $e) {
    echo 'Поймана ошибка: ' . $e->getMessage();
}

// Восстанавливаем стандартный обработчик (опционально)
restore_error_handler();
?>

Этот подход позволяет унифицировать обработку: все ошибки (и старые, и новые) превраща

Блоги

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