Pull to refresh

Comments 57

Посмотрите в эту сторону: ru.php.net/error_get_last

$haltCodes = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, 4096);

$error = error_get_last();
if ($error && in_array($error['type'], $haltCodes)) {
	self::handleError($error['type'], $error['message'], $error['file'], $error['line']);
}


Кусок из моего «супервизора» (который у меня называется по-простому Handler'ом (^_^) ).
Да, к чему это — чтобы в конце выполнения скрипта избавиться от обязательном уведомлении об успешном завершении работы.
С добрым утром после вчера. Понял, сейчас чутка подправим идею. Я почему это написал — потому что у меня в проекте как было заложено интерфейсами, так и сделал. Идея верна, сейчас примерчик переделаю.
P.S. Кусок выдран как раз из функции, которая регистрируется как shutdown_function.
В указанном обсуждении qa упоимнал про это habrahabr.ru/qa/14388/#answer_60290
register_shutdown_function(function () {
   $error = error_get_last();
   if ($error && ($error['type'] == E_ERROR || $error['type'] == E_PARSE || $error['type'] == E_COMPILE_ERROR)) {
       if (strpos($error['message'], 'Allowed memory size') === 0) { // если кончилась память
           ini_set('memory_limit', (intval(ini_get('memory_limit'))+64)."M"); // выделяем немножко, что бы доработать корректно
           Log::error("PHP Fatal: not enough memory in ".$error['file'].":".$error['line']);
	} else {
           Log::error("PHP Fatal: ".$error['message']." in ".$error['file'].":".$error['line']);
        }
        // ... завершаемся корректно ....
    }
});
Ловит ошибки парсинга, отсутствия памяти, вызовов не существующих методов. Стоит упомянуть что после наступления register_shutdown_function следующее падение вы уже не сможете поймать, сколько бы не устанавливали register_shutdown_function. Таковы специфика этого коллбека.
Стоит упомянуть что после наступления register_shutdown_function следующее падение вы уже не сможете поймать, сколько бы не устанавливали register_shutdown_function
Пичалька… Мне этого очень не хватает, чтобы сделать «неубиваемую» систему…
А каким образом ей быть неубиваемой? Если это ошибка парсинга (точку с запятой забыли) в include-файле, то строку/файл можно просто проигнорировать (но это уже вносит нестабильность в систему — например, если ошибка в строке инициализации ядра приложения, то приложение работать в любом случае не будет), а если, например, память закончилась? Подобное «неудобство» ой как здорово спасает от попыток выстрелить себе в ногу, ИМХО.
Архитектура моей системы позволяет мне в случае фатальной ошибки «перезапустить» генерацию странички, исключая проблемный модуль. А само ядро системы не может привести к Fatal error. Оно умеет лишь запускать необходимые модули, ломаться там нечему. Естественно заново весь код отрабатывать не будет. Будут выполняться только те модули, что еще не запускались.
Спасибо. Я уже года 3 пытаюсь сделать что-то вроде cms/cmf используя эту идею. Уже не помню сколько раз оно переписывалось))) Сейчас уже близок к завершению. Пишу «стандартные» модули и тестирую всю систему на своих проектиках. Как только будет готов необходимый минимум модулей, выложу всё на githab. Кто бы с документацией еще помог… У меня почему-то с этим туго
Сломаться может что угодно и по разным причинам (та же память, выделенная под скрипт, кончилась, или случайно удалили запятую из кода ядра и забыли про это), но суть не в этом. В таком виде действительно отмена действия фатальных ошибок была бы кстати (не глобально, но в некоторых конкретных случаях). Можно идти на bugs.php.net и делать фичреквест (хотя кажется мне, что такие уже были) (^_^)
согласен, сейчас делаю патч к PHP где все фатальные ошибки будут просто бросать исключения. Посмотрим что из этого выйдет
Немного перефразирую и дополню — если возникла какая-то ошибка, повлекшая за собой shutdown (остановку выполнения скрипта), то, во-первых, она не будет передана error_handler'у (который устанавливается с помощью set_error_handler), а во-вторых, отменить этот шатдаун невозможно — можно только выполнить «напоследок» какую-нибудь функцию, которая проверит, был ли шатдаун естественным (скрипт кончился, или же были вызваны exit/die), или возник в результате фатальной ошибки, и соответствующим образом отреагировать. Таким образом, как уже сказал автор предыдущего комментария, сделать фатальные ошибки нефатальными нельзя.
А что если в shutdown функцию завернуть выполнение всего приложения ещё раз! sic идея пришла в голову только что)
у меня примерно так и происходит:) Но в таком случае при следующем fatal error мы ничего сделать не сможем
И оно функционирует точно так же, как и в обычном потоке (без сбоев и неадекватных реакций)? Просто мне казалось, что в shutdown уже какие-то компоненты PHP деинициализированы.
>>И оно функционирует точно так же, как и в обычном потоке (без сбоев и неадекватных реакций)?
Абсолютно точно также:)
>Просто мне казалось, что в shutdown уже какие-то компоненты PHP деинициализированы.

До 5.0 интерпретатор переходил в «нестабильный» режим и там не всё так было хорошо. Однако сейчас всё работает в штатном режиме.

> PHP деинициализированы
если shutdown вызван падением от отсутствия памяти, но то значение, которое пыталось выделить память существовать не будет. Это единственная разница от обычного кода сейчас
Я просто добавлю в топик error_get_last(); его объяснение и в код основного метода переделаю.
У меня есть мнение, что там в документации+переводе ошибка. Система работает не по схеме стек, а по схеме очередь. Об этом я уведомил переводчика, а тот, наверное, редакторов документации.


Плохое мнение. Вот пример из комментарий из мануала.
Именно. Там действительно стек. «Переводчики» скоро уведомят об этом автора ;)

wartur, кстати, для исправления ошибок документации можно использовать ссылку у каждого раздела документации «report a bug». Там вам быстрее ответят, чем на Хабре. По переводу конечно можно использовать и багрепорты и нашу рассылку.
Да я уже поговорил, действительно это так. Сейчас исправлю что бы не вводить в заблуждение.
$this->$mWorkSuccess
Тут ошибка, очевидно. Лишний доллар.
Ничего подобного. Доллары лишними не бывают

(тем более в PHP):
php > class Foo { public $bar = 'bar'; }
php > $foo = 'Foo';
php > $inst = new $foo();
php > $bar = 'bar';
php > echo $inst->$bar;
bar
Тут — ошибка. Это всё, что я хотел сказать.
Хотя в коде этого поста, да — ошибка (код я не читал, — идея итак понятна).

К стати, изначально я хотел вот такой пример привести:
php > $inststr = 'inst';
php > echo $$inststr->$bar;
bar
согласен, я же сказал, что код даже не запускал, сейчас исправлю.
Есть куда свежее статья, так же от DKlab
«Debug_ErrorHook: перехват ошибок PHP (даже фатальных) и их обработка — например, отправка по E-mail»
dklab.ru/lib/Debug_ErrorHook/
set_error_handler(array(&$this, 'ErrorCatcher'));

Объекты и так передаются по ссылке. Зачем здесь амперсанд?
А что уже вышел ПХП5? )
Опишу ещё один тонкий момент, на который сам когда-то натолкнулся. Это поведение описано в документации к set_error_handler, но ещё раз разложу по полочкам.

Допустим, мы зарегистрировали функцию, которая должна обрабатывать все ошибки и складывать их в лог set_error_handler('myHandler', E_ALL | E_STRICT);

А где-то в рабочем коде мы используем символ подавления ошибки @ (знаю, что плохой стиль, но всё же), например: $id = (int)@$_GET['id']

В этом примере, если индекс id отсутсвует в массиве $_GET сообщение уровня Notice не выведется на экран, однако попадёт в myHandler и в наши логи!

Если вы фанат @, готовьтесь получить в логах кучу сообщений об ошибках разного уровня, которые вы так старательно подавляете при выводе на экран.
Я фанат. И постарался обойти этот момент в реализации комментом выше. Прокомментируете?
Я тоже считаю подавление неверным.
1) не советуют с точки зрения производительности — была статья по этому повод
2) на настроенном рабочем сервере ошибки должны быть отключены
3) на локальном сервере ошибки все ошибки должны быть включены и исправлены
Исключение — своя комплексная система обработки ошибок и неадекватные функции, которые выплевывают warning'и на события, вполне без этого обходящиеся (например, move_uploaded_file — если файл переместить невозможно, то он возвращает false и выдает warning, когда можно было бы просто вернуть false, а я бы уже разобрался, ругаться пользователю или попытаться переместить еще куда-нибудь).
Если у Вас борьба за производительность идет уже на уровне echo vs print, то мы на совершенно разных уровнях мастерства. Я не достигну такого дзена в реальных проектах. А по последнему пункту — что прикажете делать, когда встроенная функция умеет только генерировать ошибки, но проект построен на исключениях?
Извините, я слоупок.
В этом случае проверяйте в хендлере ошибок значение error_reporting() — насколько мне помнится, оно равно нулю, если ошибку «засобачили» (но я на всякий пожарный его проверяю как if (error_reporting() & $error['type']) { /* print error */ })
UFO just landed and posted this here
>, а мне вот всегда хочется узнать при каких боевых ситуациях могут вызываться замечания и предупреждения.
Кодить надо так, чтоб их по просту не должно возникать: ни замечаний, ни предупреждений.

>set_exception_handler —… Я бы её вообще не переопределял, а то придется в логи о случившимся исключении самому писать.

Переопределение обработки exception необходимо для его собственной обработки, в частности и кастомной записи в лог.
Так она (кастомная запись) чаще всего и нужна и исключительно полезна для анализа инцидентов.
У нас, например, кроме стандартного трейса в лог пишутся параметры учетной записи пользвоателя, некоторые заголовки и параметры запроса.
>Кодить надо так, чтоб их по просту не должно возникать: ни замечаний, ни предупреждений.

Иногда это невозможно. Ну, если @ не использовать.
Замечу, что ini_set('display_errors','On'); не нужен для работы перехвата ошибок и лучше отображение ошибок выключить.
Считаю использование ob_start не рационально. Малого того память жрёт так ещё может быть конфликт с использование других ob_start, например у smarty
Мое мнение, что надо только правильно обернуть, по поводу производительности прокомментировать не могу, профилированием моего проекта пока не было времени заниматься.
Производительность — вопрос относительный. ЕМНИП, apache все равно что-нить да заберет в буфер перед отправкой. Короче, все зависит от архитектуры системы. А вот, например, большие файлы как asset'ы (например, хранятся они в БД, и как статику через apache отдать не выйдет) отдавать не получится (банально память кончится, а если не кончится, то сначала PHP все считает в память, потом отдаст apache'у, тот опять все в буфер заберет (ну, точнее не он… не суть, короче — давно стек протоколов учил), а потом уже отдаст клиенту — в итоге достаточно длительное время клиент будет ожидать скачивание).
Правильная настройка сервера + «Transfer-Encoding: chunked» + flush(); в этом случае поможет
> set_error_handler — Задает определенный пользователем обработчик ошибок.
> Если функция не задана, то PHP лишь пытается вывести данные на экран, а если ему и это не дают, то вообще никаких признаков жизни от этих типов ошибок не возникает.

Дальше смысла читать нету. Автор даже не осилил раздел Error Handling в мануале по PHP. И не почитал про ini-параметры типа log_errors. Ну откуда вы такие неграмотные лезете. А потом наш PHP критикуют зазнайки-явщики (у которых приложения на раз съедают любой объем памяти и у которых кривые насквозь ORM типа Hibernate) и дотнетчики (которые пожизненно проданы в рабство майкрософту).

А теперь я расскажу, как ошибки обрабатывать правильно. Для этого есть втроеннный в PHP (или в SPL?) класс ErrorException. Ставим error_handler такого вида:

error_reporting(-1); // Репортить о любой мелочи, включая Strict Standart Warning

function exception_error_handler($errno, $errstr, $errfile, $errline ) {
if (error_reporting()) {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
}

set_error_hanlder('exception_error_handler');

И больше ни о чем не беспокоимся: любое предупреждение автоматически превращается в исключение, которое, как во всех правильных языках, можно ловить на самом верхнем уровне и выводить текст об ошибке.
UFO just landed and posted this here
А если исключение никто не поймал, то возникнет ошибка Unhandled exception, которая превратится в исключение, которое никто не ловит и которое вызовет ошибку Unhandled exception… ИМХО, ошибки первичней исключений (хотя бы потому что находятся уровнем ниже, как ассемблер ниже си и пайтона).
Хотелось бы ещё добавить к перхвату ошибки и trace этой самой ошибки, чтобы было понятно, как выполнялась программа.
Спасибо за замечательную статью.
UFO just landed and posted this here
Sign up to leave a comment.

Articles