Pull to refresh

Comments 75

Небольшое улучшение по обработке фатальных ошибок:
// clean all buffers
while (ob_get_level()) {
    ob_end_clean();
}
>function error_handler
никогда так не делайте. кидайте ErrorException и обрабатывайте его.
Кидать то хорошо, если сам. А если это Fatal Error, то как исключение его не обработаешь, потому что это не исключение )
>echo "{$errors[$errno]}[$errno] $errstr ($errfile на $errline строке)
\n";
так же очень хочется видеть стек вызовов, входные параметры, разную логику в девелоперском и рабочем окружении
1. Включайте трейс в начале скрипта, получайте стек в обработчике, кто мешает?
2. Ну обьявите окружение, в зависимости от окружения обрабатывайте

Эта статья — не пример использования, а гайд для тех кто желает разобратся. А уж используйте сами как хотите.
Для фатальных ошибок трейс перестанет существовать так как текущий контекст исполнения уничтожается.
Действительно ли отдельная от исключений обработка ошибок имеет смысл? Не слишком ли это все запутано? Не собираются ли разработчики PHP от нее избавляться?
хотят наверное, но не могут. ибо порушится половина сайтов где эти ошибки сейчас просто гасятся собаками или настройками php.ini. обратная совместимость чтоб её. может с выносом всех функций о отдельные неймспейс php и можно будет такое сделать. но сейчас точно нет.
Это вопрос к разработчикам. А пока не избавились иногда без этого всего просто никуда.
Имеет, например, нет смысла бросать исключения в случае E_NOTICE (тем более в случаях E_STRICT или E_DEPRECATED), а сигнализировать об ошибке полезно.
Хм, давно уже не видел людей игнорирующих нотайсы…
Посмотрите на IP.Board (http://community.invisionpower.com/) их даже за ошибки не считают :) но вообще я не говорил что их нужно игнорировать, но вот выкидывать исключения это слишком, ИМХО.
Ну почему в других языках это не «слишком» а вот конкретно в случае пхп становится «слишком»? :)
В каких других языках с динамической типизацией E_NOTICE при пропуске переменной или E_STRICT или тем более E_DEPRECATED выбрасывают исключение или приводит к фатальным ошибкам?
Не совсем понял что вы имеете в виду под «пропуском переменной».

Но если вам так нужен пример к моему коментарию — несуществующий индекс выбросит исключение в питоне, и это нормально.
> Не совсем понял что вы имеете в виду под «пропуском переменной»
Индекс как в вашем примере или свойство объекта или просто переменная с очепяткой.

> несуществующий индекс выбросит исключение в питоне
Но не выбросит в javascript и это тоже нормально.

PS: критичные ошибки (с E_WARNING) лично мне были бы удобнее в виде исключений.
Давайте я открою вам глаза…

<script>
foo={}
if(foo.bar == false) {
        alert("You won't see me!");
}
</script>


<?php
Class Foo
{

}

$bar = new Foo();

if($bar->baz == false && $not_defined_var == false)
{
        echo "BINGO!";
}


Ну и контрольный в голову:

<script>
try {
        if(not_defined_var == false) {
        }
} catch (e) {
        alert('HAHA!');
}
</script>
Не совсем понятно что именно вы хотели показать этими примерами?

1) undefined в js для неопределенных свойств (вообще никаких предупреждений)
2) особенности приведения типов в PHP (тут хоть нотис есть)
3) необходимость явного объявления в переменных в js
Я хотел сказать что некорректно сравнивать php с js, особенно так как это сделали вы:
> несуществующий индекс выбросит исключение в питоне
Но не выбросит в javascript и это тоже нормально.

Или по вашему поведение типа «не, ну переменная конечно не определена но все равно она равна false потому что я так захотел» это нормально? )

Ну а в последнем примере «просто переменная с очепяткой» выбрасывающая исключение :)
> это нормально

Абсолютно нормально. Почему undefined нормально, а это нет?
(они так кстати не равны false, а приводятся к этому типу).
Как заметили выше, есть не только E_NOTICE, но и E_STRICT и E_DEPRECATED. А ещё есть тонны устаревшего кода, который работает и который никто в здравом уме переписывать не будет. Зато будут обновлять версии пхп, дабы улучшить производительность или закрыть дыры в безопасности.

Главная особенность исключений в том, что они фатальны, если не перехвачены. Поэтому не получится просто взять и поменять старую систему ошибок на новую.

Если интересно, вот мнение расмуса на этот счёт.
Я в курсе насчет мнения расмуса, да и системма ошибок в пхп меня вроде как устраивает.
А ответ про нотайсы это просто мысли вслух (которые, впрочем, могут перерасти в нормальную конструктивную дискуссию) :)
Приезжайте к нам в офис — покажу двух
Ну если бы не в другую страну ехать — может и заехал бы ради интереса :)
Так же как и с ошибками, на что генерировать, а на что нет, должно решаться в настройках, по-моему.
Ну, в error_handler вы можете генерировать исключения, более того есть стандартный класс ErrorException, спроектированный специально для этого (в мануале есть пример его использования в этих целях). Несколько строк в начале скрипта и все перехватываемые ошибки будут генерировать исключение, которые можно ловить в месте вызова.

В принципе, наверное, не сложно добавить ini параметр для автоматической генерации, но вряд ли это будет скоро по дефолту.
нет) хотя соглашусь, что похоже.
на самом деле, хотел сделать небольшой обзор механизма обработки ошибок в кохане (с демонстрацией своего мини-модуля), а как начал копать — не смог остановиться и вот результат ))
хотя от первоначальной идеи не отказываюсь — думаю все же написать обзор, как логическое продолжение этой статьи
про кохану было бы интересно, можно ли ждать поста именно с конахой связанного?
ждать конечно можно, но вот точной датой я вас обрадовать не могу. Кто бы мог подумать, но написание статьи отнимает приличное время…
Для 3.2 писал класс перехвата ошибок. Для ajax-а отправлялся json с ошибкой, для CLI текст. Для обычного запроса — выводилась специальная страница ошибки (в зависимости от типа).

Для версии 3.3 все стало несколько проще… Или сложнее — кому как.
Там теперь можно реализовать метод get_response() в классе *_Exception а в переопределенном Kohana_Exception переопределить статический метод response, который отвечает построение «ответа» ошибки.
>при использовании оператора "@" error_reporting() вернет 0
предлагаю при использовании "@" сразу руки отрывать использующему :)

А вообще да, статья полезная для тех кто еще не в курсе, все четко и ясно.
Где вы были 3 года назад когда ну очень надо было а доков почти не было? ))
По поводу @ согласен, но… бывают ситуации разные.
Вот вам пример:
$fp = fsockopen($host, $port, $errno, $errstr);
if (!$fp) {
    echo "ERROR: $errno - $errstr<br />\n";
} else {
    fwrite($fp, "\n");
    echo fread($fp, 128);
    fclose($fp);
}

В случае если вдруг не получилось приконнектиться будет Warning: fsockopen(): unable to connect to
И как же нужно красиво и правильно обрабатывать такую ситуацию без собачки, чтобы ничего лишнего не было в ответе даже на девелоперской машине (т.к. это может, например, попортить json)? Чем тут так плоха собачка @?
А зачем, извините, использовать безбожно устаревший инструментарий?
Не бывает таких ситуаций:
php.net/manual/en/function.socket-connect.php
php.net/manual/ru/function.stream-socket-client.php

Кстати в случае с последним вам только вызов подключения поменять придется, остальной код не изменится.
Разве эти функции не генерируют warnings?
Как это не странно — нет, не генерируют :)
Хм… Извините, был не прав.
Давно с сокетами на пхп не работал, все питон да питон :)

Да, кстати, если вам нужно получать страничку из веба — curl в помощь, тот уж точно не генерирует :)
Обе эти функции еще как генерируют варнинги. Можете сами проверить (только показ варнингов включите).
Вот например: PHP Warning: socket_connect(): unable to connect

И как же быть? Собачка в этом случае всё равно такое зло? Если да, то чем же?
Как я уже написал — был не прав. С тем что собака не зло — не соглашусь никогда после того как неделю искал ошибку что притаилась за @session_start написанным каким-то умником в огромном проекте.
Собака — инструмент. Имхо, руки нужно отрывать, если сразу же после её использования не стоит проверка была ли ошибка.
О собаках. Я прекрасно понимаю потенциальный вред, который они могут нанести. Но считаю что следует поступать рационально, нежели как гласят какие-то догмы.

Случаи типа

@mysql_connect($host, $user, $pass) or die();

не рассматриваем. Здесь любой мало-мальски опытный программист увидит зло.

Но представим ситуацию: нужно удалить файл, если он есть. Если мы сделаем так:

if (file_exists($file)) unlink($file);

Есть вероятность, что файл может быть удален другим скриптом, между проверкой и удалением — получим warning.

А так не получим:

@unlink($file);

Пример выше зависит от того установлен ли обработчик ошибок и того как он работает.

Едем дальше. В последнее время, все чаще прихожу к выводу, что написание

$name = @$_GET['name'] ?: $defaultName;

тупо удобнее и лаконичнее, чем

$name = isset($_GET['name']) ? $_GET['name'] : $defaultName;


Хотелось бы услышать конструктивную критику приведенных примеров.
Кстати, да. Это тоже удобно. Спасибо, что напомнили.
А Вы как считаете, @ хуже чем isset? Т.е. Вы бы мне руки оторвали за такую запись?
Моё мнение — хуже. Управлять ошибками через @ мне не нравится. Но, для простых проектов — это нормальное рабочее решение. Для сложных многоуровневых систем — однозначно подавлять ошибки нельзя.
Хотелось бы услышать конструктивную критику приведенных примеров.
Использование @ для подавления ошибок снижает производительность. Иногда этим можно пренебречь иногда нет.
AndrewStephanoff, не особо в курсе, но разница на простом тесте 0.2-0.3 секунды. PHP 5.4.9.
Спасибо за тест, я сам не проверял — помню по мотивам презентации Змиевского. Видимо раньше было ещё хуже :D
А что за тест такой?

Тест:
<?php

$count = 100000;

$t = microtime(1);
for ($i = 0; $i < $count; $i++) {
	$zzz = isset($xxx);
}
$t = microtime(1) - $t;
echo "isset: $t<br>";

$t = microtime(1);
for ($i = 0; $i < $count; $i++) {
	$zzz = @$xxx;
}
$t = microtime(1) - $t;
echo "@: $t<br>";

Результат PHP 5.3:

isset: 0.0094430446624756
@: 0.080410003662109

Результат PHP 5.4:

isset: 0.0065739154815674
@: 0.059032201766968
Итого, чтобы замедлить скрипт Вам понадобится сто тысяч собак :)

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

set_error_handler(function() {
    sleep(1000);
});

результаты теста очень сильно изменятся :)
У вас результат еще хуже, чем у меня :) (значения с @ и без отличаются практически на порядок).
Тест простой:
% cat test1.php 
<?php 
function x() { } 
for ($i = 0; $i < 1000000; $i++) { x(); }

% time php test1.php
php test1.php  1,24s user 0,03s system 69% cpu 1,833 total

% cat test2.php 
<?php 
function x() { } 
for ($i = 0; $i < 1000000; $i++) { @x(); }

% time php test2.php
php test2.php  1,51s user 0,03s system 75% cpu 2,059 total


+ будет райзиться ошибка в кастомном хендлере, если он есть (это дополнительный тормоз)
+ сложнее дебажить
+ ошибки надо исправлять, а не прятать :)
будет райзиться ошибка в кастомном хендлере, если он есть (это дополнительный тормоз)

Это верно. Стоит уточнить, что обработчик будет срабатывать только при ошибке. Т.е. код вида

@foo($bar);

без ошибки будет трансформироваться в

$e = error_reporting(0);
foo($bar);
error_reporting($e);

а с ошибкой в

$e = error_reporting(0);
foo($bar);
error_handler();
error_reporting($e);

сложнее дебажить

В общем случае — да. Но если возникают подозрения, делается простой grep @ по коду и в обработчике ошибок начинаем логировать/выводить все подряд.

ошибки надо исправлять, а не прятать

@ != "прятать", @ == "подавлять вывод"
А исправлять конечно надо.
@ != «прятать», @ == «подавлять вывод»
По дефолту (если нет своего хендлера) именно прятать. Поскольку подавленный вывод даже в логе (error_log) не осядет.
делается простой grep @ по коду
Для этого у меня есть xdebug с опцией scream :)
Стоит уточнить, что обработчик будет срабатывать только при ошибке.
Верно. Но даже без ошибок мы получаем накладные расходы, которых можно избежать.
подавленный вывод даже в логе (error_log) не осядет.

Кстати, да. Это очень важно.

Для этого у меня есть xdebug с опцией scream

Точно! Это я видел, но еще не пробовал.

даже без ошибок мы получаем накладные расходы

Однозначно.

Я, в принципе, согласен по всем пунктам. Приятно общаться со знающими людьми.
Это все-таки не аргумент, а фуфло.
Особенно при наличии нормальных аргументов.
Есть вероятность, что файл может быть удален другим скриптом, между проверкой и удалением — получим warning.


То есть то что файл удалился не тем скриптом который его должен был удалить — это нормально и у нас все хорошо, да?

Со стороны выглядит примерно так:
1. Какая-то гадость удаляет мои файлы
2. какого хрена?! разбиратся лень
3. проигнорим мы это дело…
Не выглядит. Файл удаляется этим же скриптом. Обычный Race Condition.

С тем что собака не зло — не соглашусь никогда после того как неделю искал ошибку что притаилась за @session_start написанным каким-то умником в огромном проекте.


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

Кстати, в Вашей ситуации с сессией, можно было в обработчике ошибок логировать (или выводить) все подряд, тогда бы Вы не тратили столько времени. «Засобаченая» ошибка в любом случае попадает в обработчик.
Критика очень простая.
Типичный похапешник воображает, что ошибка бывает всегда только одна. Та, про которую он, похапешник, случайно узнал.
Мысль о том, что могут встречаться и другие ошибки, никогда не приходит ему в голову.
И по этой причине он начинает считать себя хитрее и умнее всех.
Ровно до тех пор, когда файл невозможно будет удалить не потому что кто-то успел раньше, а потому что нет прав на удаление или имя файла указано неверно.
В итоге наш похапешник имеет неработающее приложение и ни малейшей идеи — почему.
Согласен. А что это вы решили написать в прошлое :)?
На эту статью сослался очередной терпила на тостере.
Огорчило, что такие позорные статьи висят на хабре, на тему «самый извращенный способ заменить display_errors=0 в php.ini»

А про собаку уж так — под руку пришлось.
Ого, подняли тему из пепла)
Огорчило, что такие позорные статьи висят на хабре, на тему «самый извращенный способ заменить display_errors=0 в php.ini»

Хотелось бы получить ссылку на вопрос с тостера, чтобы понять контекст вопроса + больше аргументов.
В статье нет призыва использовать собаку или игнорировать ошибки.
В статье есть призыв писать при display_errors=on и инструкция, как с честью преодолеть возникающие при этом проблемы.
Забавно — а вот буквально только что сослались на перевод про ODKU :)
С перехватом фатальной ошибки есть небольшой нюанс. Если вы захотите посмотреть стек вызова через debug_print_backtrace(), то увидите, что точка входа в программу — вызов register_shutdown_function(). Чтобы посмотреть нормальный стек вызова, нужно использовать xdebug_get_function_stack() (требует xdebug).
при использовании оператора управления ошибками (знак @) функция, определенная в set_error_handler() все равно будет вызвана

Это точно? Независимо от указанных в set_error_handler классов ошибок? Как-то не обращал внимания, наверное @ крайне редко использую :)
Если вы установили собственную функцию обработки ошибок с помощью set_error_handler(), то она все равно будет вызвана, однако, если внутри этой функции будет вызвана функция error_reporting(), то она вернет 0, если функция, вызвавшая данную ошибку, была подавлена с помощью @.
Читал перед тем как вопрос написать. Но неоднозначно написано, нет примечания типа "… то она всё равно будет вызвана в любом случае, даже если классы ошибок заданных при установке обработчика и произошедшей не совпадают".

Вечерком проверю.
VolCh, походу зависит от заданного типа ошибок.
Упс, там цитата должна была быть :)
да, конечно зависит. если подавляемая ошибка не входит в класс ошибок определенных в set_error_handler(), то функция не будет вызвана.
здесь я хотел заострить внимание на том, что собака не работает как «выключатель» нашего обработчика и этот момент надо учитывать
Спасибо, теперь понятно.
Sign up to leave a comment.

Articles