[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Работа с исключениями и trigger_error
Страницы: 1, 2, 3, 4
twin
Бытует мнение, мол trigger_error() - это отстой. А exception рулит. Но всегда ли так?

Давайте рассмотрим подробнее использование его внутри фреймворка.

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

Он не выходит из скрипта. И это плюс на самом деле, а не минус. Потому что можно спокойно вернуть из метода false, и строить логику на ней (буду показывать на функциях для экономии места:
function example($value)
{
if (!is_string($value)) {
trigger_error('Ожидается строка');
return false;
}

return trim();
}

$var = example(1);

if (false === $var) {
echo '<br>Реагируем';
}


В итоге мы получим переход в блок реагирования. Отметим первое отличие от исключений. Потом обобщу. Исключения передают управление в блок catch, который может находиться черти где далеко от вызова функции. Это похоже на GOTO, который всеми порицается. А оборачивать в блок каждый вызов - ни чем не отличается от if... else, плюс сводит на нет возможности иерархий. Так что тут они на равных.

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

set_error_handler('errorHandler');

function errorHandler($code, $message, $file, $line)
{
echo $message .' in '. $file .' on line '. $line;
}


Уже лучше, мы можем не только расскрасить и сделать дебаггер, но и выключить оповещение совсем. При этом ошибки с trigger_error автоматически логируются. А ексепшены нет. Вернее они тоже логируются, но только первый. Отметим второй плюс по сравнению с исключениями.

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

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

Вроде пока по очкам триггер впереди. За что же он предается гонениям? Первое. В нем нет стека. Ну и что, сейчас исправим:
Вуаля:
set_error_handler('errorHandler');

function errorHandler($code, $message, $file, $line)
{
echo $message .' in '. $file .' on line '. $line;
var_dump(debug_backtrace());
}


Но это слишком просто. Есть проблема посложнее - привычки многих работать с try... catсh

А им это все не интересно. Ok. Не вопрос. А кто нам запретил кинуть эксепшен в обработчике? Причем можно сделать это опционально. Нравятся эксепшены, включим. Нет - нафиг надо. Добавляем второй обработчик:
if (true) {
set_error_handler('errorException');
} else {
set_error_handler('errorHandler');
}


function errorException($code, $message, $file, $line)
{
throw new Exception($message, $code);
}


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

Ага! скажут противники. А почему в локации указывается не тот файл и не та линия? Пердиманокль.

Ну ничего. Это тоже вполне решаемо. Делаем наследника от Exception и передаем их в него:
class TriggerException extends Exception
{
public function __construct($message, $code, $file, $line)
{
$this->file = $file;
$this->line = $line;
parent::__construct($message, $code);
}
}


function errorException($code, $message, $file, $line)
{
throw new TriggerException($message, $code, $file, $line);
}


Всё, получили то, что хотелось. Локация на месте.

Вроде бы победа очевидна. Но не тут то было. Аппологеты вводят в бой тяжелую артилерию. А где тут иерархия исключений? А? Как разделить ексепшены по типам? Тупик.

Однако нет безвыходных положений. Даже если тебя съели, всегда есть два выхода. :)

А вот возьмем и промаркеруем ошбки. Вот так:
defined('ABC_INVALID_ARGUMENT_EX') or define('ABC_INVALID_ARGUMENT_EX', '[004]');

function example($value)
{
if (!is_string($value)) {
trigger_error(ABC_INVALID_ARGUMENT_EX .'Ожидается строка');
return false;
}

return trim($value);
}


Теперь ничего не стоит в обработчике спарсить маркер, и на его основе построить иерархию:
function errorException($code, $message, $file, $line)
{
$type = substr($message, 0, 5);

switch ($type) {

case ABC_INVALID_ARGUMENT_EX :
$exception = 'InvalidArgumentException';
break;
// Тут все осталные
default :
throw new Exception($message, $code);
}

throw new $exception($message, $code, $file, $line);
}


Еще правда нужна библиотека наследников spl, но их всего 13 штук, не так уж и много.

Вот допустим один из них:
<?php

namespace ABC\Abc\Core\Exception;

/**
* Класс InvalidArgumentException
* Адаптирует trigger_error к Exception
* для корректного выброса исключения
* NOTE: Requires PHP version 5.5 or later
*
@author phpforum.su
*
@copyright © 2015
*
@license http://www.wtfpl.net/
*/

class InvalidArgumentException extends \InvalidArgumentException
{
/**
* Конструктор
*
*/

public function __construct($message, $code, $file, $line)
{
$this->file = $file;
$this->line = $line;
parent::__construct($message, $code);
}
}


Всё. Теперь можно юзать нативные SPL исключения, а можно свою иерархию создать. При этом получая все плюсы trigger_error() А вот наоборот уже вряд ли получится.

В моем фреймворке это уже работает. Вот они, голубчики.


Ну а теперь резюме. Хотя и так все очевидно. Я просто объединил их плюсы, нивелировав минусы.

1. Автоматическое логирование
Excepion no
trigger_error yes

2. Логирование всех ошибок
Excepion no
trigger_error yes

3. Возможность отфильтровать Notice
Excepion no
trigger_error yes

4. Переключение режимов с одного на другой
Excepion no
trigger_error yes

5. Возможность работы с прямой логикой, без передачи управления
Excepion no
trigger_error yes

Всё остальное они умеют делать одинаково.

Так что внутри фреймворка исключения будем бросать в самых крайних случаях. Когда без них совсем никак. Будем пользоваться маркированными trigger_error().

На дессерт фото моего дебаггера, чтобы развеять сомнения. И стек на месте, и локации, и листинг, и все что хочешь. А можно выключить и включить ексепшены.

При этом сами ексепшены точно так же работают в штатном режиме и ловятся дебаггером.[b]

_____________
Если вам недостаточно собственных заблуждений, можно расширить их мнениями экспертов.

Нужно уважать мнение оппонета. Ведь заблуждаться - его святое право.

Настаивал, настаиваю и буду настаивать на своем. На кедровых орешках.

user posted image
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.