Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Исключения PHP: Погружаясь в классы

Исключения – один из немногих примеров удачного использования наследования. В этом уроке мы научимся создавать свои исключения и перехватывать их.

Обычно исключения используются так. Ближе к началу программы стоит конструкция try/catch, которая ловит исключения и показывает пользователю адекватное сообщение:

<?php

try {
  doSomethingDangerous();
} catch (\Exception $e) {
  echo 'Something happens';
}

Но как понять что случилось? Иногда это важно. Разные ошибки могут приводить к разному поведению программы. Кроме того, не все ошибки требуют обработки в текущем месте программы.

Разделять ошибки можно с помощью разных классов (или интерфейсов) наследующихся от класса Exception, который в свою очередь реализует интерфейс Throwable. По техническим причинам, реализовать этот интерфейс напрямую нельзя, поэтому остаётся только один путь – наследование.

<?php

namespace App;

class MyException extends \Exception {}

Причём в отличие от других примеров наследования, в исключениях редко нужно добавлять или изменять поведение. Основная цель использования наследования исключений – описание всех возможных типов ошибок.

Теперь посмотрим как этим можно воспользоваться:

<?php

try {
    // Какой-то код
} catch (MyException $e) { // Проверка instanceof
    // Делаем что-нибудь одно
} catch (\Exception $e) {
    // Делаем что-нибудь другое
}

Конструкция try/catch работает очень похоже на switch, но только для исключений. С её помощью можно описать обработку каждого типа исключений добавив новый блок catch. Во время срабатывания исключения, PHP проверяет каждый блок catch на instanceof, начиная с верхнего. Поэтому порядок блоков catch имеет важное значение.

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

interface Throwable
  |- Error implements Throwable
      |- ArithmeticError extends Error
          |- DivisionByZeroError extends ArithmeticError
      |- AssertionError extends Error
      |- ParseError extends Error
      |- TypeError extends Error
          |- ArgumentCountError extends TypeError
  |- Exception implements Throwable
      |- ClosedGeneratorException extends Exception
      |- DOMException extends Exception
      |- ErrorException extends Exception
      |- IntlException extends Exception
      |- LogicException extends Exception
          |- BadFunctionCallException extends LogicException
              |- BadMethodCallException extends BadFunctionCallException
          |- DomainException extends LogicException
          |- InvalidArgumentException extends LogicException
          |- LengthException extends LogicException
          |- OutOfRangeException extends LogicException
      |- PharException extends Exception
      |- ReflectionException extends Exception
      |- RuntimeException extends Exception
          |- OutOfBoundsException extends RuntimeException
          |- OverflowException extends RuntimeException
          |- PDOException extends RuntimeException
          |- RangeException extends RuntimeException
          |- UnderflowException extends RuntimeException
          |- UnexpectedValueException extends RuntimeException

Перехват любого базового исключения, автоматически влечёт за собой перехват всех наследников текущего класса. Например, если в блоке catch перехватывать RuntimeException, то этот блок поймает все ошибки, входящие в иерархию RuntimeException.

В PHP есть негласное правило о том, как работать с иерархиями исключений. Любая программа, должна определять своё собственное высокоуровневое исключение, которое наследуется от RuntimeException. Все остальные исключения библиотеки наследуются от него. Такой подход позволяет изолировать обработку ошибок конкретной библиотеки буквально одним блоком catch. Например, в HTTP-клиенте Guzzle, базовое исключение TransferException. Это значит, что среди всех ошибок мы можем перехватить ошибки этого клиента:

<?php

try {
    $client = new \GuzzleHttp\Client();
    $response = $client->get('https://ru.hexlet.io');
} catch (Guzzle\TransferException) {
    // Сюда попадут все ошибки библиотеки Guzzle
}

Блок finally

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

Это привело к тому, что саму конструкцию расширили, добавив в неё блок finally. Этот блок вызывается в самом конце и в любом случае:

<?php

try {
    // Какой-то код
} catch (MyException $e) {
    // Делаем что-нибудь одно
} finally {
    // Вызовется в любом случае
}

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 25 000 ₸ в месяц
Разработка веб-приложений на Laravel
10 месяцев
с нуля
Старт 21 ноября

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»