Обработка ошибок в php, это весьма занятная вещь. С одной стороны, в этом языке есть все, чтобы обработать и вывести ошибку. С другой, в нем есть такой тип ошибок, который ни коим образом не обрабатывается. Это Fatal Error, или ошибки синтаксиса.
Ошибки таких типов не попадают в обработчики, они просто выводятся как есть.
Код ниже, без тени сомнения, взят с фреймворка Yii. Цель данного цикла ни в том чтобы написать свой велосипед, а в том чтобы знать, как этот велосипед в принципе устроен.
Итак, в прошлых уроках мы создали макет приложения, пару страниц, роутер и шаблонизатор. Мы даже объявили такой класс
class Except extends Exception{}
который ничего не делает. Зайду немного вперед, он и сейчас ничего не будет делать. Но мы поймем зачем он в принципе будет нужен в дальнейшем.
Когда в коде мы обрабатывали какую-то бизнес логику, мы выкидывали исключение, таким кодом
throw new Except('Error message');
это приводило к тому, что на экране выводилась подобная запись
Fatal error: Uncaught exception 'Except' with message 'File Z:\home\analize\ideal/application/views/page/pages/B2.php not found' in Z:\home\analize\ideal\ideal\classes\Controller.php:29 Stack trace: #0 Z:\home\analize\ideal\ideal\classes\Controller.php(43): Controller->_renderPartial('Z:\home\analize...', Array, true) #1 Z:\home\analize\ideal\application\views\page\read.php(2): Controller->renderPartial('pages/B2') #2 Z:\home\analize\ideal\ideal\classes\Controller.php(26): include('Z:\home\analize...') #3 Z:\home\analize\ideal\ideal\classes\Controller.php(43): Controller->_renderPartial('Z:\home\analize...', Array, true) #4 Z:\home\analize\ideal\ideal\classes\Controller.php(53): Controller->renderPartial('read', Array, true) #5 Z:\home\analize\ideal\application\controllers\PageController.php(4): Controller->render('read', Array) #6 [internal function]: PageController->actionRead('B2') #7 Z:\home\analize\ideal\ideal\classes\Controller.php(7): call_user_func_array(Array, Array) #8 Z:\home\analize\ideal\ideal\classes\App.php in Z:\home\analize\ideal\ideal\classes\Controller.php on line 29
Это встроенный обработчик ошибок php, вывел для нас эту исчерпывающую информацию. Здесь есть все что нам потребуется для того, чтобы выяснить, что же произошло: само сообщение, стек вызова функций с файлами и методами. Мы видим даже параметры, которые поданы в тех или методах стека. Все что нужно есть, но выводится это не очень красиво.
Для того, чтобы сделать это более удобоваримым, нам потребуется установить обработчик исключений, через метод set_exception_handler. Мы обрабатываем не только исключения, но и ошибки php (все, кроме fatal), поэтому установим еще обработчик set_error_handler.
Но перед этим, добавим в класс App из ideal/classes/App.php
несколько методов
public function handleError($code,$message,$file,$line){}; public function handleException($exception){}; public function displayError($code,$message,$file,$line){}; public function displayException($exception) {}; protected function initSystemHandlers() {};
В первую очередь напишем initSystemHandlers
protected function initSystemHandlers() { set_exception_handler(array($this,'handleException')); set_error_handler(array($this,'handleError'),error_reporting()); }
Все что он делает, это устанавливает два глобальных обработчика исключений, первый для исключений, второй для ошибок
Опишем два этих обработчика.
Обработка ошибок handleError
public function handleError($code,$message,$file,$line) { if($code ∓ error_reporting()) { restore_error_handler(); restore_exception_handler(); try{ $this->displayError($code,$message,$file,$line); } catch(Exception $e) { $this->displayException($e); } } }
error_reporting - это установленный где-то выше, уровень вывода ошибок. Точнее какие ошибки выводить. Имеет кучу вариаций, но если грубо, то E_ALL - выводить все ошибки, 0 - не выводить никакие ошибки. Значение сравнивается побитовым and со значением $code. Если выкинутому коду ошибки дозволено выводится на экран, то продолжаем.
restore_exception_handler
и restore_error_handler
- снимают все установленные обработчики. В конце должен остаться только один и этот один - это текущий метод.
Далее пробуем вывести ошибку на экран, если происходит что-то исключительное, то пытаемся вывести это исключение.
Обработка исключений handleException
public function handleException($exception){ restore_error_handler(); restore_exception_handler(); $this->displayException($exception); }
Идентично первому методу.
далее два метода для вывода ошибок и исключений
Вывод исключений на экран
public function displayException($exception) { echo '<h1>'.get_class($exception)."</h1>\n"; echo '<p>'.$exception->getMessage().' ('.$exception->getFile().':'.$exception->getLine().')</p>'; echo '<pre>'.$exception->getTraceAsString().'</pre>'; }
Тут без сюрпризов, у стандартного класса исключения Exception, есть встроенный метод вывода стека вызовов функций. Также есть метод вывода файла и линии.
get_class - то, зачем был сделан отдельный класс Except. Вы можете создать сколько угодно таких классов, и назвать их все по разному (SQLExcept, CacheExcept и т.д.). Тогда посмотрев на ошибку, вы сразу же определите, какая часть логики вашего приложения сломалась. Кроме того, вы можете переопределить метод getMessage и вывести в нем к примеру SQL запрос, который вызвал сбой.
В следующем уроке, мы поговорим о моделях и работе с БД, вот тут нам и пригодятся эти исключения
Вывод ошибок на экран
С ошибками все немного сложнее. У нас нет класса ошибок, вы можете его создать, но пока вы просто выводим стек вызова при помощи функции debug_backtrace
public function displayError($code,$message,$file,$line){ echo "<h1>PHP Error [$code]</h1>\n"; echo "<p>$message ($file:$line)</p>\n"; echo '<pre>'; $trace=debug_backtrace(); if(count($trace)>3) { $trace=array_slice($trace,3); } foreach($trace as $i=>$t){ if(!isset($t['file'])) $t['file']='unknown'; if(!isset($t['line'])) $t['line']=0; if(!isset($t['function'])) $t['function']='unknown'; echo "#$i {$t['file']}({$t['line']}): "; if(isset($t['object']) && is_object($t['object'])) echo get_class($t['object']).'->'; echo "{$t['function']}()\n"; } echo '</pre>'; exit(); }
Стек вызова может быть огромным, и поэтому ограничиваем его глубину 3-мя методами.
Теперь добавим в код нашего класса App, еще один метод
public function __construct(){ $this->initSystemHandlers(); }
Т.е. устанавливаем наши обработчики, в момент создания экземпляра класса App
Вот и все, теперь исключения выводятся так, как мы этого хотим
Посмотреть, как работает исключение, можно на тестовом сайте. Если вы помните, то если у нас не было файла в папке вида, мы выкидывали сообщение
throw new Except('File '.$fullpath.' not found');
Поэтому просто перейдем на не существующий файл, в контроллере page http://ideal.xdan.ru/about1111.html
Исходные коды урока посмотреть и скачать
Исходные коды конечного фреймворка посмотреть и скачать
Комментарии
Очень хотелось бы увидеть ,как реализовать работу с моделью т.е было описано как вытащить данные из модели, а как вернуть и обработать нет .
Зарание спасибо
А вот ни фига getMessage - final method и переопределение запрещено.
Вот так и не понял назначение : class Except extends Exception - как им пользоваться то?
а в файле Controller.php есть такая странная строчка:
throw new Except('File '.$fullpath.' not found');
В общем может я чего то не понял?
class SQLExcept extends Excep{}
а в классе работы с БД вызываете не
throw new Except('File '.$fullpath.' not found');
а
throw new SQLExcept ($sql.' - doesnt work!!!');
В итоге, когда вылезет это исключение:1) вы сразу поймете что ошибка в классе работы с БД
2)вы увидите сам запрос.
Это и есть роль исключений в пхп, это не только непреднамеренные ошибки, это еще и ошибки логики. Часто программисты забывают про то что SQL ошибки php не покажет. И отладка их бывает очень сложным делом.
С чего вы взяли что getMessage это final ?
С абстрактным классом - хммм не пойму, вроде у вас исходники качал....
хммм может я сам виноват, случайно....
"С чего вы взяли что getMessage это final ?"-
Пробовал переопределить - вылезло сообщение, что final.
Век живи век учись, и вам спасибо!)
throw new SQLExcept ($sql.' - doesnt work!!!');
работает
Неплохо бы придумать механизм замены вывода исключения.
Например в случае любого исключения писать: "Ошибка, обратитесь к администратору!"
В данном случае придётся править App.php метод displayError .
Вот был бы механизм делать это в приложении, чтоб не лезть в ядро....
class MyApp extends App
{
public function displayException($exception)
{
echo "Обратитесь куда нибудь...\n";
}
}
Скажите, пожалуйста, как лучше сделать переадресацию на 404?
Думаю нужно сделать так?
При Except('File '.$fullpath.' not found') делать
header("HTTP/1.0 404 Not Found");
А как тогда сделать, чтобы URL был http://site/404/ и обрабатывался как контроллер, и выдавал текст в $content ?
Скажите, пожалуйста, как лучше сделать переадресацию на 404?
Думаю нужно сделать так?
При Except('File '.$fullpath.' not found') делать
header("HTTP/1.0 404 Not Found");
А как тогда сделать, чтобы URL был http://site/404/ и обрабатывался как контроллер, и выдавал текст в $content ?
Скажите, пожалуйста, как лучше сделать переадресацию на 404?
Думаю нужно сделать так?
При Except('File '.$fullpath.' not found') делать
header("HTTP/1.0 404 Not Found");
А как тогда сделать, чтобы URL был http://site/404/ и обрабатывался как контроллер, и выдавал текст в $content ?