Хочу поделиться своими результатами обработки ошибок в графическом интерфейсе. Раньше у меня была проблема, что ошибка в любой модели обрывала все и отображалась в виде красного X-Debuga. Круто да? Следующим этапом был перехват ошибок в set_error_handler() если я не ошибаюсь и отображение в балее меннее красивом окне сообщения об ошибке. Но у этого метода есть недостатоки. Во- первых он не видет форму, чтобы смотреть то что он ввел и одновременно читать в чем не прав, а во- вторых, когда он нажмет кнопку "Назад" все данные, которые он ввел теряются. Маты были жуткие. На третьем этапе борьбы появились конструкции try catch вокруг всех пользовательских действий. Вывод стал красивым вверху страницы и данные сохранялись. Это уже была победа. Но неудобно, что на кождом действии приходится писать этот try - catch и обрабатывать эти исключения одним и тем же кодом. Поэтому появилась идея вывести этот код за пределы экшена. И только сейчас можно сказать, что я победил эту проблему раз и навсегда. Данные теперь сохраняются, страница отображается с сообщением для пользователя, я для этого ничего не делаю. Мечта программиста. Доволен как слон. у меня у контроллера (класс Cont) контрол есть такой метод, который связывает пользовательское действие с обработчиком. выгладит это так: PHP: <?php class Cont { /** * связывает событие с обработчиком. срабатывает на EVENT | EVENT_x | EVENT[ ] * * @param string $event ключ массива $_REQUEST input_key => key * @param string $handler название функции- обработчика * @param bool $continue продолжать после обработчика дальнейшую обработку? может быть насильственно проделжен возвратом return 'continue' * @return void * */ public function bindAction($event, $handler, $continue= false){ if (isset($_REQUEST[$this->input().'_'.$event]) || isset($_REQUEST[$this->input().'_'.$event.'_x'])){ $result= $this->$handler(); if (!$continue && $result!='continue') die; } else{ foreach ($_REQUEST as $PARAM=>$value) if (eregi("^{$event}\\[[.*]\\]$", $PARAM)){ $result= $this->$handler(); if (!$continue && $result!='continue') die; break; } } } } А для того чтобы автоматизировать передачу исключений пользователю, я переопределил этот метод у контралера страницы вот так PHP: <?php class PageCont{ /** * необработанная ошибка приводит к отображению формы на момент ошибки с соответствующим сообщением * * @param string $event событие * @param string $handler обработчик * @param bool $continue продолжить после обработки события может быть насильственно проделжен возвратом return 'continue' */ function bindAction($event, $handler, $continue= false){ $page= $this->m; // ob_start(); try{ parent::bindAction($event, $handler, $continue); } //после PDOException на повтор отправлять нельзя, т.к. транзакция все равно оборвалясь catch(PDOException $e){ throw $e; } //ошибка во время акшэна -> на повтор catch(Exception $e){ unset ($_REQUEST[$this->input().'_'.$event]); unset ($_REQUEST[$this->input().'_'.$event.'_x']); unset ($_REQUEST[$this->input().'_'.$event.'_y']); $page->error= $e; $page->show($this->input()); die; } // //варнинги во время сохранения - показать, но не обрывать // if (ob_get_length()){ // $page->error= new WarningException(ob_get_clean()); // } } } При этом, что очень важно, все данные, которые ввел пользователь, включая выбранные фaйлы, сами собой сначала пихаются в модель, а потом повторно отображаются на странице. Ну и естественно, поскольку контроллеры всех страниц пронаследованы от PageCont, на всем сайте сам собой получился однообразный вывод ошибок. очень стильно, очень предсказуемо для пользователя. И конечно радует, что я пишу только код, а если возникает ошибки, то они сами обрабатываются и отображаются пользователю. Вот несколько примеров, когда действия пользователя дергают модели и натыкаются там на эксепшины. И вот как это отображаются (в принципе вьюшку эксепшена можно нафантазировать как угодно): Понимаете в чем фокус такого трюка? сколько эксепшенов наберется во всех моделях? наверное сотни. Они могут добавляться и удаляться. Но я ни разу не беспокоюсь чтобы их обрабатывать и отображать пользователю. Сюда же входят эксепшены чужих библиотек на сотом уровне вложенности, про которые я ничего не знаю и знать не хочу. меня вообще больше эта проблема не волнует. Если, к примеру даже добавлю новый эксепшен внутри какого- то метода модели, и пользователь на него нарвется, то он и увидет этот эксепшен в верхней части формы, и его данные сохранятся на форме, все это произойдет автоматически, для этого ни строчки не надо писать в форме.
есть файл ob_start(); какой-то код var_dump(smth); бросаем исключение еще какой-то код есть обработчик исключения, установленный через set_exception_handler, который выводит бектрейс, код ошибки и тд. В нем 1 строкой прописано ob_end_flush(); (что б увидеть что var_dump нам скажет). Но выводится только бектрейс. Что я делаю не так? если в конструктор класса с исключениями запихнуть ob_end_flush(); то вывод работает. Это так задумано шоле? Фича такая?
гарантирую это. Кроме того половина страницы должна отрендериться была. Если я отказываюсь от этого обработчика исключений (удаляю или комментирую строку с его назначением) то var_dump показывает то, что нужно, и пол-страницы видно. Кроме того: function debug_exception_handler($ex) { echo "<b>Error :</b>", $ex->getMessage()."<br />\n"; } сообщение пустое. Убираю этот ссаный обработчик - сообщение выводится. Пошел я спать. Не заладилось как-то программирование сегодня.
Все отлично работает. Можешь раскомментировать echo $r и убедиться. PHP: <?php ob_start(); function exception_handler($exception) { $r = ob_get_clean(); //echo $r; echo '<hr>'; echo "Uncaught exception: " , $exception->getMessage(), "\n"; } set_exception_handler('exception_handler'); echo 'гарантирую это. Кроме того половина страницы должна отрендериться была. Если я отказываюсь от этого обработчика исключений (удаляю или комментирую строку с его назначением) то var_dump показывает то, что нужно, и пол-страницы видно. Кроме того: function debug_exception_handler($ex) { echo "<b>Error :</b>", $ex->getMessage()."<br />\n"; } сообщение пустое. Убираю этот ссаный обработчик - сообщение выводится. Пошел я спать. Не заладилось как-то программирование сегодня.'; $a = array('nothing', 'to', 'output'); echo '<hr>'; var_dump($a); throw new Exception('Uncaught Exception'); echo "Not Executed\n"; ob_end_flush();
Забавно. http://pastebin.mozilla-russia.org/102696 вывод то есть после срабатывания исключения вывод произошел даже без ob_end_flush
гага!! Вот разгадка почему так происходило PHP: <? public function render(array $context) { ob_start(); try { $this->display($context); } catch (Exception $e) { ob_end_clean(); throw $e; } return ob_get_clean(); } это шаблонизатор буйствовал.
через register_shutdown_function отлавливаю fatal error'ы. Хочу сделать backtrace - но он пуст. Вернее не то что бы полностью пуст, но в нем только мои обработчики ошибок находятся. Как быть?
PHP: <?php function eh() { print_r(debug_backtrace()); } error_reporting(E_ALL); register_shutdown_function('eh'); class D { public function p() { return $this->c(); } public static function c() { print_r(debug_backtrace()); return 'c'; } } function a() { D::p(); } function b() { echo a(); } D::c(); // тут кой-че будет b(); // а тут только eh хотя я наверно некорректно понимаю что такое backtrace. Это ж не просто какие-то последние действия, а именно действия по вызову этой функции? Тогда логично, что ничего оно мне не покажет.
PHP: <?php function eh() { var_dump(error_get_last()); var_dump(xdebug_get_function_stack()); } error_reporting(E_ALL | E_STRICT); register_shutdown_function('eh'); class D { public function p() { return $this->c(); } public static function c() { var_dump(debug_backtrace()); return 'c'; } } function a() { return D::p(); } function b() { echo a(); } D::c(); // тут кой-че будет b(); // а тут только eh
Код (Text): array 'type' => int 1 'message' => string 'Using $this when not in object context' (length=38) 'file' => string '/home/simpliest/work/test/host1/eh.php' (length=35) 'line' => int 13 array 0 => array 'function' => string '{main}' (length=6) 'file' => string '/home/simpliest/work/test/host1/eh.php' (length=35) 'line' => int 0 'params' => array empty 1 => array 'function' => string 'b' (length=1) 'file' => string '/home/simpliest/work/test/host1/eh.php' (length=35) 'line' => int 31 'params' => array empty 2 => array 'function' => string 'a' (length=1) 'file' => string '/home/simpliest/work/test/host1/eh.php' (length=35) 'line' => int 27 'params' => array empty 3 => array 'function' => string 'p' (length=1) 'class' => string 'D' (length=1) 'file' => string '/home/simpliest/work/test/host1/eh.php' (length=35) 'line' => int 23 'params' => array empty 4 => array 'function' => string 'eh' (length=2) 'file' => string '/home/simpliest/work/test/host1/eh.php' (length=35) 'line' => int 0 'params' => array empty
ну, круто конечно, но мне б более универсальное решение, без xDebug'а. Про error_get_last я знаю. В общем это баг backtrace или все нормально?
Понятия не имею. Поищи в баглисте. Хотя вряд ли баг. Можешь посмотреть еще APD, как альтернативу xdebug, возможно там тоже есть более глубокий трейс.
Костян Нахрена? PHP: <?php for ($i = 1; $i < 5; $i++) { ob_start(); echo 'started ' . $i . '<br>'; } $r = ob_get_clean(); echo '<hr>'; echo $r; echo '<hr>'; echo 'nesting ' . ob_get_level(); ob_end_flush();
PHP: <?php for ($i = 1; $i < 5; $i++) { ob_start(); echo 'started ' . $i . '<br>'; if ($i == 2) { echo 'oops'; throw new Exception('OOOOOpss'); } }