La arquitectura del controlador incluye un sistema de plugins que permite que el código de usuario sea invocado cuando ocurren ciertos eventos durante el ciclo de vida del proceso del controlador. El controlador frontal utiliza un broker de plugins como registro de los plugins de usuario, y el broker de plugins garantiza que los métodos de evento se invoquen en cada plugin registrado con el controlador frontal.
Los métodos de evento están definidos en la clase abstracta
Zend_Controller_Plugin_Abstract, de la cual heredan las
clases de plugin de usuario:
routeStartup()se invoca antes de queZend_Controller_Frontllame a el enrutador para evaluar la solicitud contra las rutas registradas.routeShutdown()se invoca después de que el enrutador termina de enrutar la solicitud.dispatchLoopStartup()se invoca antes de queZend_Controller_Frontentre en su bucle de despacho.preDispatch()se invoca antes de que una acción sea despachada por el despachador. Esta llamada de retorno permite un comportamiento de proxy o de filtro. Al modificar la solicitud y restablecer su indicador de despacho (medianteZend_Controller_Request_Abstract::setDispatched(false)), la acción actual puede ser omitida y/o reemplazada.postDispatch()se invoca después de que una acción es despachada por el despachador. Esta llamada de retorno permite un comportamiento de proxy o de filtro. Al modificar la solicitud y restablecer su indicador de despacho (medianteZend_Controller_Request_Abstract::setDispatched(false)), se puede especificar una nueva acción para el despacho.dispatchLoopShutdown()se invoca después de queZend_Controller_Frontsale de su bucle de despacho.
Para escribir una clase de plugin, simplemente incluya y extienda la
clase abstracta Zend_Controller_Plugin_Abstract:
class MyPlugin extends Zend_Controller_Plugin_Abstract
{
// ...
}
Ninguno de los métodos de Zend_Controller_Plugin_Abstract
es abstracto, lo que significa que las clases de plugin no están
obligadas a implementar ninguno de los métodos de evento disponibles
listados arriba. Los desarrolladores de plugins pueden implementar
solo los métodos que requieran sus necesidades particulares.
Zend_Controller_Plugin_Abstract también pone a
disposición los objetos de solicitud y respuesta para los plugins del
controlador a través de los métodos
getRequest() y getResponse(),
respectivamente.
Las clases de plugin se registran con
Zend_Controller_Front::registerPlugin(), y pueden
registrarse en cualquier momento. El siguiente fragmento ilustra cómo se
puede usar un plugin en la cadena del controlador:
class MyPlugin extends Zend_Controller_Plugin_Abstract
{
public function routeStartup(Zend_Controller_Request_Abstract $request)
{
$this->getResponse()
->appendBody("<p>routeStartup() called</p>\n");
}
public function routeShutdown(Zend_Controller_Request_Abstract $request)
{
$this->getResponse()
->appendBody("<p>routeShutdown() called</p>\n");
}
public function dispatchLoopStartup(
Zend_Controller_Request_Abstract $request)
{
$this->getResponse()
->appendBody("<p>dispatchLoopStartup() called</p>\n");
}
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$this->getResponse()
->appendBody("<p>preDispatch() called</p>\n");
}
public function postDispatch(Zend_Controller_Request_Abstract $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();
Suponiendo que ninguna de las acciones invocadas emite ninguna salida, y solo se invoca una acción, la funcionalidad del plugin anterior aún crearía la siguiente salida:
<p>routeStartup() called</p> <p>routeShutdown() called</p> <p>dispatchLoopStartup() called</p> <p>preDispatch() called</p> <p>postDispatch() called</p> <p>dispatchLoopShutdown() called</p>
![]() |
Nota |
|---|---|
Los plugins pueden registrarse en cualquier momento durante la ejecución del controlador frontal. Sin embargo, si ya ha pasado un evento para el cual el plugin tiene un método de evento registrado, ese método no se activará. |
En ocasiones, puede que necesite anular el registro o recuperar un plugin. Los siguientes métodos del controlador frontal le permiten hacerlo:
getPlugin($class)le permite recuperar un plugin por nombre de clase. Si no coincide ningún plugin, devuelveFALSE. Si hay más de un plugin de esa clase registrado, devuelve un array.getPlugins()recupera toda la pila de plugins.unregisterPlugin($plugin)le permite eliminar un plugin de la pila. Puede pasar un objeto de plugin, o el nombre de clase del plugin que desea anular. Si pasa el nombre de clase, se eliminarán todos los plugins de esa clase.
Zend Framework incluye un plugin para el manejo de errores en su distribución estándar.
El plugin ActionStack le permite gestionar una pila de solicitudes, y opera como un plugin postDispatch. Si ya se detecta un forward (es decir, una llamada a otra acción) en el objeto de solicitud actual, no hace nada. Sin embargo, si no, comprueba su pila y extrae el elemento superior, reenviando a la acción especificada en esa solicitud. La pila se procesa en orden LIFO.
Puede recuperar el plugin desde el controlador frontal en cualquier momento usando
Zend_Controller_Front::getPlugin('Zend_Controller_Plugin_ActionStack').
Una vez que tenga el objeto de plugin, dispone de varios mecanismos que
puede usar para manipularlo.
getRegistry()ysetRegistry(). Internamente, ActionStack usa una instancia deZend_Registrypara almacenar la pila. Puede sustituir una instancia de registro diferente o recuperarla con estos accesores.getRegistryKey()ysetRegistryKey(). Se pueden usar para indicar qué clave de registro usar al extraer la pila. El valor predeterminado es 'Zend_Controller_Plugin_ActionStack'.getStack()le permite recuperar la pila completa de acciones.pushStack()ypopStack()le permiten agregar y extraer de la pila, respectivamente.pushStack()acepta un objeto de solicitud.
Un método adicional, forward(), espera un objeto de solicitud,
y establece el estado del objeto de solicitud actual en el controlador frontal
al estado del objeto de solicitud proporcionado, y lo marca como
no despachado (forzando otra iteración del bucle de despacho).
Zend_Controller_Plugin_ErrorHandler proporciona un
plugin listo para usar para gestionar las excepciones lanzadas por su aplicación,
incluidas aquellas resultantes de controladores o acciones faltantes; es una
alternativa a los métodos listados en la sección de excepciones MVC.
Los objetivos principales del plugin son:
Interceptar excepciones generadas cuando ninguna ruta coincide
Interceptar excepciones generadas debido a controladores o métodos de acción faltantes
Interceptar excepciones generadas dentro de los controladores de acción
En otras palabras, el plugin ErrorHandler está diseñado para manejar errores de tipo HTTP 404 (página faltante) y de tipo 500 (error interno). No está pensado para capturar excepciones generadas en otros plugins.
Por defecto, Zend_Controller_Plugin_ErrorHandler
reenviará a ErrorController::errorAction() en el
módulo predeterminado. Puede establecer valores alternativos usando los distintos
accesores disponibles en el plugin:
setErrorHandlerModule()establece el módulo de controlador a usar.setErrorHandlerController()establece el controlador a usar.setErrorHandlerAction()establece la acción del controlador a usar.setErrorHandler()toma un array asociativo, que puede contener cualquiera de las claves 'module', 'controller' o 'action', con el que establecerá los valores correspondientes.
Adicionalmente, puede pasar un array asociativo opcional al
constructor, que a su vez delegará a setErrorHandler().
Zend_Controller_Plugin_ErrorHandler registra un
hook postDispatch() y comprueba si hay excepciones registradas en
el objeto de respuesta. Si
se encuentra alguna, intenta reenviar a la acción de manejo de errores registrada.
Si ocurre una excepción al despachar el manejador de errores, el plugin indicará al controlador frontal que lance excepciones, y volverá a lanzar la última excepción registrada con el objeto de respuesta.
Dado que el plugin ErrorHandler captura no solo los errores de la aplicación, sino también los errores en la cadena del controlador que surgen de clases de controlador y/o métodos de acción faltantes, puede utilizarse como manejador de errores 404. Para hacerlo, necesitará que su controlador de errores compruebe el tipo de excepción.
Las excepciones capturadas se registran en un objeto registrado en la
solicitud. Para recuperarlo, use
Zend_Controller_Action::_getParam('error_handler'):
class ErrorController extends Zend_Controller_Action
{
public function errorAction()
{
$errors = $this->_getParam('error_handler');
}
}
Una vez que tenga el objeto de error, puede obtener el tipo mediante $errors->type;. Será uno de los siguientes:
Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE, que indica que ninguna ruta coincidió.Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER, que indica que no se encontró el controlador.Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION, que indica que no se encontró la acción solicitada.Zend_Controller_Plugin_ErrorHandler::EXCEPTION_OTHER, que indica otras excepciones.
Puede entonces comprobar los tres primeros tipos, y, en ese caso, mostrar una página 404:
class ErrorController extends Zend_Controller_Action
{
public function errorAction()
{
$errors = $this->_getParam('error_handler');
switch ($errors->type) {
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
// 404 error -- controller or action not found
$this->getResponse()
->setRawHeader('HTTP/1.1 404 Not Found');
// ... get some output to display...
break;
default:
// application error; display error page, but don't
// change status code
break;
}
}
}
Finalmente, puede recuperar la excepción que activó el manejador de errores obteniendo la propiedad exception del objeto error_handler:
public function errorAction()
{
$errors = $this->_getParam('error_handler');
switch ($errors->type) {
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
// 404 error -- controller or action not found
$this->getResponse()
->setRawHeader('HTTP/1.1 404 Not Found');
// ... get some output to display...
break;
default:
// application error; display error page, but don't change
// status code
// ...
// Log the exception:
$exception = $errors->exception;
$log = new Zend_Log(
new Zend_Log_Writer_Stream(
'/tmp/applicationException.log'
)
);
$log->debug($exception->getMessage() . "\n" .
$exception->getTraceAsString());
break;
}
}
Si despacha múltiples acciones en una solicitud, o si su acción
realiza múltiples llamadas a render(), es posible que el
objeto de respuesta ya tenga contenido almacenado en él. Esto puede llevar
a renderizar una mezcla de contenido esperado y contenido de error.
Si desea renderizar los errores en línea en dichas páginas, no será necesario realizar ningún cambio. Si no desea renderizar dicho contenido, debe limpiar el cuerpo de la respuesta antes de renderizar cualquier vista:
$this->getResponse()->clearBody();
Ejemplo 24.18. Uso estándar
$front = Zend_Controller_Front::getInstance(); $front->registerPlugin(new Zend_Controller_Plugin_ErrorHandler());
Ejemplo 24.19. Estableciendo un manejador de errores diferente
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Zend_Controller_Plugin_ErrorHandler(array(
'module' => 'mystuff',
'controller' => 'static',
'action' => 'error'
)));
Ejemplo 24.20. Usando accesores
$plugin = new Zend_Controller_Plugin_ErrorHandler();
$plugin->setErrorHandlerModule('mystuff')
->setErrorHandlerController('static')
->setErrorHandlerAction('error');
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin($plugin);
Para usar el plugin Error Handler, necesita un controlador de errores. A continuación se muestra un ejemplo simple.
class ErrorController extends Zend_Controller_Action
{
public function errorAction()
{
$errors = $this->_getParam('error_handler');
switch ($errors->type) {
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
// 404 error -- controller or action not found
$this->getResponse()->setRawHeader('HTTP/1.1 404 Not Found');
$content =<<<EOH
<h1>Error!</h1>
<p>The page you requested was not found.</p>
EOH;
break;
default:
// application error
$content =<<<EOH
<h1>Error!</h1>
<p>An unexpected error occurred. Please try again later.</p>
EOH;
break;
}
// Clear previous content
$this->getResponse()->clearBody();
$this->view->content = $content;
}
}
Zend_Controller_Plugin_PutHandler proporciona un
plugin listo para usar para convertir los cuerpos de solicitudes PUT en parámetros de solicitud, tal
como con los cuerpos de solicitudes POST. Inspeccionará la solicitud y, si
es PUT, usará parse_str para analizar el cuerpo PUT en bruto
en un array de parámetros que luego se establece en la solicitud. P. ej.,
PUT /notes/5.xml HTTP/1.1 title=Hello&body=World
Para recibir los parámetros 'title' y 'body' como parámetros de solicitud normales, registre el plugin:
$front = Zend_Controller_Front::getInstance(); $front->registerPlugin(new Zend_Controller_Plugin_PutHandler());
Luego puede acceder a los parámetros del cuerpo PUT por nombre desde la solicitud dentro de
su controlador:
...
public function putAction()
{
$title = $this->getRequest()->getParam('title'); // $title = "Hello"
$body = $this->getRequest()->getParam('body'); // $body = "World"
}
...
![[Note]](images/note.png)