Aller au contenu principal

Composer : le manifeste du développeur moderne.

Composer, l'outil PHP essentiel pour gérer les dépendances. Comprenez son modèle mental : composer.json, composer.lock et le dossier vendor.

MAJ 9 min de lecture
Sommaire · 14

Ouvrez un vieux projet PHP d'avant 2012 et vous tombez parfois sur un dossier lib/ rempli de bibliothèques copiées à la main, des require_once éparpillés en haut de chaque fichier, et une question sans réponse : quelle version de quoi tourne vraiment ici ? Composer a rangé tout ça. Ce billet reprend Composer depuis le début, pour un développeur qui débute et veut un modèle mental solide plutôt qu'une liste de commandes à recopier.

Au programme : le modèle mental (deux fichiers, un dossier), les commandes require, install et update avec leurs effets exacts, la syntaxe des versions (^, ~), et l'hygiène qui évite les mauvaises surprises. À la fin, vous saurez lire un composer.json, savoir quoi committer, et pourquoi lancer composer update sans filet est une mauvaise idée.

Avant Composer

Avant, on téléchargeait une bibliothèque en ZIP, on la posait dans un dossier, on ajoutait un require_once 'lib/un-truc/src/Bidule.php';, et on recommençait pour chaque dépendance de la dépendance. Mettre à jour voulait dire re-télécharger à la main et croiser les doigts. Le fameux « ça marche sur ma machine » vient en droite ligne de cette époque : personne ne savait exactement quelles versions cohabitaient dans le projet.

Composer met fin au grenier. Il télécharge, range, calcule les compatibilités et fige le résultat. Le reste de ce billet explique comment.

Le modèle mental : deux fichiers, un dossier

Tout Composer tient dans trois objets. Les comprendre, c'est déjà l'essentiel du travail.

  • composer.json : le manifeste de votre projet. Il déclare ce que le projet est (nom, type, licence) et ce dont il a besoin (« PHP 8.5 ou plus, Symfony 8.1 ou plus »). C'est vous qui l'écrivez, à la main ou via des commandes.
  • composer.lock : le contrat. Il fige les versions exactes réellement installées à un instant T (« Symfony 8.1.1, ce commit précis, ce hash »). Composer le génère seul, vous n'y touchez jamais à la main.
  • vendor/ : l'entrepôt. Composer y télécharge le code de toutes les dépendances. Il se reconstruit tout seul à partir des deux fichiers, donc il ne se committe pas.

Un quatrième fichier mérite d'être cité, vendor/autoload.php, mais on y revient plus bas : c'est lui qui rend le code des dépendances utilisable sans le moindre require manuel.

Installer Composer

Composer est un exécutable PHP. On l'installe une fois, en global sur la machine, puis on vérifie que le terminal le trouve :

Terminal
composer --version

L'installeur officiel (voir les références en fin de billet) s'occupe du reste sur la plupart des systèmes. Point important pour un débutant : Composer est un outil posé sur votre machine, pas une dépendance du projet. On ne le réinstalle pas à chaque clone.

Les commandes qui comptent vraiment

Composer a des dizaines de commandes. Cinq couvrent le quotidien.

Pour ajouter une dépendance :

Terminal
composer require symfony/var-dumper

Cette commande met à jour composer.json, résout les versions compatibles, réécrit composer.lock et télécharge le code dans vendor/. Pour un outil utile seulement en développement, on ajoute --dev :

Terminal
composer require --dev phpunit/phpunit

Pour installer ce qui est déjà déclaré :

Terminal
composer install

Cette commande lit le composer.lock et reconstruit vendor/ à l'identique. C'est le geste d'un nouvel arrivant dans l'équipe, d'un serveur d'intégration continue, d'un déploiement. Le lock fait foi : aucune version n'est recalculée, l'environnement est le même pour tout le monde.

Pour mettre à jour :

Terminal
composer update

Cette commande recalcule les versions autorisées par composer.json, réécrit le lock et met à jour vendor/. Elle est puissante, et c'est précisément là qu'est le piège (section suivante).

Pour aller plus loin

Pour retirer une dépendance :

Terminal
composer remove symfony/var-dumper
Ce que touche chaque commande
Commande composer.json composer.lock vendor/
require foo/bar modifié réécrit installé
install lu fait foi reconstruit
update lu (contraintes) réécrit mis à jour
remove foo/bar modifié réécrit nettoyé

Un composer.json moderne, section par section

Le composer.json est le manifeste de votre application. Voici à quoi il ressemble sur un projet récent (PHP 8.5, Symfony 8.1) :

JSON
{
    "type": "project",
    "license": "MIT",
    "require": {
        "php": ">=8.5",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "doctrine/orm": "^3.5",
        "symfony/framework-bundle": "^8.1",
        "symfony/runtime": "^8.1",
        "symfony/flex": "^2.0"
    },
    "require-dev": {
        "phpstan/phpstan": "^2.1",
        "phpunit/phpunit": "^13.0",
        "rector/rector": "^2.0",
        "symfony/maker-bundle": "^1.50"
    },
    "config": {
        "allow-plugins": {
            "phpstan/extension-installer": true,
            "symfony/flex": true,
            "symfony/runtime": true
        },
        "sort-packages": true
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

require et require-dev

require liste ce dont l'application a besoin pour tourner : le moteur (php), des extensions (ext-ctype), et des bibliothèques. require-dev liste ce qui ne sert qu'au développement : analyse statique (PHPStan), tests (PHPUnit), génération de code (Maker). La distinction compte au déploiement, on y revient avec --no-dev.

Sur le même sujet

autoload et PSR-4

C'est ici que vendor/autoload.php prend son sens. La section autoload mappe un préfixe de namespace vers un dossier. Ici, tout ce qui commence par App\ se trouve sous src/. Concrètement, la classe App\Service\Newsletter vit dans src/Service/Newsletter.php. Une seule ligne suffit ensuite dans votre code :

PHP
require 'vendor/autoload.php';

Fini les require manuels fichier par fichier. Composer génère un chargeur qui suit le standard PSR-4 et trouve chaque classe à la demande, la vôtre comme celles de vos milliers de dépendances.

config et scripts

config règle le comportement de Composer (tri des paquets, plugins autorisés). La section scripts, absente de cet exemple minimal, permet d'automatiser des tâches (vider un cache, préparer une base) déclenchées après un install ou un update.

Les versions : ^, ~ et les autres

Dans "symfony/framework-bundle": "^8.1", ce ^ n'est pas décoratif. Il dit à Composer jusqu'où il a le droit de monter tout seul. Pour le comprendre, il faut le versioning sémantique (SemVer), qui découpe une version en MAJEUR.MINEUR.PATCH. La promesse des mainteneurs : un changement cassant fait monter le chiffre MAJEUR, une fonctionnalité ajoutée le MINEUR, un correctif le PATCH.

  • ^8.1 (caret) autorise tout jusqu'à la majeure suivante exclue, soit >=8.1.0 et <9.0.0. C'est le réglage par défaut, et il suffit dans la plupart des cas.
  • ~8.1.2 (tilde) est plus strict : >=8.1.2 et <8.2.0, donc uniquement les correctifs. ~8.1 autorise lui les montées mineures, comme le caret.
  • 8.1.1 fige une version précise. À réserver aux cas où une version donnée casse le reste.
  • >=8.1 <9.0 décrit un intervalle explicite, utile quand ^ et ~ ne suffisent pas.
Les contraintes de version les plus courantes
Contrainte Autorise Bloque
^8.1 8.1.0 à 8.9.x 9.0.0
~8.1.2 8.1.2 à 8.1.x 8.2.0
~8.1 8.1.0 à 8.9.x 9.0.0
8.1.1 seulement 8.1.1 tout le reste

Tout ce système repose sur une promesse : que les mainteneurs ne cassent rien en mineur ni en patch. Quand la promesse n'est pas tenue, le composer.lock versionné est votre filet. Il rejoue les versions exactes qui, elles, fonctionnaient.

install contre update : le piège qui peut vite être fatal

La confusion entre install et update est l'erreur numéro un des débuts. Les deux touchent vendor/, mais pas de la même façon.

install applique le lock tel quel : reproductible, sans surprise. update ignore le lock, recalcule les versions et le réécrit. Vous pouvez vous coucher avec une prod verte et vous réveiller avec trois dépendances passées à une version majeure que personne n'a testée.

La règle tient en une phrase : sur un serveur, toujours install. Le update se fait sur une machine de développement, à froid, avec une suite de tests derrière. Et quand on met à jour, on cible :

Terminal
# On ne met à jour qu'un paquet, pas tout l'univers
composer update symfony/console

# Répétition générale : montre ce qui bougerait, ne touche à rien
composer update --dry-run

Déployer proprement : --no-dev

Au moment du déploiement, on ne veut ni PHPUnit ni Rector sur le serveur. La commande de production le dit :

Terminal
composer install --no-dev --optimize-autoloader

--no-dev saute tout ce qui est dans require-dev. --optimize-autoloader précalcule la correspondance classe vers fichier, ce qui accélère le chargement en production. Résultat : une installation plus légère et plus rapide.

L'hygiène : outdated, audit, bump

Composer ne sert pas qu'à installer. Depuis la version 2.4, il aide aussi un projet à vieillir proprement.

Terminal
# Qui est en retard ? (dépendances directes uniquement)
composer outdated --direct

# Des failles de sécurité connues dans mes versions installées ?
composer audit

# Aligner les contraintes du json sur ce qui est réellement installé
composer bump

outdated liste ce qui a pris du retard. audit confronte votre composer.lock aux avis de sécurité publiés et sort en erreur si une dépendance vulnérable est installée. bump resserre les contraintes du composer.json sur les versions en place, une fois la mise à jour digérée.

Le fichier qui dit ce qu'un projet vaut

Composer va bien plus loin : dépôts privés, dépôts path et vcs, plugins, scripts d'événements avancés, gestion de monorepo, résolution multi-plateforme. Chacun mérite son propre billet. Ici, on est resté sur ce qui fait tenir un projet debout au quotidien, celui d'un développeur qui débute et veut des bases saines.

composer.json porte bien son surnom de manifeste. En une trentaine de lignes, il déclare ce qu'un projet est, ce dont il dépend, et jusqu'où il s'autorise à bouger. Ce petit fichier a fait plus pour la fiabilité de PHP que bien des débats de syntaxe : il a rendu un projet reproductible, d'une machine à l'autre, d'une année à l'autre.

La prochaine fois que vous clonez un dépôt, ouvrez son composer.json avant le code. Il vous dira, en un coup d'œil, si le projet a été soigné ou laissé au grenier. Le vôtre raconte quoi ?

Vous avez suivi ?

Sur un serveur de production, quelle commande installe les dépendances ?

Voir la réponse

Bonne réponse : composer install

install applique le composer.lock à l'identique, donc de façon reproductible. update recalcule les versions et peut introduire du code que personne n'a testé.
Que signifie la contrainte ^8.1 dans un composer.json ?

Voir la réponse

Bonne réponse : Toute version de 8.1.0 à la série 8.x, mais pas la 9.0.0

Le caret autorise les montées mineures et les correctifs jusqu'à la majeure suivante exclue, soit >=8.1.0 et <9.0.0.
Quels fichiers faut-il versionner dans Git ?

Voir la réponse

Bonne réponse : composer.json et composer.lock

vendor/ se reconstruit avec composer install. On committe le manifeste et le contrat, pas l'entrepôt.

Une coquille, une erreur dans ce billet ? Signale-la-moi.

Ce billet est publié sous licence Creative Commons BY-NC-SA 4.0 (attribution, pas d'usage commercial, partage dans les mêmes conditions).

Vous aimerez aussi

Activez uniquement ce que vous souhaitez. Vos choix sont conservés 6 mois.

Strictement nécessaires

Indispensables au fonctionnement du site (session, sécurité, préférence d'affichage). Aucune donnée n'est partagée à des tiers et aucun consentement n'est requis.

Toujours actif

Mesure d'audience

Statistiques via Google Analytics (GA4) : pages vues, source du trafic, navigateur et interactions clés. Dépose des cookies de mesure, activés seulement avec votre accord (Consent Mode). Sans publicité ciblée, sans Google Signals, sans partage commercial.

Contenus externes

Affiche les GIF animés hébergés par Giphy (CDN aux États-Unis). À l'affichage d'un GIF, votre adresse IP et votre navigateur sont transmis à Giphy. Sans votre accord, les GIF ne s'affichent pas.