Etiquetas

, , ,

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.