Inicio > desarrollo, menéame, software libre > Haanga: plantillas “Django” para PHP, über eficiente

Haanga: plantillas “Django” para PHP, über eficiente

agosto 11, 2010

A mediados de mayo fui a Japón, la empresa de internet más grande de ese país estaba interesado en montar un Menéame en japonés. En junio las conversaciones estaban más o menos avanzadas y tenía la sensación que algo iba a salir. Sabía que si íbamos a mantener la versión sincronizada con la nuestra, íbamos a tener complicaciones. Así que empecé a pensar seriamente en que debíamos desarrollar o migrar a un sistema de plantillas.

Cuando empecé a desarrollar Menéame en noviembre de 2005 la única alternativa madura que había era Smarty. No me gustó nada, su lenguaje de macros era bastante complicado, además era muy ineficiente. Así, decidí hacerlo sólo en PHP y separar la generación de código en funciones específicas, de forma muy similar a como hace o hacía WordPress.

Mientras no teníamos que responsabilizarnos nosotros de mantener versiones sincronizadas iba muy bien. Como estaba separado por “módulos de interfaz” (cabecera, cajas, pie de página) y orientado a objetos (cada clase como Link, Comment, Post tiene su método print_summary() ) los cambios lo hacíamos rápido y sin complicaciones. Pero ya era hora de mejorarlo y modernizarlo.

Hace meses empecé a mirar si podía reusar el sistema de plantillas de Synfony, CodeIgniter, H2O. El que más se acercaba era H2O, pero no terminaba de convencerme, le faltaban características que necesitamos para Menéame,  su rendimiento tampoco es tan bueno.

Primero pensé que sería mejor hacerlo con el API C de PHP, y así en junio tiré un globo sonda a César Rodas, un chaval de Asunción (Paraguay) que es un puto crack de PHP (desaprovechado, y se dedica a empezar decenas de proyectos al mismo tiempo :-) ).

El globo sonda dio resultado, a los pocos días me dijo que lo estudiaría. Me propuso la alternativa, interpretar las plantillas con PHP, pero como resultado se generaría una función PHP que además se cachearía por el xcache (que es el que usamos en los servidores www de MnM) por lo que sería muy eficiente.

César lo siguió desarrollando, a finales de julio ya tenía una versión bastante madura, le llamó Haanga (en guaraní significa dibujo o figura). Después de una hora de conversación telefónica nos pusimos de acuerdo en qué es lo que faltaba programar, cuál era el fast path, cómo debería implementarlo, y cómo haríamos las pruebas con Menéame (también hablamos de dinero, pero esto no interesa a nadie ;-) ).

Las pruebas consistieron en instalarlo primero en sus servidores e implementar las plantillas para reemplazar el código HTML de print_summary() de las clases Post (las notas) y Comment (los comentarios). Una vez me confirmo funcionaban en su servidor, el viernes 6 de agosto (hace cinco días) lo instalamos en los servidores de Menéame para hacer las primeras pruebas con datos reales. Fueron sorprendentes.

A parti de ese momento se aceleró y en menos de dos días corregimos bugs, lo optimizamos aún más y en la madrugada del domingo al lunes creamos la versión 4 de menéame en el svn y migramos  el servidor en “porducción” al sistema de plantillas. Por ahora sólo con esos dos templates, pero poco a poco vamos migrando a nuevos templates (ya tenemos seis).

Vale, vale ¿pero qué es el Haanga?

Un sistema de plantillas, software libre, con la conocida sintaxis de Django o I2O, con algunas extensiones necesarias para Menéame (como tratar uniformemente objetos y diccionarios, facilitar la traducción con el GNU gettext, ejecutar funciones de forma controlada, pre definir variables globales, eliminar los espacios de sangrados en la plantilla…). Está desarrollado por César Rodas a pedido nuestro para cubrir nuestras necesidades, pero se hizo de tal forma que pueda ser fácilmente integrado en cualquier proyecto PHP de la misma forma en que se integró en Menéame.

Por ese interés de que sea de uso genérico, se mantendrá como proyecto independiente, liderado por César y en su propio repositorio GIT.

Su funcionamiento es simple:

  • Se llama a una función para inicializar variables básicas, como el directorio donde almacenará los ficheros PHP. Por ejemplo en Menéame:
    /* Load template engine here */
    $config = array(
    	'template_dir' => mnmpath.'/'.$globals['haanga_templates'],
    	'cache_dir' =>  $globals['haanga_cache'] .'/Haanga/'.$_SERVER['HTTP_HOST'],
    	'autoload' => FALSE, /* Don't use Haanga's autoloader */
    	'bootstrap' => 'haanga_bootstrap',
    	'compiler' => array( /* opts for the tpl compiler */
    		'if_empty' => FALSE,
    		'autoescape' => FALSE,
    		'strip_whitespace' => TRUE,
    		'allow_exec'  => TRUE,
    		'global' => array('globals', 'current_user'),
    	),
    	'use_hash_filename' => FALSE, /* don't use hash filename for generated php */
    );
    
    require mnminclude.'Haanga.php';
    Haanga::configure($config);
    
  • Se llama a la función para generar la salida en HTML:
        $vars = compact('title', 'greeting', 'id');
        return Haanga::Load('header.html', $vars);
    
  • Haanga::Load verifica si existe el fichero de caché PHP y si es más nuevo que la plantilla. Si no se así, carga el compilador, genera el PHP, lo almacena y lo evalúa.

El código generado es muy eficiente, elimina los espacios innecesarios, no hace cambios de contexto, usa el echo –es construcción del lenguaje–, no usa comillas dobles para los strings. El template más complicado y largo que tenemos por ahora es el header.html, genera las cabeceras XHML y el HTML de los menús superiores. Este es el código generado en PHP, se puede ver que es prácticamente imposible hacerlo más eficiente (diría que imposible, pero seguro se nos escapan algunos trucos que no conocemos).

Actualizado: la plantilla de “links” es la más complicada por ahora, el código generado.

Desde que está funcionando en Menéame no hemos notado variación considerable (es muy difícil medir con precisión tan fina) con el sistema sin plantillas. Hay un pequeño overhead en la verficación de fechas de los ficheros originales y el cache compilado, pero muchas veces se ve compensado porque el código generado es más óptimo que el original programado manualmente a base de echo.

¿Qué falta?

Faltan implementar algunos detalles que queremos para Menéame, como el include condicional de otra plantilla (será la etiqueta try_include), como no usamos en Menéame el sistema de herencia/bloques (para reducir latencias y consumo de memoria, eso da para otro apunte) no lo tenemos probados en “caso real”, pero están implementados y superan los unit tests.

Lo que sí falta es documentarlo y traducir esta documentación al inglés. Lo que queráis colaborar o probar, poneros en contacto con César: crodas en php.net.

Si tenéis sitios PHP con mucho tráfico/carga y probáis el Haanga, seguro que quedaréis muy contentos. En Menéame nos está cambiando la vida… bueno, es una exageración, pero nos divierte mogollón.

  1. agosto 11, 2010 en 4:22 am

    “Hay un pequeño overhead en la verficación de fechas de los ficheros originales y el cache compilado,[...]”

    Se os ha ocurrido eliminar ese overhead con algo tan fácil como NO comprobar la fecha?
    Si solo se tiene que rehacer el fichero de caché cuando cambia la plantilla (y la plantilla no cambia sola…) con un “rm” o lo que sea cada vez que se edita una plantilla debería ser suficiente.

    Bueno, igual he dicho una chorrada, ya que no he leido nada del código, pero eAccelerator hace una cosa similar (configurable).

    Ese pequeño overhead, en un caso práctico real podía llegar casi al 50% de la carga. Poca broma :)

    Suerte!

  2. Hugo
    agosto 11, 2010 en 9:16 am

    Hola Ricardo, no se si cuando dices que has mirado el motor de plantillas de Symfony te refieres al de la versión actual o al que están preparando para Symfony 2.

    El nuevo motor de plantillas copia todo lo de Django y también se transforma a código php para aprovecharse de los compiladores.

    http://www.twig-project.org

    Un saludo!

  3. lector
    agosto 11, 2010 en 11:31 am

    Entiendo que descartasteis hiphop pq ante todo necesitabais un sistema de plantillas.Digo porque el post sobre todo habla de rendimiento.

  4. agosto 11, 2010 en 10:13 pm

    @Hugo: Yo estuve mirando y probando twig, no me convenció que hace mucha evaluación del tipo de dato (si es un array, objeto con o sin iterator) al momento de ejecución.

    En realidad no lo probé antes de comenzar con Haanga, solo vi su código fuente, y vi que aunque el template está compilado necesita cargar muchas cosas en memoria, cosa que agregaría mucho overhead aunque uses xcache.

    Aun así, no me arrepiento de crear/usar Haanga, recién probé Twig, y no puede compilar nada de meneame (faltan muchos tags y filtros). Ademas el código generado requiere muchas cosas.

    + Template: http://pastebin.com/rYW9dDyD
    + Haanga: http://pastebin.com/zf4fzmmt
    + Twig: http://pastebin.com/UXXCTHAq

    Twig es mas rapido al momento de compilar (porque tiene el lexer y parser escrito manualmente), pero haanga es mucho mas rapido una vez que este compilado el template. Una vez compilado Haanga solo cuenta con 2 archivos cargados (lib/Haanga.php y el compilado) mientras que Twig con 15.

  5. agosto 11, 2010 en 11:00 pm

    @friki

    Eliminar el control de fecha es lo peor que se puede hacer cuando tienes varios servidores [y de número e IP variable] como nosotros. No podemos ir a cada uno de ellos para borrar los viejos.

    De todas formas se puede eliminar el control en la configuración, incluso se puede dar un TTL para controle pasado ese tiempo. Para eso se usa la memorica compartida del xcache, pero hice pruebas y va más lento que la verificación de fechas (el PHP hace cache).

    @hugo

    No sé cómo será la versión 2, pero el sistema de plantillas de la 1.3/1.4 del symfony es casi como la que tenía el menéame: casi inexistente.

    El Twig es más pesado (sobre todo por su verificación durante la ejecución) y no tenía todas las cosas que necesitábamos. @césar muestra el ejemplo del código generado.

    @lector

    El hiphop me parece una chorrada enorme, lo comenté en su momento. Con tanto buenos “computer scientists” podrían haber dedicado ese tiempo a generar un buen optimizador para el XCache o similares, habría sido más flexible y con menos esfuerzo que liarte compilando a un lenguaje como el C++.

    Pero sí, lo que necesitábamos eran plantillas eficientes. Quizás @césar quiera pasar el compilador a C++ usando el hiphop ;-)

  6. agosto 12, 2010 en 11:22 am

    Vaya de antemano que tengo experiencia nula en sistema de plantillas y tal, pero me gustaría preguntar:

    ¿Por qué el uso de “compact”? ¿por qué no vale con rellenar el array directamente? ¿es más rápido?

    Enhorabuena por el desarrollo… muy, muy interesante. ;)

  7. agosto 12, 2010 en 12:36 pm

    @skarcha

    Con el compact haces de una vez el equivalente a varias asignaciones. No lo medí, pero estoy seguro que es mucho más rápido.

  8. agosto 12, 2010 en 2:01 pm

    @gallir

    Pero, de todas formas se están realizando esas asignaciones en las variables que le pasas al “compact”, ¿no?

    Otra cosa es que le pases al “compact” el nombre del 20 variables y en realidad estén asignadas sólo 3… entonces a lo mejor merece la pena. Pero en principio no veo mejor la ganancia entre hacer esto:

    $vars = compact(‘title’, ‘greeting’, ‘id’);

    y hacer esto otro:

    $vars = array(‘title’ => ‘Un título’, ‘greeting’ => ‘wowo’, ‘id’ => 234);

  9. agosto 12, 2010 en 4:12 pm

    Ops! Vale, no he dicho nada. Ahora con el estómago lleno lo he entendido. Además de que el ejemplo de mi anterior comentario no era el que quería decir O:)

  10. agosto 12, 2010 en 8:53 pm

    Ya era hora que PHP tuviera un lenguaje de plantillas decente

    Hacer Haanga tomando Django como modelo creo que ha sido muy buena idea. Las plantillas de Django han probado su fiabilidad en sitios con millones de visitas, así que mantener los mismos tags y características no puede ser malo.

    Me gusta la idea de que las plantillas siguen haciendo que el código html sea legible y mantenible. Vamos que si algún día programo seriamente en PHP Haanga será el primer lenguaje de plantillas que miraré.

  11. agosto 17, 2010 en 6:52 pm

    Sólo por si os sirve de referencia, aunque sea sólo para Python y lo usemos a través de Pylons o directamente, Mako es el mejor lenguaje de plantillas con el que hemos trabajado hasta la fecha (digámosle que desde los tiempos del Smarty, como comentas tú mismo en el post). En todos los sentidos.

    No lo puedes usar, que yo sepa, con otra cosa que no sea Python pero, como decía al principio, por si os sirve de referencia.

    http://www.makotemplates.org/

  12. agosto 17, 2010 en 6:52 pm

    Vaya de antemano que tengo experiencia nula en sistema de plantillas y tal, pero me gustaría preguntar

  1. agosto 11, 2010 en 2:46 am
  2. agosto 11, 2010 en 2:55 am
  3. agosto 13, 2010 en 10:13 pm
Los comentarios están cerrados.
Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 470 seguidores

%d personas les gusta esto: