¿Conoces composer? Seguro que ya lo utilizas desde hace tiempo en tus proyectos de PHP. Composer es un manejador de dependencias y paquetes en PHP, si quieres empezar, hace ya bastante tiempo que hablé de como sacarle partido a composer y de cómo hacer una libreria utilizando composer. Hoy voy a dar una lista de consejos y trucos.
Partimos de que ya utilizas composer, sabes sus comandos básicos y quieres subir al siguiente nivel. Quieres saber qué más puedes hacer, quieres conocer la diferencia entre ~ y ^, quieres saber qué hacer con el composer.lock,…
Lo primero, lee la documentación. Es el mejor consejo que puedo darte, en la documentación de composer está todo y seguro que hay otras muchas funcionalidades que pueden serte útiles en tu proyecto.
Diferencia entre aplicación y librería
Es importante conocer la diferencia entre aplicación y librería, ya que cada una tiene aspectos importantes que debes conocer y buenas prácticas que diferentes, ya que el objetivo de cada una es diferente.
Una librería es un paquete reutilizable, que tu puedes añadir como dependencia, algo como symfony/symfony, doctrine/orm o phpunit/phpunit.
Una aplicación es normalmente una aplicación que depende de varias librerías, normalmente no es reutilizable (otros proyectos no la requerirán como dependencia). Los ejemplos típicos son una página web, un ecommerce,…
Uso de dependencias en un aplicación
Si estás creando una aplicación, deberías utilizar las versiones más específica para definir las dependencias. Por ejemplo, si necesitas manejar variables de entorno, podrías especificar una dependencia como esta: “vlucas/phpdotenv” : “2.4.0”
¿Y esto por qué? porque la actualización de las dependencias debe ser deliberada, no accidental. Puede sonar como una exageración, pero evitará que algún compañero de trabajo actualice accidentalmente todas las dependencias al añadir una nueva librería al proyecto y por ello se corran riesgos innecesarios al desplegar en producción.
Uso de dependencias en una librería
Si estás creando una librería, quieres que esta sea utilizada por el mayor número de aplicaciones posible. Por ejemplo, si estás creando una librería que necesita analizar archivos YML, utilizaremos en nuestro archivo composer.json algo como esto: «symfony/yaml»: «^3.0 || ^4.0»
Esto significa que tu librería puede utilizar cualquier versión 3.x o 4.x, esto es importante porque esta restricción pasará a la aplicación que utilice esta librería que estás creando.
Que diferencia hay entre ~ y ^
Para entrar en materia voy a refrescar un poco eso de “semantic version”. Básicamente se puede versionar un software con 3 números: mayor.minor.patch. Se supone que estos 3 números representan específicamente la API de la librería. Por lo que, en un “mundo perfecto” realizar cambios y subir de versión minor y/o patch debería hacer que la librería siguiese 100% compatible con las versiones anteriores. La mayoría de las aplicaciones están diseñadas para establecer dependencias con esto en mente.
El uso básico es utilizando los operadores < y >:
- Con > hacemos que composer tenga que descargarse una versión mayor. por ejemplo «phpunit/phpunit»: «>5.0» estamos indicando que la librería sea mayor que 5.0
- Con > hacemos que composer tenga que descargarse una versión mayor. por ejemplo «phpunit/phpunit»: «<5.4.3» estamos indicando que la librería sea menor que 5.4.3
Con los operadores ~ y ^ podemos restringir las versiones de las librerías de diferentes maneras.
El operador ~(virgulilla o tilde en inglés o «lo de arriba de la eñe») especifica una versión mínima y crea un límite superior que debe ser menor que la siguiente versión principal. Esto permite versiones minor y patch por encima de la versión:
- ~ 1.2 significa> = 1.2.0 y <2.0.0.
- ~ 1.2.3 significa> = 1.2.3 y <2.0.0.
El operador ^(acento circunflejo o caret en inglés o «el techito») sigue las mismas reglas que el operador ^ pero con una distinción importante. Solo permitirá que suba el último dígito patch. Esto significa que:
- ^ y ~ se comportan igual si no se proporciona el parche. Por ejemplo ~ 1.2 es lo mismo que ^ 1.2.0 y lo mismo que ^1.2.
- ~ 1.2 significa> = 1.2.0 y <2.0.0, sin embargo ~ 1.2.0 significa> = 1.2.0 y <1.3.0.
Haz commit del composer.lock en tus aplicaciones
Si estas creando una aplicación web, un ecommerce,… tienes que comitear el fichero composer.lock. Esto garantiza que tanto tu como tus compañeros vais a utilizar las mismas versiones de todas las librerías.
Puede sonar un poco tontería, sobre todo cuando antes he comentado que para aplicaciones debes ser estrictos en las versiones de tu composer.json. Pero no, cuando decidimos utilizar en nuestro aplicación una librería como por ejemplo “egulias/EmailValidator”: “2.1.3”, esta librería tiene dependencias como «doctrine/lexer»: «^1.0.1», por lo que si no commiteas el composer.lock, no obtendrás exactamente el mismo conjunto de dependencias entre todas las personas del equipo.
En tus librerías añade el fichero composer.lock al gitignore
Si estás creando una librería, (la llamaremos jeslopcru/awesome-postureo), no deberías commitear el archivo composer.lock ya que no tiene ningun efecto en los aplicaciones que utilicen tu librería jeslopcru/awesome-postureo.
Imagina que estás desarrollando tu librería y le añades la dependencia “egulias/EmailValidator”: “2.1.3” al principio del desarrollo, si la librería sube de versión y elimina una funcionalidad que estabas utilizando, no te darás cuanta de que tu librería falla hasta que no la utilices en una aplicación real. Para paliar esta situación, lo mejor es añadir el composer.lock al .gitignore en tus librerías.
Añade Travis CI
Si tu librería es open source, añade Travis CI para testearla con diferentes versiones de PHP, algo así:
language: php php: - 5.6 - 7.0 - 7.1 - 7.2 before_script: - composer install script: - phpunit --configuration phpunit.xml.dist --coverage-text notifications: email: false
Por defecto, composer siempre descarga la última versión disponible del paquete, aunque afortunadamente podemos indicarle con –prefer-lowest (debe utilizarse con la opcion –prefer-stable) que descargue la versión más antigua posible.
Mantén las librerías ordenadas por orden alfabético
Es una buena práctica mantener las dependencias require y require-dev ordenadas por orden alfabético, ya que te prevendrá de posibles errores en un merge.
Como es un poco rollo hacerlo manualmente, es posible configurar composer para que lo haga automáticamente, para ello solo tenemos que añadir lo siguiente al fichero composer.json
config": { "sort-packages": true },
Luego con tan solo utilizar el comando require las dependencias se ordenarán automáticamente.
No intentar mergear el fichero composer.lock cuando surjan conflictos
Si has añadido dependencias al fichero composer.json y por tanto se ha actualizado el composer.lock tienes que tener cuidado al hacer un merge o rebase con otra rama, porque si otro compañero ha hecho lo mismo surgirán conflictos en el merge.
No tienes que resolver manualmente los conflictos del composer.json, ya que el fichero contiene hashes que harán que el fichero resultante sea incorrecto.
Para que esto no te vuelva a pasar, lo mejor sería que crees un fichero .gitattributes en la raíz del proyecto para que git no intente mergear el fichero composer.lock.
Así que creas el fichero .gitattributes y le añades la linea:
/composer.lock -merge
Para minimizar este problema lo ideal es utilizar “feature branches” de corta duración, así se minimiza el riesgo de quedarte demasiado lejos del HEAD.
¿Qué pasa si aun así te entras con el conflicto? Lo mejor es quedarte con la versión que venga de master. Ahora tienes cambios solo en composer.json. Es decir, tienes el composer.lock como master y el fichero composer.json como una mezcla de las dependencias de master más las que tu has añadido. Ahora puedes ejecutar el comando:
$ composer update --lock
Ahora se actualizará el fichero composer.lock con los cambios que añadiste en el composer.lock
¿Que diferencias hay ente require y require-dev?
La diferencia entre los bloques require y require-dev es que los paquetes que añadas a requiere-dev son solo necesarios para desarrollo. Por ejemplo PHPUnit, Behat, PHP-CS-Fixer, solo los necesitas para desarrollar, no para que la librería funcione.
Puedes definir otras restricciones en composer.json
Aparte de las versiones de las librerías, también puedes definir otras restricciones.
Puedes definir la versión de PHP que tu librería o proyecto necesita, para ello solo tienes que añadir al fichero composer.json lo siguiente:
"require": { "php": ">7.0", },
Si tu librería/aplicación necesita alguna extensión especial también puedes añadirla al archivo composer.json
"require": { "ext-mbstring": "*", "ext-pdo_mysql": "*", },
Validar composer.json
También es posible que necesites saber si todo está ok en tu fichero, para ello puedes ejecutar el comando:
$ composer validate --no-check-all --strict
Este comando es super-útil cuando tienes una librería, por ello puedes añadir esta libea a tu sistema de CI.
Si utilizar PHPStorm usa el plugin de composer.json
En PHPStorm tienes cantidad de plugins, para composer hay uno esencial y es “PHP composer.json support” Con este plugin tendrás autocompletado del fichero composer.json
Cómo utilizar paquetes privados en tus proyectos
Si necesitas utilizar un repositorio privado dentro de proyecto solo tienes que añadirlo al fichero composer.json
"repositories": [ { "type": "vcs", "url": "git@bitbucket.org:qashops/qashops-vitral.git" } ],
Con esto ya puedes utilizar tu librería dentro de la aplicación así:
"require": { "qashops/qashops-vitral": "^1.0", },
Cómo utilizar temporalmente una rama con un bugfix
Si encuentras un bug en un repositorio público y lo fixeas en tu propio fork necesitas instalar tu propio repositorio en lugar la oficial (hasta que ese bugfix se mergee y se publique la nueva versión corregida)
{ "repositories": [ { "type": "vcs", "url": "https://github.com/you/monolog" } ], "require": { "symfony/monolog-bundle": "2.0", "monolog/monolog": "dev-bugfix as 1.0.x-dev" } }
Cómo desarrollar varios proyectos a la vez cuando están relacionados
Imagina que estás desarrollando tu aplicación y que tienes una librería propia que estás utilizando. Hasta aquí todo normal, pero te das cuenta de que necesitas añadir una nueva funcionalidad a la librería para poder utilizarla en tu aplicación, ¿como lo haces?
Sencillo, utilizando enlaces simbólicos, si ambos proyectos están descargados en tu máquina local solo tienes que decirle a composer.json de tu proyecto la ruta de tu librería, algo así:
"name": "jeslopcru/awesome-posturep-web", ... "repositories": [ { "type": "path", "url": "../libreria-postureo", "options": { "symlink": true } } ], "require": { "jeslopcru//libreria-postureo": "dev-master", },
En la aplicación web, tenemos como dependencia el paquete “librería-postureo”, añadiendo la ruta e indicando a composer la ruta de la librería ya lo tenemos todo listo.
Actualizar solo una dependencia
Hay veces en las que solo necesitas actualizar una de las dependencias del proyecto y dejar el resto en la misma versión. Para ello es posible ejecutar composer update pero solo para ciertas dependencias.
Por ejemplo si necesitamos actualizar solo phpunit a la nueva versión solo tenemos que ejecutar
$ composer update phpunit/phpunit --with-dependencies
Incluso si lo necesitas puedes actualizar todas las dependencias con un mismo namespace así:
$ composer update symfony/* --with-dependencies
Cómo actualizar las dependencias de la aplicación de manera segura
Ya sabes que debes actualizar las dependencias regularmente, lo que quiero comentarte es que esa actualización debe ser deliberada y consciente. En mi opinión debería ir de manera separada, no como parte de una refactorización o de una nueva feature.
Para saber cuando actualizar las dependencias de tus proyectos se puede utilizar el comando composer outdated.
Si dicho comando nos indica que es necesario actualizar dependencias yo lo haría más o menos así.
- Revisar el changelog de esa dependencia por si hay alguna incompatibilidad
- Crear una nueva rama en git.
- Actualizar en el fichero composer.json las dependencias.
- Ejecutar el comando composer update phpunit/phpunit –with-dependencies para actualizar solo las dependencias que necesites.
- Lanzar los test y comprobar que la aplicación sigue funcionando correctamente
- commitear los cambios tanto de composer.json, composer.lock como los ficheros que hayan cambiado
- Esperar a que el sistema de CI termine la build
- mergear y desplegar
Diferencias entre autoload y autoload-dev
Son dos secciones que puedes incluir en el fichero composer.json Ambas sirven para autocargar las clases de tu proyecto usando distintos namespaces. La diferencia básica es que autoload-dev solo autocargará esos ficheros para desarrollar, no para producción.
Esto puede ser útil para tener los tests con un namespace distinto y que las clases de tests sólo se carguen cuando sea necesario, haciendo más rápida la carga de clases en producción.
Para solo cargar las clases que estén dentro de en apartado autoload es necesario ejecutar composer con la opción -o
composer install -o composer update -o composer dump-autoload -o
Ejecutar un script con composer
Es posible que se ejecuten scripts desde composer con tan solo añadir la sección script al fichero composer.json y ejecutar el comando composer run-script <nombre-script>
{ "scripts": { "clear-cache": "rm -rf cache/*" } }
Conclusiones
Seguro que hay muchos más trucos y consejos de composer, te propongo que los añadas a los comentarios y los iré añadiendo al post.
Si quieres saber más y conocer todos los entresijos de composer échale un ojo a http://composer.json.jolicode.com/ y por supuesto a la documentación https://getcomposer.org/doc/