Composer logo
Desarrollo

Composer en tu legacy app

Por partes. ¿Qué es composer?


Si eres programador PHP, lo más seguro es que hayas oído hablar de ello. No obstante, si trabajas en “Legacy” -y denominamos así a esa app que desarrolló tu empresa hace unos años- es probable que nunca lo hayas usado.

Composer es un gestor de dependencias. ¿Y esto qué significa?

Imaginemos que nos han pedido una nueva funcionalidad de nuestro proyecto, para la cual necesitamos comunicarnos con APIs de Google. Para ello, la plataforma nos ofrece una librería muy útil. Tradicionalmente, descargaríamos la librería completa, la ubicaríamos en alguna carpeta de nuestro proyecto y, para su uso posterior, realizaríamos un require de la misma en el fichero donde la necesitemos.

<?php
 …
 require_once '/path/to/google-api-php-client/vendor/autoload.php';

No parece complicado, pero la gestión de incluir cada librería en los puntos donde la usaremos, a medida que el proyecto va creciendo, puede llegar a convertirse en un problema.

Además, hay que tener en cuenta que, con este método, hemos descargado la librería y todas las dependencias (otras librerías) que necesita, y estas puede que ya estén instaladas en tu proyecto.

La tarea de Composer es rescatarnos una vez llegamos a este punto, pues es capaz de instalar todas las librerías que requiere tu proyecto con las versiones que necesiten. ¡Y no solo eso! Si nuestras librerías dependen de otras, también descarga todo lo necesario para que funcione, evitando ese horrible dolor de cabeza porque tengamos que hacerlo manualmente

Instalación de composer

Instalar Composer es realmente fácil con el set-up oficial:

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"

php -r "if (hash_file('sha384', 'composer-setup.php') === 'baf1608c33254d00611ac1705c1d9958c817a1a33bce370c0595974b342601bd80b92a3f46067da89e3b06bff421f182') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"

php composer-setup.php --install-dir=bin --filename=composer

php -r "unlink('composer-setup.php');"

Luego, añade el ejecutable a tu directorio bin para tener el comando disponible.

mv bin/composer /usr/local/bin/composer

Uso de composer

Para comenzar a usarlo, tan solo necesitas un fichero composer.json en el raíz de tu proyecto, o en una subcarpeta si lo prefieres

En este fichero, indicaremos cuáles son las dependencias de nuestro proyecto o, dicho de otra forma, qué librerías necesitamos en el mismo. Por ejemplo, si queremos que nuestro proyecto utilice monolog, nuestro fichero composer.json quedará así:

{
     "require": {
         "monolog/monolog": "1.0.*"
     }
 }  

Fácil, ¿Verdad?. Ahora solo tenemos que entrar con nuestro terminal a la carpeta donde hemos ubicado el fichero y ejecutar:

$ composer install

Composer se encargará de descargar la librería e instalarla. Como verás, instala todas las librerías en una carpeta vendor, pero nosotros no tenemos que preocuparnos de dónde queda cada cosa.

Sin embargo, si queremos usar ahora las librerías instaladas por composer en nuestro proyecto, tenemos que realizar un único include del fichero ./vendor/autoload.php.

require __DIR__ . '/vendor/autoload.php';

¡Y listo! Ya podemos hacer eso de las clases de monolog.

$log = new Monolog\Logger('name');

¿Y tengo que realizar este include en todos mis scripts?

Sí, claro. Pero seguro que en tu aplicación hay algún fichero que se incluye en todas las “páginas”. Ya sabes, ese init.php que a lo mejor hace cosas como establecer la conexión a la base de datos o cosas por el estilo. Inclúyelo en ese punto que sabes que va a realizarse en todas tus páginas.

Autoloading y namespaces

¿Qué es el autoloading?

Uno de los problemas más habituales cuando construimos aplicaciones PHP (o cualquier otro lenguaje) es tener una gran cantidad de librerías que, en algún momento de la ejecución de nuestros scripts, deberán ser cargadas.

Una posible solución es cargar todas las librerías que vas a necesitar en el comienzo de tu script:

 <?php
 include_once('clase1.php');
 include_once('clase2.php');
 …
 include_once('claseN.php');
 
 $clase2 = new Clase2();

En esta situación, nos encontramos con el problema de que hemos cargado todas las clases que necesitamos, pero solo hemos usado la clase2. Esto hace más ineficiente nuestra aplicación.

Obviamente, la solución es cargar en cada script únicamente las clases que necesitamos. Y para ello podemos usar técnicas como la de Autoloading, que nos permiten hacerlo sin la necesidad de cargar previamente (requiere o include) el fichero correspondiente.

Una posible solución:

<?php   
// mi propio autocargador de clases   
function cargador($clase) {     
  include 'lib/' . $clase . '.php';   
}   
// registrar el cargador   
spl_autoload_register('cargador');   ​   
$clase2 = new clase2(); 
 

Con la función spl_autoload_register, podemos indicarle a PHP que, cuando instanciamos la clase2, vaya a buscar y cargue el fichero donde hemos declarado esa clase. En el ejemplo, hemos decidido incluir todos nuestros ficheros en la carpeta lib y, además, el fichero deberá llamarse igual que la clase: lib/clase2.php. Hemos conseguido realizar un mapeo de cada una de nuestras clases a sus correspondientes ficheros.

Con esta solución nos encontramos con el problema de que solo estamos cargando un nivel de la carpeta lib, por lo que no podemos agrupar nuestras clases en subcarpetas.

Composer

Para ayudarnos a realizar el Autoloading, contamos con la herramienta Composer.

Composer, además de facilitarnos la gestión de librerías externas y sus dependencias, y mantenerlas actualizadas, también nos ayuda a realizar el Autoloading de nuestras propias clases.

Para realizar el ejemplo anterior, deberíamos declarar en nuestro fichero composer.json el mapeo que queremos realizar (clases > archivos):

 {
    "autoload": {
     "classmap": [
       "lib/"
     ]
   }
 }
 

Desde nuestra consola, lanzamos «composer dump-autoload» y Composer se encargará de crear los autoloaders necesarios para mapear todas las clases que incluyamos en la carpeta lib. Nosotros solo deberemos incluir un fichero generado por Composer:

<?php 
 require DIR . '/vendor/autoload.php';
 $clase2 = new clase2();

Namespaces

Imaginemos que en nuestro proyecto necesitamos, por un lado, comprimir imágenes JPG y, por otro, comprimir archivos en ZIP. Para ello, hemos investigado a ver si hay alguien que haya realizado alguna librería destinada a ello, y encontraremos las librerías (ficticias) PHP-Compress-Image y PHP-Compress-file.

Lamentablemente, las personas que hicieron estas clases no se conocen de nada, y ambos decidieron llamarlas «Compress».

En este caso ficticio, si instanciamos la clase en nuestro código, encontraremos problemas para discernir a qué clase nos referimos. De hecho, PHP arrojaría un error indicando que la clase ya está declarada al cargarla.

Para resolver este problema en la versión PHP5.3, se introdujeron los espacios de nombres: https://www.php.net/manual/es/language.namespaces.php

Con ellos, por convenio, cada programador de la librería define que esta pertenece a su propio espacio de nombres:

<?php 
 namespace pepeTeam\ImageCompress;
 class Compress {
   …
 }
 <?php 
 namespace paco\FileCompress;
 class Compress {
   …
 }



De modo que nosotros indicamos a qué clase nos estamos refiriendo:

$compressor =  new \pepeTeam\ImageCompress\compress();
$fichero = $compressor->compress($fichero);
 …

Sin embargo, incluir el nombre completo del namespace cada vez que instancias una clase puede ser un poco engorroso. Para solucionarlo, podemos hacer uso de la sentencia use. Con ella indicamos que, cuando nos refiramos a la clase “Compress”, hablamos de la clase incluida en un namespace en concreto:

 use \paco\FileCompress\Compress;
 …
 //Comprimir fichero de texto
 $compressor = new Compress();
 $fichero = $compressor->compress($fichero);


Incluso podemos diferenciar entre dos clases del mismo nombre usando alias.

<?php
 use \pacoTeam\FileCompress\Compress as FileCompress;
 use \pepeTeam\ImageCompress\Compress as ImageCompress;
 …
 //Comprimir fichero de texto
 $compressor = new FileCompress();
 $fichero = $compressor->compress($fichero);
 //Comprimir imagen
 $compressor = new ImageCompress();
 $fichero = $compressor->compress($fichero);
 …

Composer, namespaces, autoloading

El estándar PSR-4 permite enlazar los namespaces con directorios de carpetas reales. Existen autoloaders ya hechos para permitirnos implementar PSR-4 autoloading, pero el más utilizado nuevamente es Composer.

Queremos disponer de nuestro propio namespace «MiNamespace» y subnamespaces, dependiendo de las distintas funciones que realicen las clases.

Por ejemplo, nuestra clase destinada a comprimir imágenes estará incluida en el namespace MiNamespace\Images. Si deseamos usar esta clase, solo tenemos que instanciarla con su namespace:

 <?php
 …
 $compressor = new \MiNamespace\Image\Compress(…);
 …


o utilizando la sentencia use:


<?php
use \MiNamespace\Images\Compress;
$compressor = new Compress(…);

Como vemos, no es necesario incluir las clases con sentencias include o requiere. Esto es gracias a que, con Composer, hemos realizado un Autoloading PSR4 de las mismas. Para ello, como podemos ver el el fichero composer.json, además de las librerías que usamos de terceros (las cuales se instalarán en el directorio vendor al hacer un «composer install»), hemos realizado el Autoloading de la carpeta /lib, en donde se almacenará nuestro namespace MiNamespace.


{
"autoload": {
"psr-4": {
"MiNamespace\": "lib/"
}
},
"require": {
...

"phpmailer/phpmailer": "^6.1",

...

}
}

Conclusión

Como ves, Composer es una herramienta muy potente y útil. Facilita la instalación y gestión de librerías de terceros y la organización y el mantenimiento de tus propias clases.

Nuestra recomendación es que no dudéis en incorporarlo a vuestros proyectos. No cuesta mucho y, a la larga, agilizas tus desarrollos despreocupándote de la carga de ficheros.

Además, Composer es ampliamente utilizado por la comunidad y la mayoría de frameworks y proyectos modernos ya lo usan.


No lo dudes, anímate con Composer y disfrútalo.

DEJA UNA RESPUESTA

Tu dirección de correo electrónico no será publicada.