TigerZF
🌐Español

14.3. Teoría de funcionamiento

Conseguir que una aplicación MVC quede configurada y lista para despachar ha requerido una cantidad de código cada vez mayor a medida que se dispone de más funcionalidades: configurar la base de datos, configurar la vista y los helpers de vista, configurar los layouts, registrar plugins, registrar helpers de acción, y más.

Además, a menudo querrá reutilizar el mismo código para arrancar sus pruebas, un cronjob o un script de servicio. Aunque es posible simplemente incluir su script de arranque, con frecuencia hay inicializaciones específicas del entorno: puede que no necesite el MVC para un cronjob, o solo la capa de base de datos para un script de servicio.

Zend_Application pretende facilitar esto y fomentar la reutilización encapsulando el arranque en paradigmas OOP.

Zend_Application se divide en tres ámbitos:

  • Zend_Application: carga el entorno PHP, incluyendo los include_paths y la autocarga, e instancia la clase de arranque solicitada.

  • Zend_Application_Bootstrap: proporciona interfaces para las clases de arranque. Zend_Application_Bootstrap_Bootstrap proporciona funcionalidad común para la mayoría de las necesidades de arranque, incluyendo algoritmos de comprobación de dependencias y la capacidad de cargar recursos de arranque bajo demanda.

  • Zend_Application_Resource proporciona una interfaz para los recursos de arranque estándar que pueden ser cargados bajo demanda por una instancia de arranque, así como varias implementaciones de recursos por defecto.

Los desarrolladores crean una clase de arranque para su aplicación, extendiendo Zend_Application_Bootstrap_Bootstrap o implementando (como mínimo) Zend_Application_Bootstrap_Bootstrapper. El punto de entrada (por ejemplo, public/index.php) cargará Zend_Application, y la instanciará pasando:

  • El entorno actual

  • Opciones para el arranque

Las opciones de arranque incluyen la ruta al archivo que contiene la clase de arranque y, opcionalmente:

  • Cualquier include_paths adicional a establecer

  • Cualquier namespace de autoloader adicional a registrar

  • Cualquier configuración de php.ini a inicializar

  • El nombre de clase para la clase de arranque (si no es "Bootstrap")

  • Pares de prefijo de recurso a ruta a utilizar

  • Cualquier recurso a utilizar (por nombre de clase o nombre corto)

  • Ruta adicional a un archivo de configuración a cargar

  • Opciones de configuración adicionales

Las opciones pueden ser un array, un objeto Zend_Config, o la ruta a un archivo de configuración.

14.3.1. Arranque (Bootstrapping)

La segunda área de responsabilidad de Zend_Application es ejecutar el arranque de la aplicación. Los arranques necesitan, como mínimo, implementar Zend_Application_Bootstrap_Bootstrapper, que define la siguiente API:

interface Zend_Application_Bootstrap_Bootstrapper
{
    public function __construct($application);
    public function setOptions(array $options);
    public function getApplication();
    public function getEnvironment();
    public function getClassResources();
    public function getClassResourceNames();
    public function bootstrap($resource = null);
    public function run();
}

Esta API permite que el arranque acepte el entorno y la configuración del objeto application, que reporte los recursos de los que es responsable de arrancar, y luego arranque y ejecute la aplicación.

Puede implementar esta interfaz por su cuenta, extender Zend_Application_Bootstrap_BootstrapAbstract, o utilizar Zend_Application_Bootstrap_Bootstrap.

Además de esta funcionalidad, hay una serie de otras áreas de interés con las que debería familiarizarse.

14.3.1.1. Métodos de recurso

La implementación de Zend_Application_Bootstrap_BootstrapAbstract proporciona una convención sencilla para definir métodos de recurso de clase. Cualquier método protegido cuyo nombre comience con el prefijo _init será considerado un método de recurso.

Para arrancar un único método de recurso, use el método bootstrap(), y páselo el nombre del recurso. El nombre será el nombre del método menos el prefijo _init.

Para arrancar varios métodos de recurso, pase un array de nombres. Para arrancar todos los métodos de recurso, no pase nada.

Tome la siguiente clase de arranque:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    protected function _initFoo()
    {
        // ...
    }

    protected function _initBar()
    {
        // ...
    }

    protected function _initBaz()
    {
        // ...
    }
}

Para arrancar únicamente el método _initFoo(), haga lo siguiente:

$bootstrap->bootstrap('foo');

Para arrancar los métodos _initFoo() y _initBar(), haga lo siguiente:

$bootstrap->bootstrap(array('foo', 'bar'));

Para arrancar todos los métodos de recurso, llame a bootstrap() sin argumentos:

$bootstrap->bootstrap();

14.3.1.2. Arranques que usan plugins de recurso

Para hacer sus arranques más reutilizables, hemos proporcionado la capacidad de trasladar sus recursos a clases de plugin de recurso. Esto permite mezclar y combinar recursos simplemente mediante configuración. Veremos cómo crear recursos más adelante; en esta sección mostraremos solo cómo utilizarlos.

Si su arranque debe ser capaz de usar plugins de recurso, necesitará implementar una interfaz adicional, Zend_Application_Bootstrap_ResourceBootstrapper. Esta interfaz define una API para localizar, registrar y cargar plugins de recurso:

interface Zend_Application_Bootstrap_ResourceBootstrapper
{
    public function registerPluginResource($resource, $options = null);
    public function unregisterPluginResource($resource);
    public function hasPluginResource($resource);
    public function getPluginResource($resource);
    public function getPluginResources();
    public function getPluginResourceNames();
    public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader);
    public function getPluginLoader();
}

Los plugins de recurso básicamente proporcionan la capacidad de crear inicializadores de recurso que pueden reutilizarse entre aplicaciones. Esto permite mantener su arranque real relativamente limpio, e introducir nuevos recursos sin necesidad de tocar el propio arranque.

Zend_Application_Bootstrap_BootstrapAbstract (y Zend_Application_Bootstrap_Bootstrap por extensión) también implementan esta interfaz, permitiéndole utilizar plugins de recurso.

Para utilizar plugins de recurso, debe especificarlos en las opciones pasadas al objeto application y/o al arranque. Estas opciones pueden provenir de un archivo de configuración, o pasarse manualmente. Las opciones serán pares de clave a opciones, siendo la clave el nombre del recurso. El nombre del recurso será el segmento que sigue al prefijo de clase. Por ejemplo, los recursos incluidos con Zend Framework tienen el prefijo de clase "Zend_Application_Resource_"; cualquier cosa que siga a esto sería el nombre del recurso. Como ejemplo,

$application = new Zend_Application(APPLICATION_ENV, array(
    'resources' => array(
        'FrontController' => array(
            'controllerDirectory' => APPLICATION_PATH . '/controllers',
        ),
    ),
));

Esto indica que se debe utilizar el recurso "FrontController", con las opciones especificadas.

Si comienza a escribir sus propios plugins de recurso, o utiliza plugins de recurso de terceros, necesitará indicar a su arranque dónde buscarlos. Internamente, el arranque utiliza Zend_Loader_PluginLoader, así que solo necesitará indicar el prefijo de clase común y los pares de ruta.

Como ejemplo, supongamos que tiene plugins de recurso personalizados en APPLICATION_PATH/resources/ y que comparten el prefijo de clase común My_Resource. Entonces pasaría esa información al objeto application de la siguiente manera:

$application = new Zend_Application(APPLICATION_ENV, array(
    'pluginPaths' => array(
        'My_Resource' => APPLICATION_PATH . '/resources/',
    ),
    'resources' => array(
        'FrontController' => array(
            'controllerDirectory' => APPLICATION_PATH . '/controllers',
        ),
    ),
));

Ahora podría usar recursos de ese directorio.

Al igual que con los métodos de recurso, se utiliza el método bootstrap() para ejecutar los plugins de recurso. Al igual que con los métodos de recurso, puede especificar un único plugin de recurso, varios plugins (mediante un array), o todos los plugins. Además, puede mezclar y combinar para ejecutar también métodos de recurso.

// Ejecutar uno:
$bootstrap->bootstrap('FrontController');

// Ejecutar varios:
$bootstrap->bootstrap(array('FrontController', 'Foo'));

// Ejecutar todos los métodos y plugins de recurso:
$bootstrap->bootstrap();

14.3.1.3. Registro de recursos

Muchos, si no todos, de sus métodos o plugins de recurso inicializarán objetos, y en muchos casos, estos objetos serán necesarios en otras partes de su aplicación. ¿Cómo puede acceder a ellos?

Zend_Application_Bootstrap_BootstrapAbstract proporciona un registro local para estos objetos. Para almacenar sus objetos en él, simplemente los devuelve desde sus recursos.

Para máxima flexibilidad, este registro se denomina internamente "contenedor"; su único requisito es que sea un objeto. Los recursos se registran entonces como propiedades nombradas según el nombre del recurso. Por defecto, se utiliza una instancia de Zend_Registry, pero también puede especificar cualquier otro objeto que desee. Los métodos setContainer() y getContainer() pueden utilizarse para manipular el propio contenedor. getResource($resource) puede utilizarse para obtener un recurso determinado del contenedor, y hasResource($resource) para comprobar si el recurso ha sido realmente registrado.

Como ejemplo, considere un recurso de vista básico:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    protected function _initView()
    {
        $view = new Zend_View();
        // más inicialización...

        return $view;
    }
}

Puede entonces comprobarlo y/u obtenerlo de la siguiente manera:

// Usando el par has/getResource():
if ($bootstrap->hasResource('view')) {
    $view = $bootstrap->getResource('view');
}

// A través del contenedor:
$container = $bootstrap->getContainer();
if (isset($container->view)) {
    $view = $container->view;
}

Tenga en cuenta que el registro, y también el contenedor, no son globales. Esto significa que necesita acceso al arranque para poder obtener recursos. Zend_Application_Bootstrap_Bootstrap proporciona cierta comodidad para esto: durante su ejecución de run(), se registra a sí mismo como el parámetro "bootstrap" del controlador frontal, lo que le permite obtenerlo desde el router, el dispatcher, los plugins y los controladores de acción.

Como ejemplo, si quisiera acceder al recurso de vista anterior desde su controlador de acción, podría hacer lo siguiente:

class FooController extends Zend_Controller_Action
{
    public function init()
    {
        $bootstrap = $this->getInvokeArg('bootstrap');
        $view = $bootstrap->getResource('view');
        // ...
    }
}

14.3.1.4. Seguimiento de dependencias

Además de ejecutar métodos y plugins de recurso, es necesario asegurarse de que se ejecuten una única vez; están pensados para arrancar una aplicación, y ejecutarlos varias veces puede provocar sobrecarga de recursos.

Al mismo tiempo, algunos recursos pueden depender de que se hayan ejecutado otros recursos. Para resolver estos dos problemas, Zend_Application_Bootstrap_BootstrapAbstract proporciona un mecanismo sencillo y efectivo de seguimiento de dependencias.

Como se ha señalado anteriormente, todos los recursos -- ya sean métodos o plugins -- se arrancan llamando a bootstrap($resource), donde $resource es el nombre de un recurso, un array de recursos, o, si se deja vacío, indica que deben ejecutarse todos los recursos.

Si un recurso depende de otro recurso, debería llamar a bootstrap() dentro de su código para asegurarse de que ese recurso se ha ejecutado. Las llamadas posteriores serán entonces ignoradas.

En un método de recurso, una llamada así se vería de la siguiente manera:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    protected function _initRequest()
    {
        // Asegurar que el controlador frontal está inicializado
        $this->bootstrap('FrontController');

        // Obtener el controlador frontal del registro de arranque
        $front = $this->getResource('FrontController');

        $request = new Zend_Controller_Request_Http();
        $request->setBaseUrl('/foo');
        $front->setRequest($request);

        // Asegurar que la petición se almacena en el registro de arranque
        return $request;
    }
}

14.3.2. Plugins de recurso

Como se ha señalado anteriormente, una buena manera de crear recursos de arranque reutilizables y de trasladar gran parte de su código a clases discretas es utilizar plugins de recurso. Aunque Zend Framework incluye una serie de plugins de recurso estándar, la intención es que los desarrolladores escriban los suyos propios para encapsular sus propias necesidades de inicialización.

Los plugins de recurso solo necesitan implementar Zend_Application_Resource_Resource, o, de manera aún más sencilla, extender Zend_Application_Resource_ResourceAbstract. La interfaz básica es simplemente esta:

interface Zend_Application_Resource_Resource
{
    public function __construct($options = null);
    public function setBootstrap(
        Zend_Application_Bootstrap_Bootstrapper $bootstrap
    );
    public function getBootstrap();
    public function setOptions(array $options);
    public function getOptions();
    public function init();
}

La interfaz define simplemente que un plugin de recurso debe aceptar opciones en el constructor, disponer de mecanismos para establecer y obtener opciones, disponer de mecanismos para establecer y obtener el objeto de arranque, y un método de inicialización.

Como ejemplo, supongamos que tiene una inicialización de vista común que utiliza en sus aplicaciones. Tiene un doctype, CSS y JavaScript comunes, y quiere poder pasar un título de documento base mediante configuración. Un plugin de recurso así podría ser como sigue:

class My_Resource_View extends Zend_Application_Resource_ResourceAbstract
{
    protected $_view;

    public function init()
    {
        // Devolver la vista para que el arranque la almacene en el registro
        return $this->getView();
    }

    public function getView()
    {
        if (null === $this->_view) {
            $options = $this->getOptions();
            $title   = '';
            if (array_key_exists('title', $options)) {
                $title = $options['title'];
                unset($options['title']);
            }

            $view = new Zend_View($options);
            $view->doctype('XHTML1_STRICT');
            $view->headTitle($title);
            $view->headLink()->appendStylesheet('/css/site.css');
            $view->headScript()->appendfile('/js/analytics.js');

            $viewRenderer =
                Zend_Controller_Action_HelperBroker::getStaticHelper(
                    'ViewRenderer'
                );
            $viewRenderer->setView($view);

            $this->_view = $view;
        }
        return $this->_view;
    }
}

Mientras registre la ruta de prefijo para este plugin de recurso, podrá utilizarlo en su aplicación. Aún mejor, dado que usa el cargador de plugins, está efectivamente sobrescribiendo el plugin de recurso "View" incluido, asegurando que se utilice el suyo propio.