Из коробки фреймворк Kohana 3 не имеет возможности для отображения собственных страниц ошибок, как это было в Kohana 2 поэтому в этом кратком руководстве мы разберем то как сделать собственные страницы ошибок в Kohana 3.
Настройка фреймворка
Для того что бы начать обрабатывать ошибки фреймворка Kohana вам нужно будет в файле application/bootstrap.php в методе Kohana::init добавить директиву обработки ошибок ‘errors’ => TRUE. Эта директива сообщит фреймворку о том что нужно конвертировать PHP ошибки в исключения которые понадобятся нам в дальнейшем.
1. Создаем свой обработчик исключений
Для того, что бы наше приложение смогло обрабатывать ошибки фреймворка нам нужно переопределить метод обработки ошибок системы, для этого нужно создать собственный обработчик ошибок и разместить его в каталоге классов приложения application\classes\kohana\exception.php.
Код обработчика ошибок:
<?php defined('SYSPATH') or die('No direct script access.'); class Kohana_Exception extends Kohana_Kohana_Exception { public static function handler(Exception $e) { if (Kohana::DEVELOPMENT === Kohana::$environment) { parent::handler($e); } else { try { Kohana::$log->add(Log::ERROR, parent::text($e)); $attributes = array ( 'action' => 500, 'message' => rawurlencode($e->getMessage()) ); if ($e instanceof HTTP_Exception) { $attributes['action'] = $e->getCode(); } // Error sub-request. echo Request::factory(Route::get('error')->uri($attributes)) ->execute() ->send_headers() ->body(); } catch (Exception $e) { // Clean the output buffer if one exists ob_get_level() and ob_clean(); // Display the exception text echo parent::text($e); // Exit with an error status exit(1); } } } } // End Kohana_Exception
Давайте кратко разберем что делает данный класс. Единственное что мы тут делаем это переопределение системного метода фреймворка:
public static function handler(Exception $e)
В теле этого метода мы анализируем в каком режиме находится наше приложение и как будут обрабатываться ошибки системы. Если наше приложение находится в режиме разработки то мы выполняем стандарный метод системы для обработки ошибок (это мы делаем для того что бы видеть красивую и информативную стандартную страницу ошибки в фреймворке), иначе:
- Добавляем текст ошибки в лог
- Устанавливаем параметры маршрута по умолчанию
- Если произошла HTTP ошибка, устанавливаем ее код как параметр маршрута action
- Выполняем внутренностный запрос в системе (используем HMVC)
По умолчанию в качестве action для ошибок будет использоваться метод контроллера action_500 (внутренняя ошибка сервера), в случае если произошла HTTP ошибка в качестве действия контролера будет выполняться метод action_<Код HTTP ошибки> это позволит обрабатывать такие ошибки системы как 404, 403 и т. д.
2. Создаем маршрут для обработки ошибок
Добавляем маршрут для обработки ошибок в файл application/bootstrap.php перед маршрутом по умолчанию.
Код маршрута:
Route::set('error', 'error/<action>(/<message>)', array('action' => '[0-9]++', 'message' => '.+')) ->defaults(array( 'controller' => 'error_handler' ));
3. Создаем контроллер для обработки ошибок
Обработкой наших ошибок будет заниматься специальный контроллер расположенный в файле application\classes\controller\error\handler.php.
Код файла контроллера:
<?php defined('SYSPATH') or die('No direct script access.'); class Controller_Error_Handler extends Controller_Template { public $template = 'error'; public function before() { parent::before(); $this->template->page = URL::site(rawurldecode(Request::$initial->uri())); // Если внутренний запрос if (Request::$initial !== Request::$current) { if ($message = rawurldecode($this->request->param('message'))) { $this->template->message = $message; } } else { $this->request->action(404); } // устанавливаем HTTP статус $this->response->status((int) $this->request->action()); } public function action_404() { $this->template->title = '404 Страница не найдена'; // тут мы проверяем пришли попали ли мы на 404 страницу с нашего сайта if (isset ($_SERVER['HTTP_REFERER']) AND strstr($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME']) !== FALSE) { // устанавливаем влаг о том что 404 ошибка была с внутренней сслки $this->template->local = TRUE; } // устанавливаем HTTP статус $this->response->status(404); } public function action_503() { $this->template->title = 'Сервис недоступен'; } public function action_500() { $this->template->title = 'Внутренняя ошибка сервера'; } } // End Error_Handler
Давайте кратко посмотрим что делает наш контроллер.
В методе before мы определяем каким методом пытаются выполнить действие нашего контроллера используется HMVC (внутренный запрос системы) или кто то пытается получить доступ к действиям нашего контроллера через адресную строку, и в случае если у нас не внутренний запрос то мы выполняем действие action_404. Так же мы создали на разные типы ошибок собственные действия которые устанавливают переменные для использования в шаблоне и нужные HTTP коды для ответа сервера.
4. Создаем вид отображения ошибок
Последнее что нам осталось сделать это создать вид для отображения ошибок для этого создадим файл application/views/error.php с следующим содержанием:
<?php defined('SYSPATH') or die('No direct script access.'); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=utf8" /> <meta name="author" content="cyberapp.ru" /> <title><?php echo $title ?></title> <style type="text/css"> * { margin: 0; padding: 0; } html, body{ width: 100%; height: 100%; } #wrap{ width: 900px; margin: 100px auto 0 auto; } </style> </head> <body> <div id="wrap"> <h1><?php echo $title ?></h1> <?php if (isset($local) AND $local) : ?> 404 ошибка произошла по ссылке с нашего сайта. <?php endif; ?> </div> </body> </html>
Итог:
Вот собственно и все, что нужно сделать для того что бы наше приложение научилось отображать собственные страницы ошибок. Ниже представлено несколько примеров того как можно генирировать ошибки системы
// генерируем 503 ошибку throw new HTTP_Exception_503('Сайт не работает'); // генерируем 404 ошибку throw new HTTP_Exception_404(':file не найден.', array(':file' => 'Имя файла')); // генерируем системную ошибку throw new Kohana_Exception('Каталог :dir должен быть доступен для записи', array(':dir' => Debug::path(Kohana::$cache_dir)));
А у меня не работает, так же продолжает выводить кохановский красный обработчик и там выкидывает 404 exception . Такое ощущение как будто роут не срабатывает… что делать подскажите
Скорее всего дело в том, что вы пытаетесь запустить из подкаталога docroot сервера, если так то вам нужно правильно настроить параметр base_url в bootstrap.
Спасибо очень помогла статья
Интересный топик, но возник логичный вопрос. Вариант с обращением к несуществующему id товара вполне просто реализовать, но как реализовать вариант когда пользователь обращается к несуществующему контроллеру либо несуществующему екшену?
В этом случае все ошибки за вас сгенирирует сам фреймворк, если есть желание посмотреть как это работает смотрите метод Kohana_Request_Client_Internal::execute_request.
У меня нет папки kohana в application\classes\ её надо создавать?
Этот каталог должен быть создан в базовом приложении Kohana, смотрите внимательно, если не найдете то можете попробовать создать… но тут есть масса вариантов из-за которых это может не помочь.
И еще вы ничего не написали про условие
if (Kohana::DEVELOPMENT === Kohana::$environment)
По умолчанию DEVELOPMENT, и если не переопределить, то ничего работать не будет.
Непонятен смысл ‘errors’ => TRUE в Kohana::init
Хоть это и прописано в оригинальном руководстве, я не вижу чтобы это как-то работало
Не могли бы вы разъяснить конструкции
$this->template->page = URL::site(rawurldecode(Request::$initial->uri()));
и
if ($message = rawurldecode($this->request->param(‘message’)))
?