Aller au contenu principal

Composer

Composer est un outil de gestion de dépendances pour PHP.

Gestion de dépendances​

Lors de l'écriture d'une application, nous aurons souvent besoin de nombreuses fonctionnalités annexes à la réalisation du produit final.

Ainsi, je veux peut-être envoyer des emails, mais je n'ai pas envie de passer 25 jours à la réalisation d'un module complet d'envoi d'email, ou encore je veux faire de l'upload d'images pour mes utilisateurs, mais je ne veux pas chercher, concevoir et réaliser pendant des dizaines de jours un module de manipulation d'images. Les exemples sont nombreux.

On peut alors s'appuyer sur des modules réalisés par la communauté, et les intégrer à notre application pour les utiliser.

Le même principe existe dans de nombreux langages : Java, C#, Rust, Javascript...

La grande majorité de ces modules, comme des packages applicatifs, sont réalisés et maintenus par des communautés de développeurs open-source, sur des plateformes comme Github, Gitlab ou Bitbucket.

Ils sont ensuite publiés sur des repositories, des dépôts recensant tous les packages existants pour un langage donné.

Pour Composer, on trouvera ces packages sur le site Packagist. C'est le dépôt principal de packages pour PHP.

Installation​

Tout système​

Pour télécharger et installer Composer, une page dédiée est disponible sur le site. L'installation via la ligne de commande est très rapide et facile à utiliser.

astuce

Pour aller encore plus vite et pouvoir utiliser Composer depuis n'importe quel endroit sur sa machine (en ligne de commande), on pourra utiliser les options --install-dir pour viser directement un répertoire inclus dans le PATH et --filename pour nommer l'exécutable composer directement, plutôt que composer.phar :

php composer-setup.php --install-dir /usr/bin --filename composer

Windows​

Pour Windows, on a même un installeur exécutable disponible sur le site.

Prise en main​

Une fois installé, on peut utiliser Composer avec la ligne de commande en entrant composer [options] [arguments].

Par exemple, pour vérifier l'installation, on peut tout à fait utiliser la commande suivante pour afficher la version :

composer --version

On pourra également exécuter composer list ou bien composer tout court pour afficher toutes les commandes disponibles.

Mise à jour​

Pour mettre à jour l'outil Composer lui-même, la commande est également très simple :

composer self-update

Un projet avec Composer​

Plus qu'un gestionnaire de dépendances​

Nous allons à présent initialiser un projet avec Composer, et voir qu'il s'agit de plus qu'un gestionnaire de dépendances.

Si on se place dans un dossier vide et qu'on tape la commande composer init, alors un assistant commence à nous poser des questions :

Nom du package​

Le format demandé pour le nom du package est <vendor>/<name>.

  • La partie vendor correspond à l'éditeur, ou l'organisation qui réalise le package. Par exemple, on trouvera des dizaines et des dizaines de packages avec symfony comme vendor, car avant d'être un framework, Symfony est avant tout un ensemble de composants PHP réutilisables...
  • Le name, quant à lui, est beaucoup plus rapide à comprendre : c'est le nom de notre package

Par exemple, moi je vais mettre ld-web/php.

remarque

On pourra retrouver le même type de fonctionnement dans d'autres gestionnaires, par exemple dans NPM, à la différence que le vendor est préfixé par @ (@angular/cli par exemple)

Autres informations​

Les autres informations sont moins importantes, surtout si on ne souhaite pas réaliser et publier un package.

En revanche, on répondra "no" à la question "Voulez-vous définir vos dépendances de manière interactive ?", et de même pour la question suivante ("dev dependencies").

On va ensuite passer la section suivante avec "n", puisqu'on va écrire nous-mêmes notre configuration d'autoload PSR-4.

Finalement, on se retrouve avec un fichier basique, au format json, décrivant la configuration de notre projet.

Nous pouvons confirmer la génération.

Le fichier composer.json​

Finalement, l'outil nous a permis de générer un fichier de configuration décrivant notre projet.

Au-delà des dépendances, on y trouvera le nom, la description, l'auteur, la licence, et d'autres informations autres que les dépendances du projet.

Ajout d'une dépendance​

Supposons, pour le moment de manière fictive, que nous souhaitions envoyer des emails avec notre application.

Nous pouvons rechercher sur le site packagist.org le mot-clé mail, qui nous donne en retour la liste de potentiels packages correspondant à notre recherche.

On voit que le premier résultat, swiftmailer/swiftmailer, a été abandonné, au profit du package symfony/mailer. Ceci peut nous donner une bonne indication pour installer un package qui est encore maintenu et mis à jour régulièrement.

Pour installer la dépendance, nous allons utiliser la commande suivante :

composer require symfony/mailer

Composer va alors rechercher sur le dépôt de packages le composant correspondant, et l'installe.

Il va également procéder à tout un tas de modifications au sein de notre projet.

la section require​

En premier lieu, dans le fichier composer.json, on remarque que la section require, qui était vide, contient à présent une entrée :

{
"require": {
"symfony/mailer": "^6.2"
}
}

Composer a enregistré le composant symfony/mailer en tant que dépendance de notre projet, et a indiqué une version correspondante. En réalité, il s'agit d'un intervalle acceptable de versions installables au sein de notre projet, mais nous allons détailler ça un peu plus loin. Pour le moment, nous analysons ce qui a changé dans l'arborescence de notre projet.

remarque

La version peut varier suivant la date d'installation du package

le dossier vendor​

Par ailleurs, Composer a créé un nouveau dossier vendor à la racine de notre projet.

Si on le déplie, on remarque que beaucoup de dossiers ont été créés. On peut alors facilement naviguer dans le dossier symfony, puis mailer pour trouver, en réalité, le code du composant symfony/mailer.

Mais à quoi servent tous ces autres dossiers dans le dossier vendor ?

Nous voyons, dans le dossier mailer, qu'il y a également un fichier composer.json. Ce fichier contient les dépendances dont le composant Mailer a besoin pour fonctionner. Voilà pourquoi Composer a également téléchargé et installé ces autres composants

Prenons par exemple le composant psr/container, donc dans le dossier vendor/psr/container.

Si nous voulons savoir pourquoi il est installé, nous pouvons le demander à Composer :

composer depends psr/container

Composer nous indique que le composant symfony/service-contracts requiert la présence du composant psr/container.

Mais d'où vient symfony/service-contracts ?

composer depends symfony/service-contracts

Composer nous répond que symfony/mailer a besoin de symfony/service-contracts.

Arbre de dépendances

C'est ainsi toute une arborescence de dépendances qui a été installée par Composer, et ce à l'installation d'un package seulement.

Toutes les dépendances du projet se trouveront donc dans le dossier vendor

Le fichier composer.lock​

Enfin, Composer a également créé un fichier composer.lock, également à la racine du projet.

Si on l'ouvre, on voit alors un fichier de plusieurs centaines de lignes, au format json. Il est bien indiqué que ce fichier a été créé automatiquement.

On remarque une section packages. Cette section contient les versions précises des packages installés dans le dossier vendor.

Si on recherche avec Ctrl+F le composant symfony/mailer, on pourra facilement trouver sa version (6.2.5).

Mais dans le fichier composer.json, la version indiquée est ^6.2. Parlons à présent des versions des composants.

Le versioning sémantique (Semver)​

Le versioning sémantique (ou semver) est divisé en 3 parties X.Y.Z :

  • X : Version majeure (Cette version introduit des changements significatifs par rapport à la version précédente, certaines fonctionnalités qui précédaient sont supprimées par exemple)
  • Y : Version mineure (On peut introduire de nouvelles choses dans une version mineure par exemple, en s'assurant que tout ce qui existe déjà continue de fonctionner)
  • Z : Version de patch (Correctifs de bugs et sécurité)

Cela permet aux équipes qui maintiennent des packages d'effectuer des montées de versions graduelles, et d'organiser leurs sorties. On se doute qu'un patch de sécurité provoquera probablement un passage de 6.2.5 à 6.2.6 tandis que l'introduction de toutes nouvelles classes avec de nouvelles fonctionnalités, ou encore une réécriture de code existant qui changerait la manière de l'utiliser, provoquerait un passage de 6.2.5 à 7.0.0.

Les contraintes de version dans Composer​

Avec le versioning sémantique se pose alors la question de comment gérer toutes ces versions. C'est exactement le rôle d'un gestionnaire de dépendances.

composer.json​

Dans le fichier composer.json, Composer va indiquer, pour chaque package dont notre projet dépend, une contrainte de version, ou encore un intervalle de versions acceptables.

Pour ce faire, il va utiliser une syntaxe particulière avec des opérateurs qui ont un sens. Prenons encore une fois notre symfony/mailer :

ContrainteIntervalleExplication
6.2.1= 6.2.1Je veux précisément la version 6.2.1, et aucune autre
^6.2>= 6.2.0, < 7.0.0Je reste ouvert aux évolutions du package car l'intervalle signifie que je peux accueillir plusieurs versions différentes. Bien veiller aux signes >= et <, ici on indique bien qu'on exclut la version majeure 7.0.0 car c'est strictement inférieur
6.2.*>= 6.2.0, < 6.3.0Dans ce cas, je suis ouvert aux mises à jour, mais seulement pour les versions mineures en-dessous de la version 6.2
~6.2>= 6.2.0, < 7.0.0Avec ~, j'autorise la dernière partie spécifiée à augmenter (ici version mineure). Donc quelque soit la version mineure, j'autorise les mises à jour
~6.2.0>= 6.2.0, < 6.3.0De même, avec ~, j'autorise la dernière partie spécifiée à augmenter (ici version de patch)
**Toutes les versions sont acceptables

composer.lock​

Étant donné que le fichier composer.json renseigne des intervalles de versions, il faut un moyen pour Composer de retenir quelle version est installée dans un projet.

On pourrait penser que la version du composant peut se trouver dans le fichier composer.json du composant, mais il n'en est rien (vérifier dans le fichier composer.json du composant symfony/mailer, aucune version de ce composant à l'horizon).

Ainsi, ça peut devenir compliqué, si je souhaite travailler en équipe, d'avoir les mêmes versions des packages entre différents collègues. De même, si un nouveau contributeur rejoint l'équipe, il serait long et pénible de lui envoyer tout le dossier vendor.

À la place, Composer crée automatiquement et maintient un fichier composer.lock, qui contient la version précise installée dans notre projet. Pour aller plus loin même, la version est en fait associée à un commit sur Github, pour retrouver la version précise du point de vue de Git.

Versioning

Il est donc inutile d'inclure le dossier vendor au contrôle de version, puisqu'il pourra être reconstruit par Composer seulement avec le fichier composer.lock, beaucoup plus léger. Même s'il est long et complexe, nous n'avons pas à nous en occuper, puisque c'est Composer qui le gère.

Ce sont donc les fichiers composer.json et composer.lock qui seront versionnés, pas le dossier vendor

Installation et mises à jour de dépendances​

Composer nous offre des commandes pour installer, mettre à jour et supprimer des dépendances.

composer install​

Quand on récupère un projet existant, le dossier vendor n'existe pas, c'est normal. On ne trouvera que les fichiers composer.json et composer.lock.

On peut alors exécuter la commande suivante :

composer install
astuce

Avec composer install, Composer va directement voir le fichier composer.lock s'il existe. C'est ce qui lui permettra de rétablir précisément les versions que le développeur qui a committé a installées

composer require​

Comme vu précédemment, permet d'ajouter une nouvelle dépendance au projet.

Version

On peut bien sûr indiquer à Composer la contrainte de version qu'on souhaite :

composer require "symfony/mailer:6.1.*"
require et install

Attention à la différence entre require (ajouter une nouvelle dépendance) et install (télécharger et installer les dépendances à l'aide des fichiers composer) !

composer update​

Comme son nom l'indique, permettra de mettre à jour les dépendances, tout en respectant les intervalles de versions indiqués dans composer.json pour chaque dépendance.

On peut évidemment restreindre les packages qu'on souhaite mettre à jour, en ajoutant le nom derrière :

composer update symfony/mailer
Alias

Il existe un alias composer upgrade qui fait exactement la même chose

composer remove​

Pour supprimer un package (renseigner son nom derrière remove bien sûr).

Les dépendances de développement​

Dans un fichier composer.json on pourra égalemen trouver une section require-dev.

Cette section recense les dépendances de développement, c'est-à-dire tout ce dont nous aurons besoin lors de la phase de développement, mais pas en production par exemple (lorsque le site est déployé, en ligne).

Un exemple type est PHPUnit, qui est le framework de tests automatisés le plus utilisé dans l'écosystème PHP.

On aura typiquement besoin de PHPUnit lors de la phase de développement ou de tests, mais une fois le code en production, ce n'est plus le moment d'exécuter les tests. En production par exemple, pour installer les dépendances, mais sans les dépendances de développement, on exécutera la commande suivante :

composer install --no-dev

Et pour installer une dépendance de développement :

composer require --dev phpunit/phpunit # ou n'importe quelle autre dépendance

Les scripts​

Les scripts servent à exécuter une commande au sein de notre projet, en passant par Composer.

Par exemple, on pourrait définir un script qui lance les tests PHPUnit, pour éviter d'avoir à réécrire la commande à chaque fois. On pourra alors nommer la commande puis l'exécuter en faisant composer [nom-du-script].

Par exemple, si je veux exécuter des tests dans un conteneur Docker :

composer.json

{
"scripts": {
"test-in-docker": "docker-compose exec php ./vendor/bin/simple-phpunit --testdox"
}
}

Puis, à la racine du projet :

composer test-in-docker

Autres​

On pourra trouver d'autres sections dans un fichier composer.json :

  • suggest : suggère à l'utilisateur l'installation d'autres packages avec lesquels le sien a une excellente compatibilité
  • config : quelques éléments de configuration Composer, comme trier les packages par ordre alphabétique, autoriser des plugins tiers...
  • extra : des informations personnalisés qui pourront être exploitées par des plugins
  • conflict : comme son nom l'indique, une liste de packages avec lesquels le package n'est pas compatible. L'installation sera alors refusée
  • autoload : les règles d'auto-chargement des classes au sein du projet
  • autoload-dev : des règles d'auto-chargement supplémentaires à appliquer dans un environnement de tests par exemple