Autoloading en Zend Framework
by Christian Soronellas on octubre 9, 2011
Últimamente en emagister, me encuentro dedicado entre otras cosas a ajustar el rendimiento del autoloading de clases en Zend Framework ya qué es una posible mejora que puede reportar un enorme ROI en impacto de negocio. De acuerdo, quizá no directamente en impacto de negocio pero sí de manera indirecta. Probablemente un sitio web que pueda servir más páginas a más usuarios puede ver sensiblemente aumentado sus ratios de conversión. Y eso, creo que puede poner contentos a los chicos del área de negocio. Bien, empecemos.
El componente Zend_Loader
Este quizá sea uno de los componentes más usados en todo el framework, ya que cómo su propio nombre indica es el encargado de toda la carga de clases. Pero una de las cosas que más me desagrada, ya no del componente sino del propio framework en sí, es la manera qué tiene de usarlo, pues algunos de sus subcomponentes (cómo el Zend_Loader_PluginLoader) son instanciados directamente por otras clases produciendo un pequeño acople que a veces resulta un poco molesto. Y más si te dedicas a investigar cómo mejorar precisamente este aspecto.
Pros y contras de usar Zend_Loader
Por una parte, Zend_Loader es un componente muy cómodo para desarrollar sobretodo si estamos usando Zend_Loader_Autoloader. Tiene una integración perfecta con Zend_Controller_Front permitiendo la carga de clases automática dentro de módulos MVC, lo que cómo he dicho es muy cómodo pues el desarrollador ya no tiene que preocuparse de la carga de clases y puede centrarse en otros aspectos de la aplicación.
Cómo contra, está la penalización en rendimiento que puede ocasionar el utilizar según que partes. Por ejemplo, usar Zend_Loader::loadClass tiene una fuerte penalización en rendimiento debido a que siempre tiene que calcular el path a la clase que se le está pidiendo. Cálculos cómo diferenciar un namespace de PHP 5.3 de un namespace de PEAR, comprobar que ciertos caracteres no aparezcan en el nombre de archivo, jugar con el include_path y finalmente incluir la clase. Por suerte este componente ya ha sido deprecado a partir de la versión 1.8 de Zend Framework y a partir de la versión 2.0 va a dejar de existir, además de que se desaconseja encarecidamente su uso en favor del componente Zend_Loader_Autoloader.
Otro componente qué también penaliza bastante en rendimiento es el Zend_Loader_PluginLoader, es decir el responsable de la carga de plugins de Zend Framework y ampliamente usado (y acoplado :) por otros componentes. Este componente es capaz de registrar “prefijos” de classe y por cada uno de ellos asociarle uno o más paths donde puede existir el componente. El coding standards de Zend Framework dicta que los prefijos de clase sería todo el árbol de carpetas en el que se encuentra el componente separados por “_”. Así por ejemplo en el componente Zend_View_Abstract el prefijo de clase sería “Zend_View”. una vez visto esto y analizando un poco a fondo el componente Zend_Loader_PluginLoader se puede ver que al intentar cargar un plugin se recorre todos los prefijos de clases y sus paths asociados buscando un clase que encaje (por medio de Zend_Loader::isReadable, qué también penaliza). Por ejemplo para cargar el plugin (inventado) “Css” teniendo los prefijos registrados “Mi_Libreria_Personalizada” y “Zend_View_Helper” con 5 paths por cada prefijo, podría llegar a hacer 10 entradas en disco por cada petición HTTP.
Tips para mejorar el autoloading en Zend Framework
Voy a hacer una breve síntesis de los métodos qué existen para mejorar la carga de clases y de plugins en Zend Framework. Todo este material se puede encontrar en el sitio web de Zend Framework y por Google, y aquí lo voy a agrupar un poco.
Optimizar el include_path
Hay varias cosas que se pueden hacer para optimizar el include_path. En primer lugar, siempre usar paths absolutos o paths relativos a un path absoluto para hacer mayor uso de las directivas realpath_cache_*, ya que así no obligamos al intérprete de PHP a resolver el path relativo y además vamos a ganar algo en rendimiento ya que vamos a hacer que internamente PHP use el cache de paths absolutos.
<?php $paths = array( realpath(__DIR__ . '/../library'), '.' ); set_include_path(implode(PATH_SEPARATOR, $paths));
Además también podemos reducir el numero de paths dentro de include_path para reducir las posibilidades de búsqueda de clases, definir el path a Zend Framework lo antes posible en la lista de paths de include_path y/o no definir el directorio actual cómo directorio de búsqueda en include_path. Así que si aún no sigues estas reglas, ya lo sabes! ;)
Eliminar require_once innecesarios
Las aplicaciones web actuales deberían todas hacer uso del autoloading de clases. Si aún no lo estás usando, plantéatelo pues es algo bastante necesario ya que unifica los procesos de carga de clases y ayuda a establecer un orden de directorios.
Zend Framework por defecto en casi todas las definiciones de clase usa los require_once, y usando el componente Zend_Loader_Autoloader ya no son necesarios. Y no solo eso sino que de esta manera estamos usando lazy loading para la carga de clases lo que sin duda nos va a dar alguna mejora de rendimiento.
# Script para remover los "require_once" innecesarios en *nix % cd path/to/ZendFramework/library % find . -name '*.php' -not -wholename '*/Loader/Autoloader.php' \ -not -wholename '*/Application.php' -print0 | \ xargs -0 sed --regexp-extended --in-place 's/(require_once)/\/\/ \1/g'
# Script para remover los "require_once" innecesarios en mac (el sed no funciona exactamente igual) % cd path/to/ZendFramework/library % find . -name '*.php' -not -wholename '*/Loader/Autoloader.php' \ -not -wholename '*/Application.php' -print0 | \ xargs -0 sed -E -i '.bak' 's/(require_once)/\/\/ \1/g'
Mejorar la velocidad del PluginLoader
Cómo ya he dicho antes, el PluginLoader es un componente que permite registrar prefijos de clases con múltiples paths. Esto penaliza en rendimiento pues por cada solicitud de carga de plugins se debe examinar cada prefijo con cada path, para encontrar la clase. Aún así el componente Zend_Loader_PluginLoader puede crear una cache de plugins, es decir crea en tiempo de ejecución un archivo PHP dónde va añadiendo includes de todos los plugins de los que ya ha resuelto el path. Así que yo aconsejaría encarecidamente usar esta característica para entornos de producción (no tiene sentido ponerlo en entorno de desarrollo a no ser que se quiera medir el rendimiento de la aplicación).
<?php
$classFileIncCache = APPLICATION_PATH . '/../data/pluginLoaderCache.php';
if (file_exists($classFileIncCache)) {
include_once $classFileIncCache;
}
if ($config->enablePluginLoaderCache) {
Zend_Loader_PluginLoader::setIncludeFileCache($classFileIncCache);
}
Aún sigo necesitando mejor rendimiento en carga de clases, qué puedo hacer?
Si aún así, después de estos tips necesitas mejorar la carga de clases aún existe algún improvment más que se puede usar. En la versión 2.0 de Zend Framework, han mejorado el componente relativo a la carga de clases para sacar las partes deprecadas y para mejorar sustancialmente el rendimiento. Lo bueno es que este componente ya es estable y puede ser usado.
Uno de los puntos del nuevo desarrollo del componente de autoloading en Zend Framework ha sido el crear el autoloader más rápido posible, es decir aquél que no tenga que calcular nada para conocer la ubicación de una clase pero que aún así sepa dónde están ubicadas todas las clases que se van a usar. Esto se ha traducido en un mapa de clases.
En este caso, lo que propone Zend es que antes de hacer un deploy a producción se generen estos mapas de clases (un archivo con un array de clases cómo claves y paths cómo valores) para que la nueva clase ClassMapAutoloader (al fin y al cabo es un “autoloader tonto”) pueda disponer de ellos y mejorar el rendimiento. Si esto lo combinamos con APC que ya de por sí sin hacer nada acelera las aplicaciones web en PHP, tendríamos que poder ver una mejora sustancial del rendimiento.
Y cómo lo puedo usar en mi Zend Framework 1.x?
Pues bien los chicos de Zend Framework se han encargado de hacer un mini port para Zend Framework 1.x y se puede localizar en el github personal de Matthew Weier O’phinney, Techlead de Zend Framework, dónde se explica detenidamente cómo usarlo.


Inglés
Español
One comment
Buena explicación, gracias por tu tiempo.
by Julio Antúnez Tarín on 08/01/2012 at 23:05. #