В PHP5 следует ли использовать исключения или trigger_error / set_error_handler?

Каковы плюсы и минусы того или иного способа. Есть ли один правильный путь (тм)?

Это НЕ основано на мнении ... WTH

Yousha Aleayoub 17.08.2017 14:05
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
32
1
10 922
9

Ответы 9

Это зависит от ситуации. Я обычно использую исключения, когда пишу внутреннюю бизнес-логику / приложение, а также trigger_error для валидатора и тому подобного.

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

Плюсы использования trigger_error для Validator и подобных вещей, скажем,

try {
    $user->login();
}  catch (AuthenticationFailureException $e) {
    set_error_handler("my_login_form_handler");
    trigger_error("User could not be logged in. Please check username and password and try again!");
} catch (PersistenceException $pe) { // database unavailable
    set_error_handler("my_login_form_handler"); 
    trigger_error("Internal system error. Please contact the administrator.");
}

где my_login_form_handler делает строку более красивой и помещает элемент в видимую область над формой входа в систему.

Мне нравится идея использования исключений, но у меня часто задействованы сторонние библиотеки, а затем, если они не используют исключения, вы получаете 3-4 разных подхода к проблеме! Zend использует исключения. CakePHP использует специальный обработчик ошибок, а большинство библиотек PEAR используют объект PEAR :: Error.

В этом отношении БЫЛ один верный путь. Маршрут настраиваемых обработчиков ошибок, вероятно, является наиболее гибким в этой ситуации. Однако исключения - отличная идея, если вы либо используете только свой собственный код, либо используете библиотеки, которые их используют.

К сожалению, в мире PHP мы все еще страдаем от отказа умереть от PHP4, поэтому такие вещи, как исключения, хотя они могут представлять лучшую практику, были невероятно медленными, пока все еще пишут вещи, чтобы иметь возможность работать в обоих 4 и 5. Надеюсь, этот фиаско сейчас заканчивается, хотя к тому времени у нас будет напряженность между 6 и 5 вместо этого ...

/ я держу голову руками ...

Идея исключения изящна и делает процесс обработки ошибок таким плавным. но это применимо только тогда, когда у вас есть соответствующие классы исключений, а в командной разработке есть еще одна важная вещь - «стандартные» исключения. поэтому, если вы планируете использовать исключения, вам лучше сначала стандартизировать типы исключений, или лучший выбор - использовать исключения из какой-нибудь популярной платформы. еще одна вещь, которая применима к PHP (где вы можете написать ориентир объекта кода в сочетании со структурным кодом), - это то, что вы пишете все свое приложение с использованием классов. Если вы пишете объектно-ориентированный код, тогда исключения, безусловно, лучше. в конце концов, я думаю, что ваш процесс обработки ошибок будет намного более плавным, за исключением trigger_error и прочего.

Очевидно, что «Единого правильного пути» не существует, но на этот счет существует множество мнений. ;)

Лично я использую trigger_error для вещей, которые исключения не могут сделать, а именно для уведомлений и предупреждений (то есть вещей, которые вы хотите регистрировать, но не останавливать поток приложения так же, как это делают ошибки / исключения (даже если вы их поймаете на каком-то уровне) )).

Я также в основном использую исключения для условий, которые считаются невосстановимыми (для вызывающего метода, в котором возникает исключение), то есть серьезных ошибок. Я не использую исключения в качестве альтернативы возврату значения с тем же значением, если это возможно несложным способом. Например, если я создаю метод поиска, я обычно возвращаю нулевое значение, если он не нашел то, что искал, вместо того, чтобы выбросить EntityNotFoundException (или эквивалент).

Итак, мое практическое правило таково:

  • Пока не найти что-то является разумным результатом, мне гораздо проще вернуть и проверить нулевые значения (или какое-либо другое значение по умолчанию), чем обрабатывать это с помощью предложения try-catch.
  • Если, с другой стороны, не обнаружение этого является серьезной ошибкой, которая выходит за пределы области действия вызывающей стороны, от которой требуется восстановление, я все равно генерирую исключение.

Причина генерации исключений в последнем случае (в отличие от запуска ошибок) заключается в том, что исключения гораздо более выразительны, учитывая, что вы используете правильно названные подклассы Exception. Я считаю, что использование исключений стандартной библиотеки PHP является хорошей отправной точкой при принятии решения, какие исключения использовать: http://www.php.net/~helly/php/ext/spl/classException.html

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

Исключения - это современный и надежный способ сообщения об ошибке / исключительной ситуации. Используй их :)

Вы должны использовать исключения в «Исключительных обстоятельствах», то есть, когда вы вызываете метод doFoo (), вы должны ожидать, что он будет работать, если по какой-то причине doFoo не может выполнить свою работу, тогда он должен вызвать исключение.

Во многих старых кодах PHP будет использоваться подход, возвращающий false или null при сбое, но это затрудняет отладку, а исключения значительно упрощают эту отладку.

Например, скажем, у вас есть метод getDogFood (), который возвращает массив объектов DogFood, если вы вызвали этот метод и он возвращает null, когда что-то пойдет не так, как ваш вызывающий код сможет определить, был ли возвращен null из-за ошибки или корма для собак просто нет?

Что касается работы с унаследованными библиотеками кода, которые используют встроенное ведение журнала ошибок php, вы можете переопределить регистрацию ошибок с помощью функции set_error_handler (), которую вы могли бы использовать для повторного генерирования общего исключения.

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

Это старый вопрос, но чтобы указать, что собачьей еды нет, я бы вернул пустой массив вместо null.

Kwebble 22.05.2015 23:57

Если вы хотите использовать исключения вместо ошибок для всего приложения, вы можете сделать это с помощью ErrorException и настраиваемого обработчика ошибок (пример обработчика ошибок см. На странице ErrorException). Единственным недостатком этого метода является то, что нефатальные ошибки по-прежнему будут вызывать исключения, которые всегда являются фатальными, если не будут обнаружены. По сути, даже E_NOTICE остановит все ваше приложение, если ваши настройки Отчет об ошибках не подавляют их.

На мой взгляд, использование ErrorException дает несколько преимуществ:

  1. Пользовательский обработчик исключений позволит вам отображать хорошие сообщения даже об ошибках, используя set_exception_handler.
  2. Это никоим образом не нарушает существующий код ... trigger_error и другие функции обработки ошибок по-прежнему будут работать нормально.
  3. Это действительно затрудняет игнорирование глупых ошибок кодирования, которые запускают E_NOTICE и E_WARNING.
  4. Вы можете использовать try / catch для обертывания кода, который может генерировать ошибку PHP (а не только исключения), что является хорошим способом избежать использования хака подавления ошибок @:

    try {
        $foo = $_GET['foo'];
    } catch (ErrorException $e) {
        $foo = NULL;
    }
    
  5. Вы можете заключить весь сценарий в один блок try / catch, если хотите отображать дружественное сообщение для ваших пользователей при возникновении любой неперехваченной ошибки. (Делайте это осторожно, потому что регистрируются только неперехваченные ошибки и исключения.)

re: 5, вы можете вернуть false из обработчика ошибок, чтобы он запускал обработчик по умолчанию. Это все равно будет записывать ошибку, не так ли? (Полагаю, вы говорите о журналах ошибок apache?) В противном случае .. вы всегда можете записать ошибку самостоятельно в обработчике.

mpen 01.03.2012 20:27

На мой взгляд, №3 действительно важен. Уведомления почти более информативны, чем более серьезные предупреждения или ошибки, поскольку они могут указывать на неправильную практику (более инвазивный, чем ранее закрытый ресурс), и их повышение гарантирует, что они будут исправлены соответствующим образом.

Dan Lugg 18.07.2012 18:42

Ваш код в №4 не работает. Я по-прежнему получаю ошибку PHP («Неопределенный индекс: foo»). Похоже, это одна из многих хитростей в PHP.

fool4jesus 18.01.2015 00:16

Использование исключений - не лучшая идея в эпоху интеграции сторонних приложений..

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

ДАЖЕ если у вас есть средства в вашем приложении, чтобы исправить ошибку той библиотеки, которую вы используете. ....

В качестве примера можно привести стороннюю библиотеку входа в социальную сеть, которая выдает исключение, потому что поставщик входа в социальную сеть вернул ошибку и без надобности убивает все ваше приложение - кстати, hybridauth. Итак, у вас есть целое приложение, и там у вас есть библиотека, обеспечивающая дополнительные функции для вас - в данном случае, вход в социальную сеть - и даже если у вас есть много резервных материалов на случай, если провайдер не аутентифицируется (ваш собственный система входа в систему, а также около 20 других провайдеров входа в социальные сети), ваше ВСЕ приложение остановится. И вам придется изменить стороннюю библиотеку, чтобы обойти эти проблемы, и смысл использования сторонней библиотеки для ускорения разработки будет потерян.

Это серьезный недостаток дизайна с точки зрения философии обработки ошибок в PHP. Посмотрим правде в глаза - на другом конце большинства приложений, разрабатываемых сегодня, есть пользователь. Будь то пользователь интрасети, будь то пользователь в Интернете, будь то системный администратор, это не имеет значения - обычно есть пользователь.

И если приложение умирает на вашем лице, и в этот момент вы ничего не можете сделать, кроме как вернуться на предыдущую страницу и сделать снимок в темноте относительно того, что вы пытаетесь сделать, как пользователь, - это плохо, плохая практика со стороны разработчиков. Не говоря уже о внутренней ошибке, о которой должны знать только разработчики по многим причинам (от удобства использования до безопасности), которую бросают на лицо пользователю.

В результате мне придется просто отпустить конкретную стороннюю библиотеку (в данном случае hybridauth) и не использовать ее в моем приложении исключительно по этой причине. Несмотря на то, что hybridauth - очень хорошая библиотека, и очевидно, что на нее было потрачено много хороших усилий с множеством возможностей.

Поэтому вам следует воздерживаться от использования исключений в своем коде. ДАЖЕ если код, который вы делаете прямо сейчас, является кодом верхнего уровня, который будет запускать ваше приложение, а не библиотекой, возможно, вы захотите включить весь или часть своего кода в другие проекты или вам придется интегрировать части или целиком с другим вашим кодом или кодом третьей стороны. И если вы использовали исключения, вы столкнетесь с той же ситуацией - целые приложения / интеграции умирают прямо у вас на глазах, даже если у вас есть надлежащие средства для решения любой проблемы, которую предоставляет фрагмент кода.

Поправьте меня, если я вас неправильно понял, но я думаю, что вы все путаете. Если вы используете сторонние плагины, вы, вероятно, захотите, чтобы они генерировали исключения, а не выдавали ошибки. Исключения всегда можно отловить с помощью команды try / catch. Единственный способ отловить ошибки - это преобразовать их в ErrorExceptions с помощью error_handler. А некоторые ошибки, такие как E_ERROR, вообще невозможно отловить. Более того, исключения могут быть расширены или присвоены коды, что дает вам высокую степень контроля над тем, что происходит. Кроме того, вы можете использовать set_exception_handler для отображения какого-либо сообщения в пользовательском пространстве.

Tivie 01.12.2012 02:04

вступление

По моему личному опыту, как правило, я предпочитаю использовать исключения в моем коде вместо trigger_error. Это главным образом потому, что использование исключений более гибкое, чем запуск ошибок. И, ИМХО, это тоже выгодно не только мне как стороннему разработчику.

  1. Я могу расширить класс Exception (или использовать коды исключений), чтобы явно различать состояния моей библиотеки. Это помогает мне и сторонним разработчикам обрабатывать и отлаживать код. Это также показывает, где и почему он может выйти из строя. без необходимости просмотра исходного кода.
  2. Я могу эффективно остановить выполнение моей библиотеки, не останавливая выполнение скрипта.
  3. Сторонний разработчик может связать мои исключения (в PHP> 5.3. *). Очень полезно для отладки и может быть полезно при обработке ситуаций, когда моя библиотека может выйти из строя по разным причинам.

И я могу делать все это, не навязывая ему, как он должен обрабатывать отказы моей библиотеки. (например, создание сложных функций обработки ошибок). Он может использовать блок try catch или просто использовать общий обработчик исключений.

Примечание:

Некоторые из этих пунктов, по сути, также действительны для trigger_error, но их немного сложнее реализовать. Блоки try catch действительно просты в использовании и очень удобны для кода.


Пример

Думаю, этот пример может проиллюстрировать мою точку зрения:

class HTMLParser {
    protected $doc;
    protected $source = null;
    public $parsedHtml;
    protected $parseErrors = array();
    public function __construct($doc) {
        if (!$doc instanceof DOMDocument) {
            // My Object is unusable without a valid DOMDOcument object
            // so I throw a CriticalException
            throw new CriticalException("Could not create Object Foo. You must pass a valid DOMDOcument object as parameter in the constructor");
        }
        $this->doc = $doc;
    }

    public function setSource($source) {
        if (!is_string($source)) {
            // I expect $source to be a string but was passed something else so I throw an exception
            throw new InvalidArgumentException("I expected a string but got " . gettype($source) . " instead");
        }
        $this->source = trim($source);
        return $this;
    }

    public function parse() {
        if (is_null($this->source) || $this->source == '') {
            throw new EmptyStringException("Source is empty");
        }
        libxml_use_internal_errors(true);
        $this->doc->loadHTML($this->source);
        $this->parsedHtml = $this->doc->saveHTML();
        $errors = libxml_get_errors();
        if (count($errors) > 0) {
            $this->parseErrors = $errors;
            throw new HtmlParsingException($errors[0]->message,$errors[0]->code,null,
                $errors[0]->level,$errors[0]->column,$errors[0]->file,$errors[0]->line);
        }
        return $this;
    }

    public function getParseErrors() {
        return $this->parseErrors;
    }

    public function getDOMObj() {
        return clone $this->doc;
    }
}

Объяснение

В конструктор я бросаю CriticalException, если переданный параметр не относится к типу DOMDocument, потому что без него моя библиотека вообще не будет работать.

(Примечание: я мог бы просто написать __construct(DOMDocument $doc), но это всего лишь пример).

В методе setsource() я бросаю InvalidArgumentException, если переданный параметр не является строкой. Я предпочитаю останавливать выполнение библиотеки здесь, потому что исходное свойство является важным свойством моего класса, а недопустимое значение приведет к распространению ошибки по всей моей библиотеке.

Метод parse() обычно является последним вызываемым методом в цикле. Несмотря на то, что я выдаю XmlParsingException, если libXML находит искаженный документ, сначала выполняется синтаксический анализ, и результаты можно использовать (в определенной степени).


Работа с библиотекой примеров

Вот пример того, как обращаться с этой созданной библиотекой:

$source = file_get_contents('http://www.somehost.com/some_page.html');
try {
    $parser = new HTMLParser(new DOMDocument());
    $parser->setSource($source)
           ->parse();
} catch (CriticalException $e) {
    // Library failed miserably, no recover is possible for it.
    // In this case, it's prorably my fault because I didn't pass
    // a DOMDocument object.
    print 'Sorry. I made a mistake. Please send me feedback!';
} catch (InvalidArgumentException $e) {
    // the source passed is not a string, again probably my fault.
    // But I have a working parser object. 
    // Maybe I can try again by typecasting the argument to string
    var_dump($parser);
} catch (EmptyStringException $e) {
    // The source string was empty. Maybe there was an error
    // retrieving the HTML? Maybe the remote server is down?
    // Maybe the website does not exist anymore? In this case,
    // it isn't my fault it failed. Maybe I can use a cached
    // version?
    var_dump($parser);
} catch (HtmlParsingException $e) {
    // The html suplied is malformed. I got it from the interwebs
    // so it's not my fault. I can use $e or getParseErrors() 
    // to see if the html (and DOM Object) is usable
    // I also have a full functioning HTMLParser Object and can
    // retrieve a "loaded" functioning DOMDocument Object
    var_dump($parser->getParseErrors());
    var_dump($parser->getDOMObj());
}
$var = 'this will print wether an exception was previously thrown or not';
print $var;

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

В качестве побочного примечания, использование исключений не означает, что выполнение ПРОГРАММЫ будет остановлено, это просто означает, что код, зависящий от моего объекта, будет пропущен. Мне или стороннему разработчику решать, что делать с ним, как ему заблагорассудится.

Другие вопросы по теме