Introducción
Después de algún tiempo sin postear, me he decidido a retomar mi actividad blogera. Y para ello me he decidido a hablar un poco sobre aplicaciones legacy y cómo podemos hacer para mejorarlas. Así que voy a empezar una serie de entradas, relativas a como podemos migrar una aplicación legacy hacia Symfony2. Sí, Symfony2. Quizá algunos se pregunten porqué Symfony2, si la mayor parte de entradas que había hecho hasta ahora eran acerca de Zend Framework. La verdad es que después de ver el nacimiento de Zend Framework 2 y de trastearlo un poco … He quedado un poco desilusionado. Zend Framework 2 es un muy buen framework, un excelente framework, eso es indudable. Pero después de usar Symfony2 y sus componentes la sensación final que me queda es que hubo unos señores que se sentaron y se propusieron desarrollar un buen framework. Por otra parte hubo unos señores que se sentaron y se propusieron desarrollar el mejor framework del mercado. Aunque me consideraba fanboy de Zend Framework, tengo que reconocer que el sr. Potencier y compañía han hecho un trabajo para sacarse el sombrero.
¿PS-Qué?
Lo primero que vamos a tratar va a ser de los naming conventions de clases. Si aún no te suena, el acrónimo PSR significa “PHP Standards Recomendation” y son una serie de estándards propuestos por el groupo PHP-FIG. PHP-FIG es el acrónimo a su vez de PHP Framework Intergroup y es un grupo que congrega a los principales creadores de frameworks y se sientan a debatir y proponer estándares para unificar ciertos aspectos del desarrollo en PHP. La verdad que ya era hora que naciera un grupo así.
La primera recomendación que sacó PHP-FIG fue el PSR-0 que define una convención para unificar los nombres de las clases y permitir la interoperabilidad de autoloads. Para ello dada una clase se establece una relación 1:1 con un archivo físico mediante las siguientes reglas
- Los nombres de clase deben tener una estructura cómo esta: \Vendor\(Namespace)*\Clase dónde pueden definirse tantos Namespaces cómo se quiera y vale tanto para namespaces de PHP 5.3 como para prefijos de clase al estilo PEAR (Vendor_Namespace_Clase). Ejemplos válidos de ello serían: Symfony\Component\HttpFoundation\Request o Zend_Controller_Front.
- Todos los nombres de clase deben tener un primer nivel de namespace (Vendor: Zend o Symfony siguiendo la pauta del ejemplo del primer punto)
Vale. Muy bonito todo. Pero cómo lo hago?
Muy sencillo, puedes usar el componente de autoloading de Symfony que soporta a rajatabla todo el PSR-0 y además añade algunos extras muy interesantes que nos pueden ayudar. Para ello y paso previo a usar el componente debemos cerciorarnos que nuestras clases cumplen el PSR-0 estricto. Luego, una vez lo hemos descargado e instalado, el componente nos provee con la clase Symfony\Component\ClassLoader\UniversalClassLoader que nos permite cargar cualquier clase que siga el PSR-0.
<?php
require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
use Symfony\Component\ClassLoader\UniversalClassLoader;
$loader = new UniversalClassLoader();
$loader->registerNamespaces(array(
'Symfony' => array(__DIR__.'/src', __DIR__.'/symfony/src'),
'Doctrine\\Common' => __DIR__.'/vendor/doctrine-common/lib',
'Doctrine' => __DIR__.'/vendor/doctrine/lib',
'Monolog' => __DIR__.'/vendor/monolog/src'
));
$loader->registerPrefixes(array(
'Zend_' => __DIR__.'/vendor/zendframework/zf1/src'
));
$loader->register();
Mejorando el rendimiento del ClassLoader
Incluso si quisiéramos mejorar el rendimiento del ClassLoader, el propio componente nos provee de herramientas para hacerlo. En este caso, hablamos de la clase Symfony\Component\ClassLoader\ApcUniversalClassLoader. Esta clase a medida que vaya resolviendo los nombres de clase, los irá almacenando en APC (también existe su homologo en XCache) para luego en sucesivas requests no tener que volver a hacer el lookup de la clase.
<?php
require 'vendor/symfony/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
require 'vendor/symfony/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php';
use Symfony\Component\ClassLoader\ApcUniversalClassLoader;
$loader = new ApcUniversalClassLoader('apc.prefix.');
// register classes with namespaces
$loader->registerNamespaces(array(
'Symfony\Component' => __DIR__.'/component',
'Symfony' => __DIR__.'/framework',
'Sensio' => array(__DIR__.'/src', __DIR__.'/vendor'),
));
// register a library using the PEAR naming convention
$loader->registerPrefixes(array(
'Swift_' => __DIR__.'/Swift',
));
// activate the autoloader
$loader->register();
<?php
require 'vendor/symfony/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
require 'vendor/symfony/src/Symfony/Component/ClassLoader/ApcClassLoader.php';
use Symfony\Component\ClassLoader\UniversalClassLoader;
$loader = new UniversalClassLoader();
// register classes with namespaces
$loader->registerNamespaces(array(
'Symfony\Component' => __DIR__ . '/component',
'Symfony' => __DIR__ . '/framework',
));
$loader->registerPrefixes(array(
'Zend_' => __DIR__ . '/zend'
));
$cachedLoader = new ApcClassLoader('my_prefix', $loader);
// activate the cached autoloader
$cachedLoader->register();
// eventually deactivate the non-cached loader if it was registered previously
// to be sure to use the cached one.
$loader->unregister();
Conclusión
Hemos empezado a migrar nuestra aplicación legacy a través de estandarizar y unificar los nombres de clase de nuestra aplicación. Y lo hemos hecho a través del PSR-0 y del componente ClassLoader de Symfony. Si bien seguramente podrán existir métodos para hacer autoloading seguramente más eficientes, este nos provee una manera fácil e intuitiva para dar nombres a las clases que nos permitirá dado un nombre de clase saber su ubicación exacta casi al instante dentro de nuestra base de código, además de saber exactamente cómo se va a cargar.
Para el siguiente post voy a tratar acerca de cómo podemos manejar dependencias de librerías dentro de proyectos y qué papel juega el PSR-0 con el ClassLoader de Symfony en ello.
