Собственные страницы ошибок в Kohana 3.2

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

В теле этого метода мы анализируем в каком режиме находится наше приложение и как будут обрабатываться ошибки системы. Если наше приложение находится в режиме разработки то мы выполняем стандарный метод системы для обработки ошибок (это мы делаем для того что бы видеть красивую и информативную стандартную страницу ошибки в фреймворке), иначе:

  1. Добавляем текст ошибки в лог
  2. Устанавливаем параметры маршрута по умолчанию
  3. Если произошла HTTP ошибка, устанавливаем ее код как параметр маршрута action
  4. Выполняем внутренностный запрос в системе (используем 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)));

9 комментариев

  1. Алекс:

    А у меня не работает, так же продолжает выводить кохановский красный обработчик и там выкидывает 404 exception . Такое ощущение как будто роут не срабатывает… что делать подскажите

    • radik:

      Скорее всего дело в том, что вы пытаетесь запустить из подкаталога docroot сервера, если так то вам нужно правильно настроить параметр base_url в bootstrap.

  2. Спасибо очень помогла статья

  3. Дмитрий:

    Интересный топик, но возник логичный вопрос. Вариант с обращением к несуществующему id товара вполне просто реализовать, но как реализовать вариант когда пользователь обращается к несуществующему контроллеру либо несуществующему екшену?

    • radik:

      В этом случае все ошибки за вас сгенирирует сам фреймворк, если есть желание посмотреть как это работает смотрите метод Kohana_Request_Client_Internal::execute_request.

  4. dolche:

    У меня нет папки kohana в application\classes\ её надо создавать?

    • radik:

      Этот каталог должен быть создан в базовом приложении Kohana, смотрите внимательно, если не найдете то можете попробовать создать… но тут есть масса вариантов из-за которых это может не помочь.

  5. Vital:

    И еще вы ничего не написали про условие
    if (Kohana::DEVELOPMENT === Kohana::$environment)

    По умолчанию DEVELOPMENT, и если не переопределить, то ничего работать не будет.

    Непонятен смысл ‘errors’ => TRUE в Kohana::init
    Хоть это и прописано в оригинальном руководстве, я не вижу чтобы это как-то работало

  6. Vital:

    Не могли бы вы разъяснить конструкции
    $this->template->page = URL::site(rawurldecode(Request::$initial->uri()));
    и
    if ($message = rawurldecode($this->request->param(‘message’)))
    ?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *