Почему вы должны использовать исключения SPL в PHP для лучшей обработки исключений — веб-разработчики и т. д.
Почему вы должны использовать исключения SPL в PHP для лучшей обработки исключений Содержание
- Что такое классы исключений SPL?
- Почему следует использовать исключения SPL?
- Как выбрать соответствующее исключение
- 13 исключений SPL — и когда их использовать!
- LogicException
- BadFunctionCallException
- BadMethodCallException
- DomainException
- InvalidArgumentException
- LengthException
- OutOfBoundsException
- RuntimeException
- OutOfRangeException
- OverflowException
- UnderflowException
- RangeException
- UnexpectedValueException
Since PHP 5.1.0, we have had access to the SPL Exceptions .
До того, как они появились, большинство людей просто использовали старый добрый throw new Exception(. ..)
или создали свой собственный класс Exception, который расширил базовый класс Exception.
Но теперь у нас есть возможность использовать (или расширять) определенные типы исключений.
И вы должны всегда использовать исключения SPL (или расширять их с помощью подкласса) и никогда больше не использовать старый добрый /Exception
!
Каковы классы исключений SPL?
Существует около дюжины классов исключений SPL, которые в конечном итоге расширяют базу 9.0046 \Класс исключения .
Сами по себе они ничего особенного не делают, но их можно использовать для создания отчетов и обработки определенных типов ошибок.
Они используются точно таким же образом — просто используйте и выдайте новое исключение OverflowException
.
Почему следует использовать исключения SPL?
Они предоставляют более подробные отчеты об ошибках. Плохая практика иметь сотни строк кода, выдающих только Exception
. В идеале у вас должны быть свои собственные исключения (которые должны расширять исключения SPL), но нецелесообразно делать это каждый раз, когда вы можете создать исключение.
13 исключений SPL охватывают широкий спектр распространенных ситуаций, с которыми вы столкнетесь, например недопустимые аргументы (InvalidArgumentException) или переполнения (OverflowException).
Как выбрать подходящее исключение
Иногда может быть действительно очевидно, какое исключение выбрасывать (или расширять с помощью подкласса), однако в некоторых случаях это неочевидно или когда оно может соответствовать двум или более классам исключений. .
Если неясно, какой из них выбрать, вы всегда можете выбрать родительский класс исключений. Есть два «основных» класса:
- LogicException , который включает следующие подклассы:
- BadFunctionCallException
- BadMethodCallException
- Исключение домена
- Инвалидаргументексцептион
- Длина исключения
- OutOfRangeException
- BadFunctionCallException
- RuntimeException , который включает следующие подклассы:
- OutOfBoundsException
- Исключение переполнения
- RangeException
- UnderflowException
- UnexpectedValueException
Таким образом, вы всегда можете просто выбрать соответствующее родительское исключение (но больше не выбирайте базовый класс \Exception!)
Большинство из них, как правило, представляют собой вариант RuntimeException.
13 исключений SPL —
и когда их использовать!LogicException
Это родительский класс примерно для половины исключений SPL (RuntimeException является родительским классом для остальных).
Довольно часто используется (хотя во многих случаях следует использовать один из его подклассов). Используйте его, когда есть ошибка в логике программы.
BadFunctionCallException
По моему опыту, это исключение редко используется. Вы должны выбросить его, если обратный вызов относится к неопределенной функции или если отсутствуют некоторые аргументы.
BadMethodCallException
Это подкласс BadFunctionCallException
Это похоже на BadFunctionCallException, но для методов. Его следует использовать, когда метод класса не существует или имеет неправильные параметры (т.е. отсутствующие параметры).
Вы часто обнаружите, что BadFunctionCallException вызывается в методе класса __call($method, $params)
, если self::$method() не существует.
DomainException
Это исключение следует использовать, если значение не соответствует определенной допустимой области данных.
Часто используется для «проверки работоспособности». Например, если вы пытаетесь имитировать бросок шестигранной кости, то, если значение > 6, вы можете сгенерировать исключение DomainException (поскольку значения всегда должны быть от 1 до 6). Он довольно часто используется в классах базы данных Laravel, если вы пытаетесь выполнить какое-либо действие, которое драйвер базы данных не поддерживает (например, выполнение doAdvisoryLock() вызовет исключение DomainException, если ваше подключение к базе данных SQLite, поскольку SQLite не поддержка рекомендательных замков
InvalidArgumentException
Это еще одно часто используемое исключение. Вы видите это, когда аргумент (например, метод или параметр функции) недействителен.
Например, если вы ожидаете, что $input
будет массивом, вы можете сгенерировать InvalidArgumentException if is_array($input) != true
.
LengthException
Это исключение предназначено для создания, если длина недействительна. Например, если длина строки слишком велика или слишком мала.
Например, если вы имеете дело со штрих-кодами ISBN-10 (длина которых всегда составляет 9 символов), если strlen($input) != 9
, вы должны создать исключение LengthException.
OutOfBoundsException
Исключение OutOfBoundsException должно быть сгенерировано, если значение не является допустимым ключом.
Обычно используется при попытке доступа к несуществующим элементам массива или при слишком большом смещении.
Используется только для значения ключа, не являющегося индексом (например, $array['некоторый-ключ-который-не-существует']
.)
Если вы имеете дело с целочисленным индексом, выходящим за пределы, вам следует использовать следующее исключение: OutOfRangeException.
RuntimeException
Это родительский класс примерно для половины исключений SPL, и его следует вызывать, если возникает ошибка, которую можно обнаружить только во время выполнения.
OutOfRangeException
Это похоже на OutOfBoundsException, но должно использоваться для недопустимых индексов массива (целых чисел).
OverflowException
Если у вас есть класс-контейнер, и вы пытаетесь добавить в него элемент, но он полон, вам следует создать исключение OverflowException. Их также можно использовать в ситуациях, когда у вас есть максимальное количество повторных попыток, и после $i > $maxRetries
было бы неплохо создать исключение OverflowException.
UnderflowException
Это противоположно OverflowException и используется при удалении элементов в пустом контейнере.
Если у вас есть класс-контейнер с нулевыми элементами в нем, и ваш код пытается удалить элемент, вы должны создать исключение UnderflowException.
RangeException
Используется, когда значение не находится в определенном диапазоне значений.
Похож на DomainException, но этот класс расширяет RuntimeException.
UnexpectedValueException
Вы должны сгенерировать исключение UnexpectedValueException, если значение не совпадает с набором значений.
Обычно это происходит, когда функция вызывает другую функцию и ожидает, что возвращаемое значение будет иметь определенный тип или значение, не включая арифметические ошибки или ошибки, связанные с буфером.
Дизайн исключений в вашей команде PHP упрощает отладку
Ух ты, правда — статья о дизайне исключений? Не судите пока, позвольте мне ввести вас в курс дела. Когда меня повысили до должности руководителя PHP, я получил новую точку зрения и новый набор обязанностей — анализ качества и широко понимаемые исследования. Я быстро заметил, что исключениям в PHP не уделяется должного внимания. Обычно это представляется как программная конструкция, которую вы должны использовать, и редко обсуждается дальше. Позор, потому что дизайн исключений — очень широкая тема.
Поэтому я хотел бы поделиться с вами своими открытиями — в этой статье будет рассказано, почему PHP-разработчики обычно игнорируют исключения, когда их использовать и как создавать их таким образом, чтобы они облегчили отладку.
Начало работы с дизайном исключений в PHPЗнакомьтесь, Рон — мой воображаемый друг. Он внештатный разработчик, который делает лендинги и портфолио. Ничего сложного. Кроме шуток, он поможет нам проиллюстрировать здесь некоторые проблемы.
Итак, Рон решил, что ему надоела бухгалтерия и взыскание долгов, поэтому он устроился на работу в ИТ-компанию, которая обслуживает различных клиентов. Он получил свою первую задачу: общую абстракцию файловой системы (он получил идентификатор, представляющий ресурс).
Он нетерпеливо подошел к своему столу и придумал это:
У нас 2019 год и PHP 7, поэтому в Code Review один из его коллег рекомендовал использовать типы и как-то сигнализировать о возникновении ошибки. Рон просмотрел и скорректировал свой код:
Потом он услышал, что это не будет работать, потому что ошибки PHP конвертируются в исключения в проекте, и он должен учитывать SPLFileInfo в решении. Ну, он вернулся к документации и создал это:
Здесь я не преувеличиваю. Вы, вероятно, видели этот тип кода на собственном опыте.
Смотрите также: Создание сайта с нуля 👇
- Генератор статических сайтов на JavaScript: создадим сайт с нуля!
Наш разработчик узнал от очень любезных коллег, что исключения — это хороший механизм, который позволяет вам объективно сигнализировать о проблемах в вашем коде. На второй рабочий день Рона попросили реализовать ипотечный калькулятор. Учась на своем новом опыте, он создал следующий код.
Вопрос сливается – нужно ли было здесь использовать исключение? Если предположить, что Рон ожидает, что объект $loadDeptorRequest должен содержать данные, позволяющие правильно рассчитать кредит, это будет совершенно нормально.
Однако давайте представим бизнес-кейс, когда вам нужно массово проверять профили и ускорять процесс. Вы предполагаете, что данные не обязательно должны быть полными, и когда это происходит, вы возвращаете информацию. Поскольку мы ожидаем, что набор данных будет неправильным, остается ли это исключение в силе?
По-моему, нет — ситуация уже не особенная. От него можно избавиться и выбрать что-то более точное, например, Result Object. Если вы не знаете, что такое Result Object, я представлю его в конце статьи.
Когда следует использовать исключения?Существует множество мнений, где именно следует использовать исключения. Исключая официальное определение, я выбрал две самые выдающиеся мысли в ИТ-сообществе:
- Исключения следует использовать в исключительной ситуации — везде, где вы отступаете от счастливого пути.
- Исключения служат в первую очередь для улучшения удобочитаемости, поэтому вы используете их таким образом, чтобы повысить удобочитаемость кода.
Лично я за второй вариант. Объектно-ориентированное программирование было создано для того, чтобы упростить управление сложным кодом, а для того, чтобы управлять им хорошо, он должен быть читабельным.
Однако это не откровение. Вот почему я говорю об искусстве дизайна исключений. Каждый понимает искусство по-своему, и то же самое, за исключениями. Вам нужно что-то решить и придерживаться этого до конца.
Имея это в виду, нам может быть трудно разработать исключение где-то в нашем коде, особенно когда мы начинаем осознавать структуру исключений. Итак, как подойти к этому? Где их разместить? Это довольно просто, но сложно.
Прежде чем принять решение, я всегда смотрю на эти два фактора: p вероятность возникновения проблемы и серьезность приложения или бизнеса.
Кажется очевидным и, возможно, вы уже это знаете, но иногда бывают ситуации, когда это не так просто.
Это довольно просто для вероятности, потому что правило тривиально — если что-то действительно вероятно или часто происходит, вам, вероятно, нужно что-то еще, кроме исключения (и под этим я не имею в виду сразу вернуться к примитивам) . Есть много других объективных способов справиться с проблемными ситуациями.
Например, хранилище объектов. Должен ли он генерировать исключение при неудачной выборке объекта или, может быть, просто вернуть нуль или нулевой объект? Ну, конечно, это зависит. Что, если это репозиторий для конкретного домена? Или просто обычное хранилище доктрин?
В таких ситуациях вы должны спросить себя, насколько важно в вашем бизнес-контексте относиться к этому как к чему-то исключительному?
Включение исключенийЕсли достаточно критично, то ответ прост — использовать исключение.
Вы уже знаете, что включение исключений зависит от бизнес-контекста и вероятности проблемы, как мы рассмотрели, но как создавать исключения, чтобы они были полезными и читабельными?
Думаю, все скажут, что в разработке исключений – нет ничего сложного. Что ж, давайте посмотрим, что задумал Рон…
Он использовал очень типичное исключение, которое я уже видел миллион раз. На первый взгляд вроде бы все хорошо, но давайте проанализируем, насколько это полезно.
- Мы действительно не знаем, почему наш документ не может быть подписан.
- Имя исключения очень общее и не очень помогает понять нашу проблему.
- Мы не получили ничего, что могло бы помочь нам диагностировать проблему.
Чтобы исправить это сейчас, нам нужно поработать над первым и вторым пунктом. Было бы неплохо узнать, в чем проблема. В нашем примере мы можем получить эту информацию, используя код состояния. Назовем исключение именем проблемы.
Лучше, не так ли? Теперь каждый, кто посмотрит на это, будет лучше понимать происхождение проблемы.
Далее нам нужно подумать о сотрудничестве с будущими сопровождающими. Есть ли лучшее место, где вы можете найти информацию о проблеме, чем фактический источник проблемы?
Поскольку мы выявляем и разрабатываем исключительные ситуации, велика вероятность того, что при написании кода у нас уже есть большая часть информации о возникающей проблеме.
Для HTTP-запросов объяснение проблемы чаще всего описывается в HTTP-ответе сервиса. Используйте это в своих интересах!
Кто-то может подумать, что исключения для всех возможных случаев ошибок — это преувеличение. Точно нет! Было бы довольно легко зафиксировать антипаттерн Object Explosion. Помните, что обработка ошибок — это искусство, и вам просто нужно принимать решения осознанно?
Вы должны спросить себя, сбалансированы ли ваши действия, а затем внести ценность в код. В конце концов, может исключения не будут лучшим решением, и стоит рассмотреть другие варианты?
При создании сообщений об исключениях рекомендуется проверять, не содержат ли ваши сообщения конфиденциальные данные. Если это так, вам следует подумать о дополнительном уровне очистки, который будет маскировать конфиденциальные данные в сообщениях.
Мы избавились от суффикса «Exception» в наших классах. Почему? Лично я считаю, что этот суффикс не нужен, когда у нас есть пространства имен. Кроме того, вы не можете получить ничего, кроме исключения в try-catch.
Как насчет имен исключений?Если у вас проблемы с именованием классов, а метод XYZ не кажется вам привлекательным, у меня есть для вас альтернатива — CPS(R): решение контекстной проблемы (причина). О, я думаю, у Рона появилось новое задание, и он использовал именно этот метод.
Контекст: Пополнить счет системы предоплаты
Проблема: Аккаунт пользователя неактивен
Решение: Передайте код причины
Давайте попробуем другой пример…
Контекст: Пользовательский запрос, определенный в файле конфигурации
Проблема: В строке найден нераспознанный токен
Решение: Передайте место, где начинается токен
В каких ситуациях мы хотим быть подробными? Когда мы отлаживаем код, конечно! Исключения являются частью этого процесса, поэтому нет ничего плохого в том, чтобы быть подробными в номенклатуре и структуре в содержании тела исключений. В случае нахождения ошибок в том, чтобы быть очевидным или выразительным, нет ничего плохого — скорее наоборот.
Каковы преимущества выразительных исключений?- Этот способ дополняет философию написания кода, чтобы он читался как проза. что вам не придется исследовать причину проблемы, но вы решите ее сразу без проблем
Мы (включая Рона!) узнали следующее:
- Исключения могут быть очень полезными.
- Существуют и другие механизмы, которые могут сигнализировать о потенциальной проблеме.
- Вставка исключения вместо другого механизма является дискреционным вопросом и зависит от контекста проекта.
- Звоните и создавайте исключения, чтобы наилучшим образом отразить характер проблемы.
Я на 100% уверен, что после этого практического опыта Рон станет опытным разработчиком исключений PHP и передаст свои новые знания новым Ронам в софтверной компании.