Programmer's Reference Guide

Объект ответаThe Response Object

Плагины

Введение

Архитектура контроллеров включает в себя систему плагинов, которая позволяет добавлять свой код, который будет вызываться при определенных событиях в процессе жизни контроллера. Фронт-контроллер использует брокер плагинов (plugin broker) в качестве реестра пользовательских плагинов, брокер плагинов также обеспечивает вызов методов событий в каждом плагине, зарегистрированном через фронт-контроллер.

Методы событий определены в абстрактном классе Zend_Controller_Plugin_Abstract, от которого должны наследовать все пользовательские плагины:

  • routeStartup() вызывается до того, как Zend_Controller_Front вызовет маршрутизатор для сопоставления запроса с зарегистрированными маршрутами.

  • routeShutdown() вызывается после того, как маршрутизатор завершит обработку запроса.

  • dispatchLoopStartup() вызывается до того, как Zend_Controller_Front войдет в цикл диспетчеризации.

  • preDispatch() вызывается до того, как диспетчером будет вызвано действие. Этот обратный вызов (callback) позволяет реализовать поведение посредника или фильтра. Через изменение запроса и сброс его флага диспетчеризации (методом Zend_Controller_Request_Abstract::setDispatched(false)) текущее действие может быть пропущено и/или заменено на другое.

  • postDispatch() вызывается после того, как действие было вызвано диспетчером. Этот обратный вызов позволяет реализовать поведение фильтра или посредника. Через изменение запроса и сброс его флага диспетчеризации (методом Zend_Controller_Request_Abstract::setDispatched(false)) может быть определено новое действие для диспетчеризации.

  • dispatchLoopShutdown() вызывается после выхода Zend_Controller_Front из его цикла диспетчеризации.

Написание плагинов

Для того, чтобы написать класс плагина, просто включите и расширьте абстрактный класс Zend_Controller_Plugin_Abstract:

<?php
require_once 'Zend/Controller/Plugin/Abstract.php';

class MyPlugin extends Zend_Controller_Plugin_Abstract
{
    // ...
}
        

Ни один из методов класса Zend_Controller_Plugin_Abstract не является абстрактным, поэтому классы плагинов не обязательно должны реализовывать все из перечисленных выше методов событий. Разработчики плагинов могут реализовывать только те методы, которые требуются для их конкретных нужд.

Zend_Controller_Plugin_Abstract также делает объекты запроса и ответа доступными плагинам контроллеров через методы getRequest() и getResponse(), соответственно.

Использование плагинов

Классы плагинов регистрируются через Zend_Controller_Front::registerPlugin(), их можно регистрировать в любой момент времени. Следующий пример демонстрирует использование плагина в цепочке контроллеров:

<?php
require_once 'Zend/Controller/Front.php';
require_once 'Zend/Controller/Router.php';
require_once 'Zend/Controller/Plugin/Abstract.php';

class MyPlugin extends Zend_Controller_Plugin_Abstract
{
    public function routeStartup()
    {
        $this->getResponse()->appendBody("<p>routeStartup() called</p>\n");
    }

    public function routeShutdown($request)
    {
        $this->getResponse()->appendBody("<p>routeShutdown() called</p>\n");
    }

    public function dispatchLoopStartup($request)
    {
        $this->getResponse()->appendBody("<p>dispatchLoopStartup() called</p>\n");
    }

    public function preDispatch($request)
    {
        $this->getResponse()->appendBody("<p>preDispatch() called</p>\n");
    }

    public function postDispatch($request)
    {
        $this->getResponse()->appendBody("<p>postDispatch() called</p>\n");
    }

    public function dispatchLoopShutdown()
    {
        $this->getResponse()->appendBody("<p>dispatchLoopShutdown() called</p>\n");
    }
}

$front = Zend_Controller_Front::getInstance();
$front->setControllerDirectory('/path/to/controllers')
      ->setRouter(new Zend_Controller_Router_Rewrite())
      ->registerPlugin(new MyPlugin());
$front->dispatch();
        

При условии, что вызываемые действия не производят вывод, и что вызвано только одно действие, с плагином выше должен получиться следующий вывод:

<p>routeStartup() called</p>
<p>routeShutdown() called</p>
<p>dispatchLoopStartup() called</p>
<p>preDispatch() called</p>
<p>postDispatch() called</p>
<p>dispatchLoopShutdown() called</p>
        

Замечание: Плагины могут регистрироваться в любой точке выполнения фронт-контроллера. Однако, если событие, для которого плагин имеет зарегистрированный метод события, уже произошло, то этот метод не будет запущен.

Извлечение и работа с плагинами

Иногда может понадобиться отменить регистрацию плагина или извлечь его. Следующие методы фронт-контроллера позволяют сделать это:

  • getPlugin($class) позволяет извлекать плагин по имени класса. Если не найден соответствующий плагин, то возвращается false. Если зарегистрировано более одного плагина этого класса, то будет возвращен массив.

  • getPlugins() возвращает весь стек плагинов.

  • unregisterPlugin($plugin) производит удаление плагина из стека. Вы можете передавать объект плагина или имя класса плагина, регистрацию которого вы хотите отменить. Если вы передаете имя класса, то будут удалены все плагины этого класса.

Плагины, включенные в стандартную поставку

Zend Framework в его стандартной поставке включает в себя плагин для обработки ошибок.

Zend_Controller_Plugins_ErrorHandler

Zend_Controller_Plugins_ErrorHandler представляет собой плагин для обработки исключений, брошенных вашим приложением, включая те, которые были вызваны отсутствием запрошенного контроллера или действия. Он является альтернативой способам, перечисленным в разделе об исключениях MVC.

Основные назначения этого плагина:

  • Перехват исключений, вызваных отсутствием контроллера или метода действия

  • Перехват исключений, брошенных в контроллерах действий

Другими словами, плагин ErrorHandler спроектирован для обработки HTTP-ошибок типа 404 (отсутствует страница) и 500 (внутренняя ошибка). Он не предназначен для отлова исключений, сгенерированных в других плагинах или в процессе маршрутизации.

По умолчанию Zend_Controller_Plugins_ErrorHandler будет производить переход к ErrorController::errorAction() в модуле по умолчанию. Вы можете установить альтернативные значения для перехода, используя набор методов-аксессоров, доступных в плагине:

  • setErrorHandlerModule() устанавливает модуль, на который производится переход.

  • setErrorHandlerController() устанавливает контроллер, на который производится переход.

  • setErrorHandlerAction() устанавливает действие, на которое производится переход.

  • setErrorHandler() принимает ассоциативный массив, который может сожержать любые из ключей 'module', 'controller' или 'action', с которыми он установит соответствующие значения.

Кроме этого, вы можете опционально передавать конструктору ассоциативный массив, который будет в свою очередь передан setErrorHandler().

Zend_Controller_Plugin_ErrorHandler регистрирует перехватчик postDispatch() и проверяет, есть ли зарегистрированые исключения в объекте ответа. Если есть, то производится попытка перехода на зарегистрированное в качестве обработчика ошибок действие.

Если во время диспетчеризации обработчика ошибок произошло исключение, то плагин скажет фронт-контроллеру, чтобы тот бросил исключения, и повторно бросит последнее исключение, зарегистрированное в объекте ответа.

Использование ErrorHandler в качестве обработчика ошибки 404

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

Перехваченные исключения журналируются в объекте, зарегистрированном в запросе. Для его получения используйте метод Zend_Controller_Action::_getParam('error_handler'):

<?php
class ErrorController extends Zend_Controller_Action
{
    public function errorAction()
    {
        $errors = $this->_getParam('error_handler');
    }
}
        

Имея объект ошибки, вы можете получить тип ошибки через $errors->type. Тип ошибки может быть одним из следующих:

  • Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER, означает, что контроллер не был найден.

  • Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION, означает, что запрошенное действие не было найдено.

  • Zend_Controller_Plugin_ErrorHandler::EXCEPTION_OTHER, обозначает другие исключения.

Вы можете производить проверку на первые два типа и в случае положительного результата указывать страницу 404:

<?php
class ErrorController extends Zend_Controller_Action
{
    public function errorAction()
    {
        $errors = $this->_getParam('error_handler');

        switch ($errors->type) {
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
                // ошибка 404 - не найден контроллер или действие
                $this->getResponse()->setRawHeader('HTTP/1.1 404 Not Found');

                // ... получение данных для отображения...
                break;
            default:
                // ошибка приложения; выводим страницу ошибки,
                // но не меняем код статуса
                break;
        }
    }
}
        

Вы можете извлекать исключение, которое инициировало вызов обрабочика ошибок, через свойство exception объекта error_handler:

<?php
public function errorAction()
{
        $errors = $this->_getParam('error_handler');


        switch ($errors->type) {
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
                // ошибка 404 - не найден контроллер или действие
                $this->getResponse()->setRawHeader('HTTP/1.1 404 Not Found');

                // ... получение данных для отображения...
                break;
            default:
                // ошибка приложения; выводим страницу ошибки,
                // но не меняем код статуса

                // ...

                // Журналируем исключение:
                $exception = $errors->exception;
                $log = new Zend_Log(new Zend_Log_Writer_Stream('/tmp/applicationException.log'));
                $log->debug($exception->getMessage() . "\n" .  $exception->getTraceAsString());
                break;
        }
}
        

Управление сгенерированным ранее выводом

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

Если вы хотите, чтобы сообщения об ошибках выводились на этих страницах, то не нужно ничего менять. Если же не хотите выводить такое содержимое, то должны очистить тело ответа до того, как производить рендеринг каких-либо видов:

<?php
$this->getResponse()->clearBody();
        

Примеры использования плагина

Пример #1 Стандартное использование

<?php
require_once 'Zend/Controller/Front.php';
require_once 'Zend/Controller/Plugin/ErrorHandler.php';

$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Zend_Controller_Plugin_ErrorHandler());
            

Пример #2 Установка другого обработчика ошибок

<?php
require_once 'Zend/Controller/Front.php';
require_once 'Zend/Controller/Plugin/ErrorHandler.php';

$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Zend_Controller_Plugin_ErrorHandler(array
    'module'     => 'mystuff',
    'controller' => 'static',
    'action'     => 'error'
)));
            

Пример #3 Использование аксессоров

<?php
require_once 'Zend/Controller/Front.php';
require_once 'Zend/Controller/Plugin/ErrorHandler.php';

$plugin = new Zend_Controller_Plugin_ErrorHandler();
$plugin->setErrorHandlerModule('mystuff')
       ->setErrorHandlerController('static')
       ->setErrorHandlerAction('error');

$front = Zend_Controller_Front::getInstance();
$front->registerPlugin($plugin);
            

Пример контроллера ошибок

Для того, чтобы использовать плагин ErrorHandler, нужен контроллер ошибок. Ниже приведен простой пример такого контроллера.

<?php
class ErrorController extends Zend_Controller_Action
{
    public function errorAction()
    {
        $errors = $this->_getParam('error_handler');

        switch ($errors->type) {
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
                // ошибка 404 - не найден контроллер или действие
                $this->getResponse()->setRawHeader('HTTP/1.1 404 Not Found');

                $content =<<<EOH
<h1>Ошибка!</h1>
<p>Запрошенная вами страница не найдена.</p>
EOH;
                break;
            default:
                // ошибка приложения
                $content =<<<EOH
<h1>Ошибка!</h1>
<p>При обработке вашего запроса произошла непредвиденная ошибка. Пожалуйста, попробуйте позднее.</p>
EOH;
                break;
        }

        // Удаление добавленного ранее содержимого
        $this->getResponse()->clearBody();

        $this->view->content = $content;
    }
}
        

Объект ответаThe Response Object
blog comments powered by Disqus

Select a Version

Languages Available

Components

Search the Manual