Aller au contenu principal

Développement PHP dans un devcontainer avec des outils de qualité de code préinstallés

Arrêtez de vous battre avec les problèmes de qualité de code. Mettez en place un environnement de développement PHP cohérent instantanément ! Ce guide pas à pas vous montre comment configurer un devcontainer VSCode avec des outils de qualité et d'analyse préinstallés.

· 23 minutes de lecture
Voir la source
Christophe
Amoureux de Markdown, WSL et Docker ~ Curieux insatiable.
Cet article existe aussi en anglais — lire la version originale.

Traduction automatique

Cette traduction a été produite par une IA. Elle peut contenir des contresens ou des maladresses, en particulier sur les termes techniques. En cas de doute, consultez l'article de référence en anglais — c'est lui qui fait foi.

Journal des modifications

Dernière
PHP_CodeSniffer moved from squizlabs to PHPCSStandards/PHP_CodeSniffer (Dec 2023); PHP-CS-Fixer is now v3.89.2 — Dockerfile example pins v3.46.0, update to current release
Install Rector automatically

Développement PHP dans un devcontainer avec des outils de qualité de code préinstallés

⚡
TL;DR

Ce long article pas à pas construit un devcontainer VSCode pour PHP avec un ensemble complet d'outils de qualité de code préinstallés : PHP-CS-Fixer et PHPCBF/PHPCS pour le formatage automatique à la sauvegarde, SonarLint et Intelephense pour la détection de bugs en temps réel, et Rector pour la mise à niveau automatisée du code et les suggestions de refactoring — tout est configuré une fois dans devcontainer.json et Dockerfile, pour que chaque membre de l'équipe obtienne un environnement identique.

Imaginons une des situations suivantes :

  1. Vous travaillez à plusieurs sur le même projet PHP et vous remarquez qu'une personne ou une autre ne respecte pas vos critères de qualité concernant la mise en forme du code. Vous aimez utiliser quatre espaces pour l'indentation, vous voulez que l'accolade qui marque le début d'une fonction soit sur la ligne, vous ne voulez plus voir d'espaces inutiles en fin de ligne, ... et zut ! vous constatez que certains s'en fichent complètement.

  2. Vous travaillez seul sur un projet et vous ne voulez pas perdre de temps à configurer votre VSCode. Vous souhaitez démarrer très vite avec beaucoup d'outils déjà installés dans VSCode, pour vous concentrer sur le code, pas sur l'éditeur.

La solution ultime : utiliser un devcontainer dans VSCode.

En utilisant un devcontainer, vous (et vos collègues) utiliserez un environnement préinstallé et tout le monde aura exactement le même. Vous gagnerez beaucoup de temps en évitant de configurer votre système, et vous pourrez coder tout de suite, épaulé par toute une série d'outils d'analyse de qualité.

Ce qu'un devcontainer fait pour vous​

Voici un fichier PHP tel que je l'ai enregistré, avec mes pires habitudes bien visibles — indentation aléatoire, espaces en fin de ligne (affichés en points), accolades posées n'importe où :

Index php avec du code mal écrit

Maintenant le même fichier, après un simple CTRL+S dans le devcontainer. Je n'ai rien changé ; j'ai juste enregistré :

Votre script a été correctement formaté cette fois

Rien n'a été installé sur ma machine pour obtenir ça — ni PHP, ni Composer, ni binaire de formatage. Et le collègue qui clone le repository obtient exactement le même comportement, sans qu'on lui demande de configurer quoi que ce soit.

Pourquoi ça fonctionne​

  • L'image Docker embarque les outils. PHP-CS-Fixer, PHPCS/PHPCBF et Composer sont téléchargés lors du build de l'image, donc ils existent dans le container et nulle part ailleurs.
  • devcontainer.json embarque l'éditeur. Il liste les extensions VSCode à installer et les réglages à appliquer — y compris « formate ce fichier à la sauvegarde, avec ce binaire, en utilisant ce fichier de configuration ».
  • VSCode s'attache au container. Votre éditeur continue de tourner sur votre machine, mais tout ce qu'il exécute se passe dans le container. C'est pour ça qu'un chemin comme /usr/local/bin/php-cs-fixer.phar est parfaitement valide même s'il n'existe pas sur votre disque.

Vous voulez juste l'environnement prêt à l'emploi ?

Alors sautez tout ce tutoriel et allez voir Installer un environnement PHP Docker en quelques secondes : même résultat, trois commandes, aucune explication.

Récupérer les fichiers​

Cet article est écrit sous forme de tutoriel pas à pas. Si vous ne voulez pas prendre le temps de créer les fichiers de configuration vous-même et préférez les télécharger directement, lancez les commandes ci-dessous dans une console Linux. Vous obtiendrez tous les fichiers et, chaque fois que le tutoriel ci-dessous vous demandera de créer un fichier, vous l'aurez déjà.

>user@machine: ~/yourproject

Télécharger la toute dernière version

Le repository php_devcontainer va évoluer avec le temps. Si vous souhaitez télécharger la dernière version et non celle figée pour cet article, utilisez les commandes suivantes et non celles ci-dessus :

>user@machine: ~/yourproject

1. Créer le projet​

Démarrons tout de suite un nouveau projet en créant un répertoire temporaire avec mkdir /tmp/devcontainer_php && cd $_.

Comme nous allons créer quelques fichiers, lancez code . pour démarrer Visual Studio Code (alias VSCode).

Créez un nouveau fichier appelé index.php avec le code PHP ci-dessous :

<?php
function sayHello( ) {
$text="Hello World!";
return $text;
}
echo sayHello();

Ce snippet définit une fonction simple appelée sayHello qui retourne la chaîne de caractères "Hello World!", puis affiche le résultat de cette fonction à l'écran.

C'est le fichier montré au début de cet article, dans son état avant : VSCode l'a enregistré exactement tel quel, avec une mise en forme tout simplement dégueulasse, espaces superflus compris.

On peut exécuter ce script dans un navigateur avec docker run -d -p 80:80 -u $(id -u):$(id -g) -v .:/var/www/html php:8.2-apache (lisez l'article La façon la plus simple de lancer un script / site PHP si une remise à niveau est nécessaire).

Comme prévu, le script tourne très bien :

Exécution de la page index dans un navigateur

Arrêtez de lire ici si ...

... vous êtes du genre à penser Le script fonctionne, non ? Alors pourquoi tout ce bruit ?, éteignez votre ordinateur et promettez de ne plus jamais toucher une seule ligne de code. Même si le code fonctionne ... il pue.

Dans cet article, nous allons apprendre à configurer Visual Studio Code pour qu'il arrive avec des extensions préinstallées et configurées pour la version de PHP que vous ciblez (ce sera 8.2 mais vous pouvez changer ça très facilement).

La technique que nous allons utiliser s'appelle devcontainer.

Un devcontainer est un environnement de développement isolé et préconfiguré qui tourne dans un container Docker, offrant les mêmes outils et réglages sur différentes machines.

2. Installer d'abord l'extension VSCode ms-azuretools.vscode-docker​

Dans VSCode, appuyez sur CTRL+SHIFT+X pour ouvrir le panneau Extensions et recherchez l'extension Docker de Microsoft appelée ms-azuretools.vscode-docker ; si vous ne l'avez pas encore, installez-la et activez-la.

3. Créer le fichier devcontainer.json​

L'étape suivante consiste à créer un dossier appelé .devcontainer et, dedans, un fichier appelé devcontainer.json avec le contenu ci-dessous.

Dans la suite de cet article, nous reviendrons sur ce fichier.

{
"name": "php_devcontainer",
"build": {
"dockerfile": "Dockerfile",
"args": {
"PHP_VERSION": "8.2",
"COMPOSER_VERSION": "2.5.5",
"PHPCSFIXER_INSTALL": "true",
"PHPCSFIXER_VERSION": "3.46.0",
"PHPCBF_INSTALL": "true",
"PHPCBF_VERSION": "3.7.2"
}
},
"workspaceMount": "source=${localWorkspaceFolder},target=/var/www/html,type=bind",
"workspaceFolder": "/var/www/html",
"remoteUser": "docker",
"customizations": {
"vscode": {
"settings": {
"[php]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "junstyle.php-cs-fixer"
},
"editor.codeActionsOnSave": {
"source.fixAll": true
},
"editor.formatOnSave": true,
"editor.renderWhitespace": "all",
"files.autoSave": "onFocusChange",
"files.eol": "\n",
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"intelephense.environment.phpVersion": "8.2",
"intelephense.telemetry.enabled": false,
"php-cs-fixer.config": "/var/www/html/.config/.php-cs-fixer.php",
"php-cs-fixer.executablePath": "/usr/local/bin/php-cs-fixer.phar",
"php-cs-fixer.onsave": true,
"php-cs-fixer.rules": "@PSR12",
"php.validate.executablePath": "/usr/local/bin/php",
"phpsab.executablePathCBF": "/usr/local/bin/phpcbf.phar",
"phpsab.executablePathCS": "/usr/local/bin/phpcs.phar",
"phpsab.fixerEnable": true,
"phpsab.snifferShowSources": true,
"phpsab.standard": "/var/www/html/.config/phpcs.xml",
"telemetry.telemetryLevel": "off",
"terminal.integrated.profiles.linux": {
"bash": {
"path": "/bin/bash",
"icon": "terminal-bash"
}
},
"terminal.integrated.defaultProfile.linux": "bash"
},
"extensions": [
"bmewburn.vscode-intelephense-client",
"junstyle.php-cs-fixer",
"ms-azuretools.vscode-docker",
"ValeryanM.vscode-phpsab",
"zobo.php-intellisense"
]
}
},
"postCreateCommand": "composer require rector/rector --dev"
}

Ce fichier configure un conteneur de développement Docker pour PHP 8.2 avec des outils de formatage et d'analyse de code. Il monte le dossier de travail local dans le conteneur, configure VS Code avec des extensions PHP et des règles de formatage automatique (PHP CS Fixer, PHPCS), puis installe Rector après la création du conteneur.

4. Créer le fichier Dockerfile​

Continuons et créons un second fichier appelé Dockerfile avec ce contenu :

ARG PHP_VERSION=8.2
ARG COMPOSER_HOME="/var/cache/composer"
ARG COMPOSER_VERSION=2.5.5
ARG PHPCSFIXER_INSTALL=true
ARG PHPCSFIXER_VERSION=3.46.0
ARG PHPCBF_INSTALL=true
ARG PHPCBF_VERSION=3.7.2
ARG TIMEZONE="Europe/Brussels"
ARG OS_USERID=1000
ARG OS_USERNAME="docker"
FROM composer:${COMPOSER_VERSION} AS composer_base
FROM php:${PHP_VERSION}-fpm
ARG TIMEZONE
ENV TZ=${TIMEZONE}
ARG PHPCSFIXER_INSTALL
ARG PHPCSFIXER_VERSION
RUN set -e -x \
&& if [ "$PHPCSFIXER_INSTALL" = "true" ] ; then \
printf "\e[0;105m%s\e[0;0m\n" "Install php-cs-fixer.phar ${PHPCSFIXER_VERSION}" \
&& curl -L https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/releases/download/v${PHPCSFIXER_VERSION}/php-cs-fixer.phar -o /usr/local/bin/php-cs-fixer.phar \
&& chmod +x /usr/local/bin/php-cs-fixer.phar ; \
fi
ARG PHPCBF_INSTALL
ARG PHPCBF_VERSION
RUN set -e -x \
&& if [ "$PHPCBF_INSTALL" = "true" ] ; then \
printf "\e[0;105m%s\e[0;0m\n" "Install phpcbf.phar ${PHPCBF_VERSION}" \
&& curl -L https://github.com/squizlabs/PHP_CodeSniffer/releases/download/${PHPCBF_VERSION}/phpcbf.phar -o /usr/local/bin/phpcbf.phar \
&& chmod +x /usr/local/bin/phpcbf.phar \
&& curl -L https://github.com/squizlabs/PHP_CodeSniffer/releases/download/${PHPCBF_VERSION}/phpcs.phar -o /usr/local/bin/phpcs.phar \
&& chmod +x /usr/local/bin/phpcs.phar ; \
fi
ARG COMPOSER_HOME
ENV COMPOSER_HOME=${COMPOSER_HOME}
RUN set -e -x \
&& mkdir -p "${COMPOSER_HOME}/cache/files" \
&& mkdir -p "${COMPOSER_HOME}/cache/vcs" \
&& chmod -R 777 "${COMPOSER_HOME}"
COPY --from=composer_base /usr/bin/composer /usr/bin/composer
ARG OS_USERID
ARG OS_USERNAME
RUN set -e -x \
&& useradd --password '' -G www-data,root -u ${OS_USERID} -l -d "/home/${OS_USERNAME}" "${OS_USERNAME}" \
&& mkdir -p "/home/${OS_USERNAME}" \
&& chown -R "${OS_USERNAME}":"${OS_USERNAME}" "/home/${OS_USERNAME}"
RUN set -e -x \
&& printf "\e[0;105m%s\e[0;0m\n" "Install Linux binaries" \
&& apt-get update -yqq \
&& apt-get install -y --no-install-recommends curl libzip-dev zip unzip \
&& docker-php-ext-install zip \
&& docker-php-source delete \
&& apt-get clean \
&& rm -rf /tmp/* /var/list/apt/*
WORKDIR /var/www/html

Ce Dockerfile construit une image PHP-FPM avec des outils de développement (php-cs-fixer, phpcbf, phpcs) et Composer. Il configure un utilisateur non-root avec des répertoires de travail, installe les dépendances système nécessaires, et prépare l'environnement pour exécuter des applications PHP dans un conteneur.

5. Réouvrir dans le container​

Nous avons donc maintenant trois fichiers : un index.php dégueulasse, .devcontainer/devcontainer.json et .devcontainer/Dockerfile.

Ce que nous allons faire maintenant, c'est sauter dans un container Docker. Regardez en bas à gauche de votre écran :

Exécution dans WSL

Vous verrez quelque chose comme, dans mon cas, WSL: Ubuntu-20.04. Cela peut être différent sur votre ordinateur selon votre système d'exploitation. Ce que VSCode dit ici, c'est je code sur ma machine.

Cliquez sur ce statut ou, autre possibilité, appuyez sur CTRL+SHIFT+P pour ouvrir la Command Palette puis cherchez Reopen in Container et appuyez sur Enter.

Reopen in Container

Visual Studio Code va faire beaucoup de choses et, la première fois, cela peut prendre quelques minutes.

Maintenant, vous êtes dans le container

6. Travailler dans le container​

Et voilà la magie : réouvrez le fichier index.php si mal formaté et enregistrez-le sans aucune modification. Appuyez juste sur CTRL+S et tadaaa 🎉🎉🎉 — vous obtenez le fichier reformaté montré au début de cet article.

Comment est-ce possible ?

C'est parce que nous avons appris à VSCode à utiliser un formateur spécifique pour nos fichiers PHP et que nous lui avons dit de formater le fichier à chaque sauvegarde. Et savez-vous où nous avons fait ça ? Dans nos fichiers .devcontainer/devcontainer.json et .devcontainer/Dockerfile, bien sûr !

C'est pour ça que nous travaillons dans un devcontainer ! La configuration est faite une fois pour toutes, il n'y a plus qu'à la réutiliser, quel que soit le projet PHP.

Les outils (prenez ceux dont vous avez besoin)​

Les cinq sections ci-dessous sont indépendantes les unes des autres : chacune couvre un outil déjà installé dans le container, ce qu'il détecte, et où il a été déclaré. Lisez celles qui vous intéressent et sautez le reste — l'environnement fonctionne de toute façon avec tous activés.

Le premier outil utilisé ici est PHP-CS-Fixer​

Quand nous enregistrons un fichier dans VSCode, nous avons configuré un formateur qui va adapter la mise en forme du fichier selon les règles que nous avons définies. Par exemple, le nombre d'espaces pour l'indentation ou le fait que les accolades doivent être alignées (et bien d'autres choses). Cet outil est PHP-CS-Fixer.

Mais voyons quand PHP-CS-Fixer a été installé...

Réouvrez votre fichier .devcontainer/devcontainer.json et regardez les lignes surlignées ci-dessous :

{
// ...
"build": {
"dockerfile": "Dockerfile"
},
// ...
"customizations": {
"vscode": {
"settings": {
// highlight-next-line
"[php]": {
// highlight-next-line
"editor.formatOnSave": true,
// highlight-next-line
"editor.defaultFormatter": "junstyle.php-cs-fixer"
},
// ...
// highlight-next-line
"files.autoSave": "onFocusChange",
// ...
// highlight-next-line
"php-cs-fixer.config": "/var/www/html/.config/.php-cs-fixer.php",
// highlight-next-line
"php-cs-fixer.executablePath": "/usr/local/bin/php-cs-fixer.phar",
// highlight-next-line
"php-cs-fixer.onsave": true,
// highlight-next-line
"php-cs-fixer.rules": "@PSR12",
//...
},
"extensions": [
// ...
// highlight-next-line
"junstyle.php-cs-fixer",
]
}
}
}

Ce fichier de configuration définit l'environnement de développement pour un projet PHP en Docker. Il configure VS Code pour formater automatiquement le code PHP à la sauvegarde en utilisant php-cs-fixer selon les règles PSR12, et installe l'extension correspondante.

Nous demandons donc à VSCode de lancer, pour les fichiers PHP, un formatter quand le fichier est enregistré, et l'outil à utiliser pour le formatage est junstyle.php-cs-fixer. Regardez directement à la fin, dans la section extensions : nous demandons à VSCode d'installer cette extension.

Nous demandons aussi à VSCode d'enregistrer automatiquement le fichier dès que le focus change, par exemple si nous sélectionnons un autre fichier, si nous cliquons ailleurs (plus dans l'éditeur), ...

Enfin, junstyle.php-cs-fixer demande quelques réglages à initialiser comme par exemple php-cs-fixer.executablePath.

Là, vous pouvez vous dire : Attendez une minute, je n'ai pas le fichier /usr/local/bin/php-cs-fixer.phar sur mon ordinateur. Et vous avez probablement raison, mais réfléchissez : codez-vous sur votre ordinateur ou êtes-vous dans un container Docker ?

En exécutant VSCode dans un container, vous n'êtes plus sur votre machine.

Et maintenant, il faut regarder le troisième fichier que nous avons créé : Dockerfile.

Réouvrez le fichier .devcontainer/Dockerfile et regardez les lignes surlignées ci-dessous :

# highlight-next-line
ARG PHP_VERSION=8.2
# ...
# highlight-next-line
ARG PHPCSFIXER_INSTALL=true
# highlight-next-line
ARG PHPCSFIXER_VERSION=3.46.0
# ...
# highlight-next-line
FROM php:${PHP_VERSION}-fpm
# ...
# highlight-next-line
ARG PHPCSFIXER_INSTALL
# highlight-next-line
ARG PHPCSFIXER_VERSION
# highlight-next-line
RUN set -e -x \
# highlight-next-line
&& if [ "$PHPCSFIXER_INSTALL" = "true" ] ; then \
# highlight-next-line
printf "\e[0;105m%s\e[0;0m\n" "Install php-cs-fixer.phar ${PHPCSFIXER_VERSION}" \
# highlight-next-line
&& curl -L https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/releases/download/v${PHPCSFIXER_VERSION}/php-cs-fixer.phar -o /usr/local/bin/php-cs-fixer.phar \
# highlight-next-line
&& chmod +x /usr/local/bin/php-cs-fixer.phar ; \
# highlight-next-line
fi
# ...

Ce Dockerfile configure une image Docker basée sur PHP-FPM et installe optionnellement php-cs-fixer, un outil de formatage de code PHP. Les arguments permettent de contrôler la version de PHP et si php-cs-fixer doit être installé, avec la possibilité de passer ces valeurs au moment du build.

Ce fichier va demander à VSCode de télécharger une image Docker php:8.2-fpm et d'installer PHP-CS-Fixer (puisque PHPCSFIXER_INSTALL a été mis à true).

La version 3.46.0 de l'exécutable PHP-CS-Fixer sera téléchargée et stockée, dans l'image Docker, sous usr/local/bin/php-cs-fixer.phar. C'est pour ça que, dans notre devcontainer.json, nous pouvons écrire "php-cs-fixer.executablePath": "/usr/local/bin/php-cs-fixer.phar", : parce que nous avons déjà téléchargé et installé l'exécutable.

Besoin de vérifier ?

Toujours dans VSCode, ouvrez un terminal en appuyant sur CTRL+´ et, dans le prompt, tapez ls -l /usr/local/bin. Comme vous le voyez, php-cs-fixer.phar est bien là. Il a été téléchargé et installé par VSCode quand vous avez sauté dans le container.

Le binaire php-cs-fixer.phar est bien là

PHP-CS-FIXER - Fichier de configuration​

Le fichier .devcontainer/devcontainer.json contient aussi cette ligne : "php-cs-fixer.config": "/var/www/html/.config/.php-cs-fixer.php".

Nous pouvons donc avoir notre propre fichier de configuration pour PHP-CS-Fixer (avec nos propres règles) et ce fichier doit être créé dans un dossier appelé .config et nommé .php-cs-fixer.php.

Essayons, et cette fois nous allons nous assurer que tous nos fichiers PHP auront un bloc d'en-tête avec notre copyright, nos informations de licence ou autre.

Dans VSCode, créez le dossier .config et dedans le fichier .php-cs-fixer.php avec ce contenu :

<?php
$finder = PhpCsFixer\Finder::create()
->in('.')
->exclude([
'.config',
'.devcontainer',
'.git',
'node_modules',
'vendor'
]);
$header = file_get_contents(__DIR__ . '/licenseHeader.txt');
$config = new PhpCsFixer\Config();
return $config->setRules(
[
'@PSR12' => true,
'header_comment' => [
'header' => rtrim($header, "\r\n"),
'location' => 'after_declare_strict',
'comment_type' => 'PHPDoc',
],
]
)
->setCacheFile('/tmp/.php-cs-fixer.cache')
->setIndent(' ')
->setLineEnding("\n")
->setFinder($finder);

Ce fichier configure PHP CS Fixer, un outil qui formate automatiquement le code PHP selon des règles de style. Il crée un finder pour cibler tous les fichiers PHP du projet en excluant les dossiers inutiles, puis définit les règles de formatage incluant la norme PSR12 et l'ajout d'un en-tête de licence en haut de chaque fichier.

Créez aussi un second fichier appelé licenseHeader.txt avec le contenu que vous voulez, par exemple :

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>.

Ceci est une notice de licence GPL v3 standard qui permet la redistribution et la modification du logiciel sous certaines conditions. Elle précise que le logiciel est fourni sans garantie et renvoie vers les termes complets de la licence GNU General Public License.

Maintenant, réouvrez votre fichier index.php et appuyez simplement sur CTRL+S pour l'enregistrer à nouveau et tadaaa...

Un bloc de licence est automatiquement ajouté par php-cs-fixer

Comme vous le voyez, lors du formatage du fichier, PHP-CS-Fixer va désormais aussi injecter notre en-tête.

Beaucoup d'options sont expliquées ici

Rendez-vous sur https://mlocati.github.io/php-cs-fixer-configurator ou https://github.com/PHP-CS-Fixer/PHP-CS-Fixer pour en savoir plus sur les options du fichier de configuration de php-cs-fixer.

PHP-CS-FIXER - Corriger tous les fichiers d'un coup​

Un pas de plus : est-il possible de lancer PHP-CS-Fixer non seulement sur le fichier en cours d'édition dans VSCode mais sur tous les fichiers de votre repository ? Oui, bien sûr.

Dans VSCode, appuyez sur CTRL+´ (ou cliquez sur le menu View puis Terminal) pour ouvrir un terminal. Assurez-vous d'être dans le dossier /var/www/html (lancez cd /var/www/html si besoin) et exécutez la commande suivante :

>user@machine: ~/yourproject

Cela va lancer PHP-CS-Fixer sur tout votre code (.). Si vous voulez exclure certains dossiers, ouvrez le fichier .config/.php-cs-fixer.php et mettez à jour le tableau exclude.

Le deuxième jeu d'outils, ce sont PHPCBF et PHPCS​

Notre Dockerfile a été configuré pour télécharger et installer les outils PHP_CodeSniffer appelés phpcbf et phpcs.

phpcbf est un outil de Code beautifier : tout comme PHP-CS-Fixer, phpcbf va apporter quelques modifications mineures à vos fichiers selon les règles activées.

Par exemple, mettez à jour votre script index.php avec ce contenu :

<?php
function sayHello($firstname)
{
if($firstname == "") {
$text = "Hello World!";
}else{
$text = "Hello " . $firstname;
}
return $text;
}
echo sayHello();

Cette fonction sayHello prend un prénom en paramètre et retourne un message de salutation personnalisé. Si le prénom est vide, elle retourne « Hello World! », sinon elle retourne « Hello » suivi du prénom fourni. À la ligne 14, on appelle cette fonction sans argument, ce qui provoquera un avertissement puisque le paramètre $firstname ne reçoit rien.

Qu'est-ce qui ne va pas avec cette syntaxe ? Presque rien mais ... le standard (PSR12) demande de mettre un espace après le mot-clé if et un espace avant et après else.

PHP-CS-Fixer sera appelé quand vous enregistrerez le fichier

Vous avez remarqué ? Vous avez copié/collé le script ci-dessous où il n'y avait pas d'espaces avant et après le mot-clé else. Mais dès que vous enregistrez le fichier, boum, le fichier est refactoré par PHP-CS-Fixer et, entre autres, des espaces ont été ajoutés avant et après else mais pas pour if. C'est parce que, en réalité, il n'y a pas UN OUTIL MAGIQUE pour tout. Parfois PHP-CS-Fixer ne peut pas faire certaines modifications alors que PHPCBF oui, ou l'inverse.

Comme pour PHP-CS-Fixer, nous avons déjà installé PHPCBF et PHPCS dans notre container. Voyez votre fichier .devcontainer/devcontainer.json :

{
// ..
"customizations": {
"vscode": {
"settings": {
// ...
// highlight-next-line
"phpsab.executablePathCBF": "/usr/local/bin/phpcbf.phar",
// highlight-next-line
"phpsab.executablePathCS": "/usr/local/bin/phpcs.phar",
// highlight-next-line
"phpsab.fixerEnable": true,
// highlight-next-line
"phpsab.snifferShowSources": true,
// highlight-next-line
"phpsab.standard": "/var/www/html/.config/phpcs.xml",
// ...
},
"extensions": [
// ...
// highlight-next-line
"ValeryanM.vscode-phpsab",
// ...
]
}
}
}

Ce fichier de configuration JSON défini les paramètres VS Code pour l'extension phpsab, qui est un outil de vérification et de formatage de code PHP. Il spécifie les chemins vers les exécutables phpcbf et phpcs, active la correction automatique, configure l'affichage des sources, définit le standard de codage à utiliser et enregistre l'extension à installer.

Et les deux exécutables /usr/local/bin/phpcbf.phar et /usr/local/bin/phpcs.phar ont été téléchargés grâce à votre Dockerfile. Jetez-y un œil si besoin.

PHPCS et PHPCBF - Fichier de configuration​

Dans VSCode, créez dans le dossier .config un fichier appelé phpcs.xml avec ce contenu :

<?xml version="1.0"?>
<ruleset name="MyRules">
<arg name="colors" />
<arg name="extensions" value="php" />
<arg value="s" />
<arg value="p" />
<rule ref="PSR12" />
</ruleset>

Ce fichier configure un ensemble de règles de style de code PHP appelé MyRules. Il active l'affichage en couleur, définit l'extension de fichier à analyser comme PHP, configure le mode verbose et de parallélisation, puis applique l'ensemble de normes PSR12 pour vérifier la conformité du code.

Maintenant que nous avons défini notre standard de codage (PSR12 ici), affichez à nouveau le script index.php :

PHPCS dans VSCode

Ouvrez un terminal en appuyant sur CTRL+´ (ou en cliquant sur le menu View puis Terminal) et lancez cette commande :

>user@machine: ~/yourproject

Et vous obtiendrez cette sortie :

>user@machine: ~/yourproject
FILE: /var/www/html/index.php
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AND 1 WARNING AFFECTING 2 LINES
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1 | WARNING | [ ] A file should declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it should execute logic with side effects, but should not do
    |         |     both. The first symbol is defined on line 18 and the first side effect is on line 29. (PSR1.Files.SideEffects.FoundWithSymbols)
 20 | ERROR   | [x] Expected 1 space(s) after IF keyword; 0 found (Squiz.ControlStructures.ControlSignature.SpaceAfterKeyword)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Il y a un avertissement et une erreur concernant une violation de convention de codage et le plus sympa, c'est que phpcbf peut en corriger certaines.

Maintenant, lancez presque la même commande mais plus avec phpcs (pour détecter les violations), avec phpcbf (pour les corriger — celles qui peuvent l'être automatiquement).

>user@machine: ~/yourproject

Vous obtiendrez ceci :

>user@machine: ~/yourproject
PHPCBF RESULT SUMMARY
----------------------------------------------------------------------
FILE                                                  FIXED  REMAINING
----------------------------------------------------------------------
/var/www/html/index.php                               1      1
----------------------------------------------------------------------
A TOTAL OF 1 ERROR WERE FIXED IN 1 FILE
----------------------------------------------------------------------

Et effectivement, si vous regardez votre script PHP, il y a maintenant un espace après le mot-clé if.

PHPCS et PHPCBF - Tous les fichiers d'un coup​

Vous avez probablement déjà compris la syntaxe. Utilisez simplement le . pour scanner tout le code :

  1. Pour corriger les violations, lancez /usr/local/bin/phpcbf.phar --standard=/var/www/html/.config/phpcs.xml .
  2. Pour scanner les violations restantes, lancez /usr/local/bin/phpcs.phar --standard=/var/www/html/.config/phpcs.xml ..

Le troisième outil est SonarLint​

Ici aussi, SonarLint était déjà installé dans notre container Docker grâce à notre fichier .devcontainer/devcontainer.json :

{
// ...
"customizations": {
"vscode": {
"extensions": [
// ...
// highlight-next-line
"sonarsource.sonarlint-vscode",
// ...
]
}
}
}

Ce snippet configure les personnalisations pour l'environnement de développement VS Code en spécifiant une liste d'extensions à installer. Ici, on ajoute l'extension SonarLint de SonarSource, qui analyse le code pour détecter les bugs et problèmes de qualité directement dans l'éditeur.

Ce n'est qu'une extension VSCode

SonarLint n'a pas besoin d'outil externe ; c'est une extension VSCode autonome, donc rien à prévoir dans notre Dockerfile.

Réouvrez donc le fichier index.php que vous avez, et faites attention à la ligne echo sayHello() :

<?php
function sayHello($firstname)
{
if ($firstname == "") {
$text = "Hello World!";
} else {
$text = "Hello " . $firstname;
}
return $text;
}
echo sayHello();

Cette fonction accepte un prénom en paramètre et retourne un message d'accueil personnalisé. Si le prénom est vide, elle retourne « Hello World! », sinon elle retourne « Hello » suivi du prénom fourni. À la ligne 14, on appelle la fonction sans argument, ce qui génère un avertissement car le paramètre est obligatoire.

SonarLint voit quelque chose

Déplacez le curseur de la souris sur la variable sayHello() et obtenez des informations supplémentaires de SonarLint dans une popup. Vous pouvez aussi appuyer sur CTRL+SHIFT+M pour afficher le panneau Problems (ou menu View puis Problems) et obtenir la liste de tous les problèmes SonarLint rencontrés dans le fichier courant et, aussi, dans tout fichier que vous ouvrirez désormais.

SonarLint expliqué

Une solution serait de mettre à jour le prototype de sayHello comme ceci : function sayHello($firstname = "").

SonarLint évite les bugs !

Contrairement aux outils vus plus haut, il ne s'agit plus de formatage, mais d'alerter le développeur sur des bugs potentiels et des erreurs fatales pendant qu'il écrit son code.

Le quatrième outil est Intelephense​

À côté de SonarLint, il y a aussi Intelephense qui va lui aussi détecter un certain nombre de bugs potentiels.

Dans l'exemple précédent, quand le prototype était function sayHello($firstname), Intelephense alertait aussi le développeur sur le paramètre manquant.

<?php
function sayHello($firstname)
{
if($firstname == "") {
$text = "Hello World!";
} else {
$text = "Hello " . $firstname;
}
return $text;
}
echo sayHello();

Cette fonction sayHello prend un prénom en paramètre et retourne un message de salutation personnalisé. Si le prénom est vide, elle retourne « Hello World! », sinon elle retourne « Hello » suivi du prénom fourni. À la ligne 14, on appelle cette fonction mais sans passer d'argument, ce qui causera une erreur car le paramètre $firstname ne sera pas défini.

Intelephense en action

Nous avons besoin de plusieurs outils

Cet exemple illustre bien le point : à ce jour, en février 2024, il faut encore jongler avec plusieurs extensions et outils pour obtenir un code propre et sans bug.

Le meilleur pour la fin, le cinquième outil est Rector​

Rector est une application formidable pour scanner et mettre à niveau automatiquement votre code vers une version donnée de PHP, et cela signifie aussi inspecter votre façon de coder.

Peut-être utilisez-vous encore une manière de programmer digne de PHP 5.4 mais indigne d'un développeur moderne à jour sur les dernières évolutions.

Rector est mon coach personnel

J'ADORE vraiment Rector puisqu'il m'aide à apprendre de nouvelles fonctionnalités de PHP. Quand je le lance sur n'importe lequel de mes projets, je vois où je peux améliorer mon code en refactorant certaines parties et faire mieux.

J'ADORE VRAIMENT RECTOR.

Cette fois, il va falloir installer Rector pour notre projet et pour cela, nous allons suivre la documentation officielle : https://github.com/rectorphp/rector?tab=readme-ov-file#install mais ... c'est déjà fait dans notre fichier .devcontainer/devcontainer.json.

Si vous le réouvrez, regardez le nœud postCreateCommand :

{
// ...
"customizations": {
// ...
},
// highlight-next-line
"postCreateCommand": "composer require rector/rector --dev"
}

Ce snippet JSON configure un environnement de développement avec une commande exécutée automatiquement après création du conteneur. La ligne postCreateCommand installe Rector, un outil PHP qui refactorise automatiquement le code, en tant que dépendance de développement via composer.

Rector sera ajouté au fichier composer.json (et le fichier sera créé s'il n'existe pas encore).

`Composer` a été installé dans notre `Dockerfile`

Si vous pensez Oui, mais je n'ai pas installé composer..., eh bien c'est faux. Nous l'avons installé dans notre container. Revoyez votre .devcontainer/Dockerfile si besoin.

Rector - fichier de configuration​

Sautez ce chapitre si vous avez déjà téléchargé les fichiers de configuration

La deuxième étape du guide d'installation nous demande de lancer la commande ci-dessous. À l'invite, répondez yes pour créer votre fichier de configuration rector.php.

>user@machine: ~/yourproject

Enfin, ouvrez le fichier de configuration rector.php et mettez-le à jour comme ceci :

<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withSkip(
[
'.config', '.devcontainer', '.git', 'node_modules','vendor'
]
)
->withPhpSets(php82: true)
->withPreparedSets(codeQuality: true, deadCode: true, typeDeclarations: true);

Ce fichier configure Rector, un outil d'automatisation de refactoring PHP. Il active la vérification stricte des types, exclut certains répertoires de l'analyse, cible PHP 8.2, et active trois ensembles de transformations : amélioration de la qualité du code, suppression du code mort, et ajout de déclarations de type.

Dites à Rector quelle version de PHP vous utilisez

Rector va vous suggérer des changements mais pour cela, il doit savoir quelle version de PHP vous utilisez. En effet, Rector ne suggérera pas une syntaxe PHP 8.2 par exemple si vous tournez encore sur une version plus ancienne (voir PHP Version Features).

De mon côté, je préfère mettre tous les fichiers de configuration dans un dossier .config

Vous avez remarqué que Rector a créé son fichier de configuration à la racine de votre projet. Je préfère le déplacer dans le dossier .config.

Lancer Rector​

Pour illustrer ce que Rector peut faire, éditez le index.php avec ce contenu, une fois de plus :

<?php
function sayHello($firstname = "")
{
if($firstname == "") {
$text = "Hello World!";
} else {
$text = "Hello " . $firstname;
}
return $text;
}
echo sayHello();

Cette fonction accepte un prénom optionnel et retourne un message de salutation personnalisé. Si aucun prénom n'est fourni, elle retourne 'Hello World!', sinon elle retourne un message avec le prénom passé en paramètre. Le script appelle ensuite la fonction sans argument et affiche le résultat.

Dans un terminal, lancez vendor/bin/rector process index.php --dry-run --config .config/rector.php comme précédemment.

Rector simplifie notre fonction sayHello

Regardez l'image ci-dessus. En rouge, votre code actuel et, oui, on le savait, n'est-ce pas, la qualité du code est mauvaise.

En effet, un des concepts du clean code est d'éviter l'instruction conditionnelle if ... else .... Très probablement, nous n'avons pas besoin de else (voir https://phpmd.org/rules/cleancode.html#elseexpression).

Rector suggère de supprimer complètement l'expression if .. else ... et d'utiliser une expression ternaire. Et aussi, de retourner directement la valeur au lieu d'utiliser une variable $text inutile.

Et c'est correct, nous pouvons écrire return $firstname == "" ? "Hello World!" : "Hello " . $firstname; pour obtenir exactement la même chose.

Et si vous pensez que c'est fini, vous vous trompez. Rector regarde maintenant le paramètre de notre fonction et comprend que c'est une chaîne, donc il va suggérer d'ajouter un type hint et, aussi, d'ajouter un type de retour pour la même raison.

Puisque nous avons analysé les suggestions de Rector et que nous sommes d'accord, relancez la même commande mais, cette fois, sans le flag --dry-run.

>user@machine: ~/yourproject

Rector a mis à jour notre fichier index.php ! Maintenant, notre fonction tient sur une seule ligne et non plus sept. Belle amélioration !

Notre nouveau code, amélioré, est maintenant :

<?php
function sayHello(string $firstname = ""): string
{
return $firstname == "" ? "Hello World!" : "Hello " . $firstname;
}
echo sayHello();

Cette fonction sayHello accepte un prénom optionnel et retourne un message de salutation personnalisé. Si aucun prénom n'est fourni, elle retourne « Hello World! », sinon elle retourne « Hello » suivi du prénom. Le script appelle ensuite cette fonction sans argument et affiche le résultat.

Rector est absolument brillamment puissant. Apprenez-en plus en lisant attentivement son site de documentation : https://getrector.com/documentation/.

Le programmeur a le dernier mot, mais pour combien de temps encore ?

Nous pouvons faire une amélioration de plus en extrayant le préfixe Hello. Rector ne l'a pas encore vu, mais pour combien de temps ?

<?php
function sayHello(string $firstname = ""): string
{
return "Hello " . ($firstname == "" ? "World!" : $firstname);
}
echo sayHello();

Ce code définit une fonction sayHello qui prend un prénom optionnel en paramètre et retourne un message de salutation. Si aucun prénom n'est fourni, elle dit bonjour au monde, sinon elle salue la personne nommée. La fonction est ensuite appelée sans argument et son résultat s'affiche à l'écran.

Rector - Tous les fichiers d'un coup​

Retournez dans votre terminal et lancez la commande ci-dessous pour scanner tout votre code.

>user@machine: ~/yourproject
Dry-run signifie qu'aucune modification n'est faite, juste affichée​

Quand vous lancez Rector avec vendor/bin/rector process . --dry-run --config .config/rector.php, aucun fichier ne sera réécrit. Rector se contentera de suggérer des changements.

Si vous autorisez Rector à modifier vos fichiers, lancez plutôt vendor/bin/rector process . --config .config/rector.php mais, là, vous devez être sûr que c'est OK.

Versionner son code avec par exemple GitHub

Si vous utilisez un système de versioning, vous pouvez pousser votre code actuel sur GitHub, créer une nouvelle branch comme par exemple refactoring puis lancer Rector sans risque sur votre disque. Si quelque chose casse, vous pouvez toujours récupérer vos sources d'avant les modifications.

Et on peut ajouter d'autres outils​

Il existe encore plusieurs outils d'analyse de code pour PHP :

Vous pouvez certainement aussi les ajouter à votre container.

Extensions​

Certaines extensions seront installées automatiquement :

Conclusion​

Souvenez-vous du collègue du début de cet article, celui qui n'indente pas comme vous. Il n'y a plus rien à lui demander. Les règles ne sont plus dans une page de wiki ou dans un commentaire de code review, elles sont dans deux fichiers versionnés — devcontainer.json et Dockerfile — et elles s'appliquent dès l'ouverture du projet, sur chaque machine, à l'identique.

C'est vraiment là le changement : l'environnement n'est plus quelque chose que chaque développeur construit, mais quelque chose que le repository fournit.

Si vous préférez lancer ces outils à la demande plutôt que de les intégrer dans une image, voyez jakzal/phpqa. Et pour être sûr qu'aucun de ces outils de développement ne se retrouve dans votre image de production, lisez Une seule image Docker pour la production et les devcontainers - la méthode propre.


🦋 Rejoignez la conversation sur BlueskyUn compte Bluesky gratuit est nécessaire pour aimer, repartager ou commenter. Ce bloc interroge l'API publique de Bluesky directement depuis votre navigateur (sans cookie, sans connexion) — votre adresse IP parvient donc aux serveurs de Bluesky dès que cette section se charge.

Chargement des commentaires…

Cet article vous a-t-il été utile ?
Avez-vous suivi les étapes de cet article ?