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....