Un environnement de dev identique pour toute l'équipe
Fini les "ça marche sur ma machine". Docker garantit que chaque développeur travaille dans un environnement identique, éliminant les problèmes de compatibilité.
Sans conteneurs, chaque poste de travail finit par avoir sa propre version de PHP, ses propres extensions, une version de MySQL différente de celle de la production et des réglages php.ini hérités d'anciens projets. Les bugs qui en découlent sont les plus coûteux à diagnostiquer, parce qu'ils ne se reproduisent pas ailleurs. Avec Docker, l'environnement est décrit dans des fichiers versionnés avec le code : mettre à jour PHP devient une modification relue en code review, et chaque membre de l'équipe la récupère avec un simple git pull.
Cet article construit une stack de développement PHP complète : PHP avec Xdebug, MySQL et un serveur mail de test, puis aborde le débogage, les performances des volumes sur macOS et Windows et les pièges classiques.
Stack de développement complète
services:
php:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/var/www/html
- composer-cache:/root/.composer
environment:
- APP_ENV=dev
- XDEBUG_MODE=debug
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: app
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
mailhog:
image: mailhog/mailhog
ports:
- "8025:8025"
volumes:
composer-cache:
mysql-data:
Détaillons les choix de ce fichier :
- Le code est monté (
.:/var/www/html) : toute modification dans l'IDE est immédiatement visible dans le conteneur, sans reconstruire l'image. - Le cache Composer vit dans un volume nommé : les paquets téléchargés sont conservés même si le conteneur est recréé.
- MySQL publie le port 3306 pour permettre à un client graphique de s'y connecter depuis le poste. Les données survivent aux redémarrages grâce au volume
mysql-data. Utilisez la même version majeure qu'en production. - MailHog intercepte tous les e-mails envoyés par l'application et les affiche sur
http://localhost:8025: aucun risque d'écrire à de vrais clients depuis un poste de développement. Dans un projet Symfony, pointezMAILER_DSNverssmtp://mailhog:1025. MailHog n'étant plus maintenu, Mailpit (imageaxllent/mailpit, mêmes ports) est aujourd'hui une alternative active et compatible.
Les mots de passe en clair sont acceptables ici parce que cet environnement ne quitte jamais le poste du développeur.
Le Dockerfile de développement
Le fichier Dockerfile.dev part de l'image officielle PHP et ajoute les extensions courantes, Composer et Xdebug. Il reste volontairement distinct du Dockerfile de production, qui ne doit jamais contenir Xdebug :
FROM php:8.3-fpm
RUN apt-get update \
&& apt-get install -y --no-install-recommends git unzip libicu-dev libzip-dev \
&& docker-php-ext-install intl pdo_mysql zip opcache \
&& pecl install xdebug \
&& docker-php-ext-enable xdebug \
&& rm -rf /var/lib/apt/lists/*
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
COPY docker/php/xdebug.ini /usr/local/etc/php/conf.d/zz-xdebug.ini
WORKDIR /var/www/html
La ligne COPY --from=composer:2 récupère le binaire Composer depuis l'image officielle, sans script d'installation. Le fichier de configuration Xdebug est préfixé par zz- pour être chargé après ceux générés par docker-php-ext-enable.
Configuration Xdebug
Le debugging est essentiel en développement. Voici la configuration Xdebug optimale :
[xdebug]
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9003
Chaque ligne a son importance. xdebug.mode=debug active le débogage pas à pas ; la variable d'environnement XDEBUG_MODE du fichier Compose a priorité sur cette valeur, ce qui permet de passer à XDEBUG_MODE=off sans reconstruire l'image lorsque vous n'en avez pas besoin. start_with_request=yes démarre une session à chaque requête ; si cela ralentit trop votre application, utilisez plutôt trigger avec une extension de navigateur qui ajoute le déclencheur à la demande. Le port 9003 est celui par défaut depuis Xdebug 3 (l'ancien 9000 entrait en conflit avec PHP-FPM).
Le nom host.docker.internal désigne la machine hôte, là où tourne l'IDE. Docker Desktop le fournit automatiquement sur macOS et Windows ; sur Linux, il faut le déclarer. Pour PhpStorm, ajoutez aussi un nom de serveur, qui sert à retrouver le mapping de chemins, y compris pour les commandes CLI :
services:
php:
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
- PHP_IDE_CONFIG=serverName=docker
Dans PhpStorm, créez ensuite un serveur nommé docker et associez la racine du projet à /var/www/html. Sans ce mapping, l'IDE reçoit bien la connexion mais ne s'arrête sur aucun point d'arrêt.
Hot reload et performances
Sur macOS et Windows, les volumes montés peuvent être lents. Utilisez des stratégies de synchronisation :
- Mutagen pour la synchronisation rapide des fichiers
- Exclure
vendor/etnode_modules/du montage - Utiliser des volumes nommés pour les dépendances
La lenteur vient du fait que Docker tourne dans une machine virtuelle sur ces systèmes : chaque accès à un fichier monté traverse la frontière entre l'hôte et la VM. Les versions récentes de Docker Desktop utilisent VirtioFS, nettement plus rapide qu'auparavant, mais un projet Symfony lit des milliers de fichiers dans vendor/ à chaque requête. Placer ces dossiers dans des volumes nommés les garde à l'intérieur de la VM :
services:
php:
volumes:
- .:/var/www/html
- vendor:/var/www/html/vendor
- composer-cache:/root/.composer
volumes:
vendor:
composer-cache:
Contrepartie : le dossier vendor/ n'est plus visible depuis l'hôte, et l'IDE perd l'autocomplétion des dépendances. Beaucoup d'équipes lancent donc aussi un composer install local pour l'IDE, ou utilisent l'interpréteur distant de PhpStorm. Le cache de Symfony (var/) peut être traité de la même manière. Sur Linux, les montages sont natifs et rapides : ces optimisations ne sont pas nécessaires.
Les commandes du quotidien
Toutes les commandes PHP s'exécutent dans le conteneur, jamais avec le PHP de l'hôte :
docker compose up -d --build
docker compose exec php composer install
docker compose exec php bin/console doctrine:migrations:migrate --no-interaction
docker compose exec php bin/console cache:clear
# Sur Linux, pour que les fichiers créés appartiennent à votre utilisateur
docker compose exec -u "$(id -u):$(id -g)" php composer require symfony/uid
Regroupez ces commandes dans un Makefile ou un script documenté dans le README : un nouveau développeur ne doit avoir à retenir qu'une ou deux commandes.
Pièges fréquents
- Fichiers appartenant à root sur Linux : les commandes lancées en root dans le conteneur créent des fichiers que l'utilisateur de l'hôte ne peut plus modifier. Exécutez-les avec votre UID, comme ci-dessus.
- Utiliser
localhostpour joindre la base : dans un conteneur,localhostdésigne le conteneur lui-même. L'hôte de la base est le nom du service, icimysql. - Xdebug laissé actif en permanence : il ralentit sensiblement chaque requête et chaque commande Composer. Désactivez-le quand vous ne déboguez pas.
- Environnement de dev trop éloigné de la production : même version de PHP, mêmes extensions, même moteur de base de données, sinon l'avantage de Docker disparaît.
Cette approche permet d'avoir un environnement de développement opérationnel en moins de 5 minutes pour tout nouveau développeur rejoignant l'équipe.