Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

По моему мнению, существует несколько преимуществ использования ErrorException:

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

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

Ответ 2

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

Многие старые php-коды применяют подход к возврату false или null при возникновении сбоя, но это усложняет процесс отладки, исключения делают эту отладку намного проще.

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

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

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

Ответ 3

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

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

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

/me держит голову в руках...

Ответ 4

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

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

Про использование 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 претит строку и помещает элемент в видимую область над формой входа.

Ответ 5

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

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

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

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

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

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

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

Ответ 6

Введение

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

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

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

Примечание:

Некоторые из этих пунктов, в сущности, также действительны для trigger_error, которые немного сложнее реализовать. Попробуйте блокировки 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, если переданный параметр является чем-то другим, кроме строки. Я предпочитаю останавливать выполнение библиотеки здесь, потому что свойство source является существенным свойством моего класса, а недопустимое значение будет распространять ошибку в моей библиотеке.

Метод 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 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 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;

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

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

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

EVEN, если у вас есть средства в приложении, чтобы восполнить ошибку в этой библиотеке, которую вы используете....

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

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

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

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

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