[ZF] Lightweight Bootstrapping

9 views
Skip to first unread message

Andrés Gattinoni

unread,
Jul 21, 2009, 5:59:15 PM7/21/09
to weban...@googlegroups.com
Cómo va?

Esta vez no escribo para pedir ayuda con ZF, sino para dar una suerte de advertencia después de laburar algunos días con ZF 1.8. Por supuesto, en un larguísimo mail como a mí me gusta.
Hace algunos días habíamos estado hablando del tema de los módulos y cómo hacer bootstrapping de distintos módulos con esta cosita loca que introdujo ZF 1.8 que es Zend_Application.

Como la mayoría sabe ZF ofrece una forma de "bootstrapear" las aplicaciones. "Bootstrap" en inglés, además del cordón de una bota se usa como verbo para decir algo así como "iniciar solo" o "iniciar de la nada". La definición de Merrian-Webster es más clara [1]. Hasta ahora el "bootstrap" era un archivo PHP, donde se instanciaban un par de clases, se definían algunas constantes y en teoría no mucho más. Pero eso medio que se pasaba por el orto la POO y quedaba bastante fulero.

En ZF 1.8 quisieron arreglar un poco esto y metieron Zend_Application que es una forma de "objetivar" (u "objetizar" si quieren) el proceso de bootstrapping, y de paso le agregaron nuevas funcionalidades. Entonces a partir de ahora, agregando el concepto de Resources e integrando muy facilmente Zend_Application con Zend_Config, uno desde un archivo de configuración (un INI o un XML), hace la mayor parte de las cosas que uno hacía en el viejo bootstrap.php (definir el directorio del controller, los módulos, las rutas de las vistas, agregar rutas de otras clases, cargar plugins al frontcontroller, etc, etc., etc.). Y además tenés la clase Bootstrap (que extiende de la muy amigablemente llamada Zend_Application_Bootstrap_Bootstrap), donde vos ponés poner todos los métodos "_init*" que quieras, y automáticamente los llama, permitiendote inicializar cualquier cosa (por ejemplo, iniciar Zend_Log y configurarlo como te pinte). Además dentro de esa clase podés llamar al método getOptions, que te trae un array con todas las opciones del archivo de configuración. También, podés registrar esto de los Resources, entonces creás una clase que se llama automáticamente y te sirve para inicar un recurso (una conexión a la DB por ejemplo), y tu clase ya recibe automáticamente los parámetros específicos para ese recurso del archivo de configuración y bla bla bla.

Hasta acá todo muy lindo, pero como ya irán adivinando, la pregunta que se hace obvia es: "¿y esto quién lo paga?". Y la respuesta sería "bueno, ustedes sabrán...". Es decir, este lindo feature se hace muuuy costoso en algunos escenarios y hay que usarlo con mucho cuidado. Lo voy a explicar concretamente con el problema que tuve, que se relaciona con otro mail anterior.

Hace algunos días estuvimos hablando, antes de que la conversación se desvirtuara como de costumbre, de la forma de bootstrapear módulos con ZF 1.8 [2]. Finalmente la solución que encontramos terminó siendo tan sencilla como agregar en el archivo de configuración estas líneas:

resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.modules[] = ""

Ah! Qué lindo! Con esas dos líneas le digo al Bootstrapper que busque en la carpeta "modules" todos los módulos (aparte del default, que en mi caso lo tengo afuera de esa carpeta). Entonces yo adentro de modules tengo mi carpeta "admin", con todos los archivos del módulo homónimo. Y de hecho, ésto me permite hacer otras cosas simpáticas:

admin.resources.frontController.plugins.auth = "Admin_Plugin_Auth"
admin.resources.frontController.plugins.menu = "Admin_Plugin_Menu"

Con esas dos líneas le agrego dos plugins al frontcontroller para el módulo "admin". Buenísimo....... NOT!

ES UNA GARCHAAAAAAAAA!!!!!!!

¿Por qué?

El bootstrapping es el primer proceso que se ejecuta en la aplicación. Esto quiere decir, particularmente, que es anterior al ruteo del request, donde se determina a qué módulo/controlador/acción está dirigido. Entonces, ¿qué pasa?, todo lo que yo hice definiendo plugins o recursos específicos para mi módulo, es totalmente al pedo, porque se va a ejecutar siempre. Es decir, el bootstrapping es de la aplicación en general y todo lo que yo ponga ahí se va a levantar cada vez que levanta la aplicación. De hecho Zend_Application te permite tener distintas clases Bootstrap (una general y después una por módulo), pero siempre te las levanta todas. En otras palabras: el bootstrapping por módulos significa levantar todos los módulos a la vez!!!. Y no hay forma "prevista" de evitar esto. De hecho, si yo le digo que para el módulo admin registre el plugin "Admin_Plugin_Auth" que es el que verifica que el usuario esté logueado (funcionalidad que en mi caso solamente me sirve para el backend, porque el frontend no maneja usuarios), después adentro del plugin también tengo que verificar en qué módulo estoy... porque también va a ser llamado desde el frontend. Una mierda! Y además imagínense si tuviera 3 ó 4 módulos... y los cargo en cada fuckin request.... para mostrar una página estática puedo tardar 5 minutos!

Al principio me pareció que era piloteable, al menos en mi aplicación, porque es bastante sencilla y por eso seguí adelante. Pero después de cargar algunas cosas más me dí cuenta que era un desastre. Las páginas me empezaban a cargar considerablemente mucho más lentas y no se justificaba para nada. Además ya saben cómo soy yo de histérico con optimizar todo por todos lados, los recaudos que tomaba por un lado se me iban a la re mierda con esta pelotudez.

Finalmente decidí (al menos por ahora, hasta que me de cuenta que también es una mierda) implementar una solución/hack para hacer un lightweight bootstrapping.
Por una cuestión de no se qué (llamale "orden") me pintó hacerlo con 4 clases, pero bien se puede hacer en 3. En realidad en cantidad-de-módulos + 1.
Ahora tengo las siguientes clases:

Bootstrap (la original)
Application_Bootstrap_Base (dentro de mi library)
Application_Bootstrap_Frontend
Application_Bootstrap_Backend

Entonces, la original lo único que hace es llamar a mí bootstrap base (es decir, Application_Bootstrap_Base). Por eso que tranquilamente podría haber una clase menos.

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    public function _initLightweightBootstrap ()
    {
        require_once(APPLICATION_PATH . "/../library/Application/Bootstrap/Base.php");
        $bootstrap = new Application_Bootstrap_Base($this->getApplication());
        $bootstrap->bootstrap();
    }
}

Luego, la Base lo que hace es inicializar cuestiones en común (por ejemplo, en mi caso uso el mismo logger, así que lo creo acá, también inicializo las locales, etc.), y tiene un método que es el que hace la magia (o en realidad, que agrega la funcionalidad que necesito):

    protected function _initModuleBootrstrap ()
    {
        if (array_shift(explode(".", $_SERVER['HTTP_HOST'])) == 'admin') {
            $bootstrap = new Application_Bootstrap_Backend($this->getApplication());
        } else {
            $bootstrap = new Application_Bootstrap_Frontend($this->getApplication());
        }
        $bootstrap->bootstrap();
    }

Como verán, de una forma bastante fulera veo qué módulo estoy cargando (el backend está en el subdominio "admin", todos los otros subdominios son frontend). Y después lo que hago es instanciar el bootstrap que corresponde (para el backend o el frontend) y "bootstrapearlo".

Y luego en cada Bootstrap de módulo defino las cuestiones específicas, por ejemplo los paths, los plugins específicos.
De hecho, de esta forma también evito definir muchas rutas al mismo tiempo. Para Frontend defino una y para Backend otra (como el backend no lo va a indexar google elijo las rutas default de ZF, que son mucho más cómodas).

Esto me ahorra cargar muchas pelotudeces innecesarias en cada request y simplificar algunos procesos, definiendo de entrada en qué módulo estoy parado.

Obviamente no es LA solución, y en algunos casos podría no ser tan fácil de implementar (por el tema de las rutas, que en este caso es muy boludo).
Pero creo que es una solución útil, y que en todo caso advierte sobre un problema (o potencial problema) de ZF que es necesario tener en cuenta antes de usar esa herramienta.

Qué larguito en serio me salió el mail, eh?

Salu2

A

[1] http://www.merriam-webster.com/dictionary/bootstrap%5B3%5D
[2] Es una paja, no pienso buscar el link....

pablof...@gmail.com

unread,
Jul 21, 2009, 8:02:27 PM7/21/09
to weban...@googlegroups.com
Un bajon, y gracias por compartirlo.

Cuando tuve que adaptar unos plugins que tenia colgados por ahi que estaban lleno de cosas misticas, me puse a ver un poco, y nose porque no me anduvo, y me cerro mas el hecho de cargar lo que hacen los plugins en un metodo dentro del bootstrap _init* No vi la necesidad, aunque siguen estando muy bueno los plugins pero todavia no los necesite.

Es medio loco que este error exista y lo hayan dejado, es obvio que en cuanto a performance descuidaron muchos aspectos pero este es grave. Habria que ver si ya no sacaron alguna cosa rara para usar Zend_Cache con estas cosas. Quizas lo hayan hecho.




----------------------------------------
Pablo Morales
celular: +54 11 15 63248362
blog: http://blog.pablo-morales.com
linkedln: http://www.linkedin.com/pub/9/528/21
skype: pablofmorales
gtalk: pablof...@gmail.com
msn: pfm...@hotmail.com



2009/7/21 Andrés Gattinoni <andresg...@gmail.com>

Andrés Gattinoni

unread,
Jul 21, 2009, 8:07:30 PM7/21/09
to weban...@googlegroups.com
Es que no creo que piensen que es un error. Yo creo que lo hicieron así por algún motivo.... lo que no sé es cuál será ese motivo jeje.

De hecho buscando algo en estos días, llegué a un flaco que proponía hacer una cuestión X sobre cómo cargar módulos con un plugin onRouteShutdown, y le respondía un flaco de Zend diciendo que justamente eso querían solucionar con Zend_Application (esta discusión era poco antes de que saliera ZF 1.8). Pero la verdad no me metí a ver bien cómo era la discusión porque me dí cuenta que lo que había ahí no me servía, y seguí buscando.

Alan Reid

unread,
Jul 21, 2009, 8:15:57 PM7/21/09
to weban...@googlegroups.com
Gracias por compartirlo!
No será la solución ideal, pero defintivamente es mucho mejor que cargue todo en todos los módulos.

No me había percatado de esto porque todavía no tengo mucho puesto en los bootstrap...
Lo lógico no sería que lo que cargue en todos lados lo que esté en el bootstrap principal y que después le agregue sólo el bootstrap del módulo seleccionado? :S

No se me ocurre razón para que esto sea un "funcionamiento normal".

Saludos!


2009/7/21 Andrés Gattinoni <andresg...@gmail.com>



--
Alan Reid
Cel: (+54 9) 11 6934 7002

Reply all
Reply to author
Forward
0 new messages