Из коробки фреймворк 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’)))
?