Déboguer comme un chef en PHP avec XDebug
Tags: debug, php, xdebug
Published: 2020-12-17 08:55:12

On retrouve encore trop souvent des développeurs PHP qui déboguent à l’ancienne avec des  `var_dump()`, `kint()` ou `dump()`.

Souvent, cela vient de la complexité à mettre en place XDebug et de la méconnaissance de son mode de fonctionnement.

## Qu’est-ce qu’XDebug ?

XDebug est un outil de débogage pas à pas, qui permet de placer des points d’arrêt (*breakpoints*) et de suivre l’exécution en temps réel en consultant les variables ainsi qu’en pouvant évaluer et modifier leurs valeurs.

Maîtriser un tel outil fait gagner énormément de temps dans l’analyse d’un bogue.

## Pourquoi choisir le débogage avec XDebug ?

Le débogage avec XDebug est certes plus complexe à mettre en place initialement car il demande de configurer votre environnement d’exécution (extension PHP XDebug + paramétrage) et votre IDE. Mais cette opération n’est à faire qu’une fois sur votre poste par projet. Ensuite, vous n’avez qu’à lancer la session de débogage sans rien avoir à configurer de plus.

Par rapport à du débogage à l’ancienne, c’est-à-dire avec des traces de log, vous gagnerez énormément de temps du fait de pouvoir visualiser et manipuler les variables (expression, définition de valeur), aussi bien dans le contexte du code où sont posés les points d’arrêt qu'en remontant la pile d’exécution.

## Comment fonctionne XDebug ?

XDebug va se connecter à votre IDE. Il faut donc s’assurer que l’environnement d’exécution de votre application écrite en PHP ait accès à votre IDE.

Si comme beaucoup vous utilisez Docker, cela veut dire passer par l’interface `docker0` de votre conteneur, qui est l'interface qui permet de faire le pont entre votre conteneur et le système d'exploitation principal de votre machine.

Pour trouver l'IP de l'interface `docker0`, taper la commande suivante :

- macOS et Linux : `ifconfig`

  ```bash
  docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
          inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
          inet6 fe80::42:a3ff:febc:7ab4  prefixlen 64  scopeid 0x20<link>
          ether 02:42:a3:bc:7a:b4  txqueuelen 0  (Ethernet)
          RX packets 0  bytes 0 (0.0 B)
          RX errors 0  dropped 0  overruns 0  frame 0
          TX packets 13430  bytes 1949419 (1.9 MB)
          TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
  ```

- Windows : `ipconfig` (chercher l'interface `DockerNAT`)

![DockerNAT on windows](https://dev-to-uploads.s3.amazonaws.com/i/gli0zbu1ujxeokqd44rg.png)

## Installation de XDebug

XDebug est disponible sous la forme d’une extension PHP. Bien souvent pour l’installer, il suffit d’installer le paquet de cette extension sur votre machine de développement ou dans votre conteneur.

Je vais donner la commande pour une Ubuntu 20.04 LTS, mais je vous invite à vous référer à la documentation de votre image Docker ou de votre distribution pour trouver les instructions d’installation :

```bash
apt install php-xdebug
```

## Configuration de XDebug

C’est la partie la plus délicate, il est impératif de bien comprendre chacun des paramètres pour pouvoir ensuite identifier les éventuels problèmes de configuration qui pourront expliquer les dysfonctionnements potentiels.

Les paramètres importants pour déboguer en distant, c’est-à-dire d’un conteneur vers votre IDE sur l’hôte, sont les suivants :

- `xdebug.remote_enable` => A valoriser à 1 pour activer.
- `xdebug.remote_host` => Mettre l’IP de l’interface `docker0` du conteneur ou `host.docker.internal` sous Docker for Mac ou Docker for Windows.
- `xdebug.remote_port` => En général `9000`, mais pourra être changé dans l’IDE.
- `xdebug.idekey` (optionnel) => Un identifiant unique (utile si vous voulez exécuter plusieurs sessions de débogage en même temps sur des applications différentes).

Exemple de configuration :

```bash
/etc/php/7.4/fpm/conf.d/20-xdebug.ini
zend_extension=xdebug.so

xdebug.remote_enable=1
xdebug.remote_host="host.docker.internal"
xdebug.remote_port=9000
```

## Activation dans votre IDE

### Visual Studio Code

Sous VSCode, il vous faudra installer l’extension "PHP Debug" de Félix Fbecker puis créer un `launch.json` dans le répertoire `.vscode` avec le contenu suivant :

```json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen to XDebug",
            "type": "php",
            "request": "launch",
            "port": 9000,
            // server -> local
            "pathMappings": {
                "/var/www/docroot": "${workspaceRoot}/docroot"
            },
            "xdebugSettings": {
                "max_children": 128,
                "max_data": 512,
                "max_depth": 5
             }
        }
    ]
}
```

La clé `pathMappings` est utile si vous utilisez des conteneurs ou que votre application s’exécute sur un serveur distant, il permet d'indiquer la correspondance entre le chemin dans le conteneur, ou sur le serveur, et le chemin sur votre environnement local (hors conteneur).

Une fois cela fait, vous lancez le profile "_Listen to XDebug_" et vous pouvez commencer à déboguer.

### PHPStorm

Vous devez vous assurer que le port d'écoute pour XDebug dans PHPStorm est configuré en accord avec votre configuration PHP (9000 par défaut) :

![](https://dev-to-uploads.s3.amazonaws.com/i/lyxsxmf4c9pg7owwzji1.jpg)

Puis ajouter une configuration de lancement :

![](https://dev-to-uploads.s3.amazonaws.com/i/hxeuz36ms3iuqhnjlxmf.jpg)

![](https://dev-to-uploads.s3.amazonaws.com/i/h6cvl1ylzl01781ei479.jpg)

![](https://dev-to-uploads.s3.amazonaws.com/i/ni492813xynwqre87lqn.png)

Si vous utilisez un conteneur ou que votre application s’exécute sur un serveur, cochez l'option "_Use path mappings_" pour définir la correspondance entre le chemin dans le conteneur, ou sur le serveur, avec vos fichiers sur votre machine locale.

![](https://dev-to-uploads.s3.amazonaws.com/i/8qyqh4l0boo9yfvoph29.png)

Ensuite il suffira de lancer le debug en mode écoute :

![](https://dev-to-uploads.s3.amazonaws.com/i/kpgtufpy7dobqsai4l6w.jpg)

Si vous avez plusieurs applications qui tournent et que vous voulez déboguer l’une d’entre elles en particulier, vous pouvez utiliser la clé de session (_idekey_).

Dans ce cas, il faut désactiver l’écoute (avec l'icône en forme de téléphone), et appeler l’URL à déboguer avec le paramètre `XDEBUG_SESSION_START` valorisé avec la clé de session configuré dans votre IDE (ex : `XDEBUG_SESSION_START=PHPSTORM`).

Cela peut être simplifié en utilisant l’extension navigateur  `XDebug Helper` :

- Pour Firefox: https://addons.mozilla.org/en-US/firefox/addon/xdebug-helper-for-firefox/
- Pour Chrome/Chromium/Brave/Opera: https://chrome.google.com/webstore/detail/xdebug-helper/eadndfjplgieldjbigjakmdgkmoaaaoc

### Ligne de commande

Il est également possible de lancer le débogage pour une application PHP en ligne de commande.

Pour cela, il suffit de définir les paramètres XDebug via la variable d’environnement `XDEBUG_CONFIG` :

```bash
export XDEBUG_CONFIG="remote_enable=1 remote_host=<ip_docker0_ou_localhost> remote_port=9000 idekey=<vscode ou PHPSTORM>"
```

Si votre IDE est PHPStorm, il faudra en plus définir la variable `PHP_IDE_CONFIG` pour spécifier le nom de la configuration serveur ayant le _path mapping_ correspondant à votre conteneur Docker ou à votre serveur :

```bash
export PHP_IDE_CONFIG="serverName=<nom_config_serveur>"
```

Ensuite, lancez votre script PHP CLI.

## Fonctionnalités de débogage

### Les points d’arrêt

L’élément principal dans un débogage pas à pas sont les points d’arrêt. Ils vous permettent d’indiquer à votre IDE à quel endroit vous souhaitez que l’exécution s’arrête pour inspecter le contexte de l’application à ce moment-là.

Il existe deux types de points d’arrêt :

1. les points d'arrêt inconditionnels,
2. les points d'arrêt conditionnels.

Comme vous l’avez certainement deviné, les points d’arrêts inconditionnels arrêteront l’exécution de l’application aux endroits où ils sont positionnés, quoiqu’il arrive, alors que les points d’arrêt conditionnels le feront que si les conditions définies sont remplies.

Ces derniers sont très pratiques pour déboguer des cas précis, en particulier sans avoir à passer ceux qui ne nous intéressent pas.

![](https://dev-to-uploads.s3.amazonaws.com/i/smhag3u7iv1vl6atx1rf.png)

Pour placer un point d'arrêt, il suffit de cliquer devant une ligne sur la colonne à gauche de l'éditeur.

Pour définir une condition sur le point d'arrêt, il suffit de cliquer droit sur un point d'arrêt existant (ou clic-droit puis sélectionner `Edit breakpoint` sous VSCode).

### L’inspecteur de variables

L’autre outil à disposition est l’inspecteur de variables. Vous aurez peut-être remarqué, dans la configuration de VSCode, nous avons mis des paramètres de profondeurs d’inspection (`max_children`, `max_data`, `max_depth`), ils permettent de définir la profondeur d'inspection des variables, comme, par exemple, dans des tableaux ou objets, directement depuis cet inspecteur. Nous n’avons pas eu à définir ces paramètres sous PHPStorm parce que dans son cas ils sont déjà assez importants par défaut.

Voyons à quoi ressemble cet inspecteur sous PHPStorm :

![](https://dev-to-uploads.s3.amazonaws.com/i/p30um4sftnzrjxg1obrl.png)

Et sous VSCode :

![](https://dev-to-uploads.s3.amazonaws.com/i/mobs8ttjpje4ztokatlp.png)

En plus d’inspecter les variables déclarées dans le contexte du point d’arrêt courant, il est possible de sélectionner une ligne de la pile d’exécution pour visualiser les variables des éléments appelants, ce qui est très pratique quand le bogue provient en réalité d’un élément plus en amont dans la pile d’exécution.

Sous PHPStorm, cet inspecteur de variables permet également de modifier dynamiquement la valeur d’une variable :

![](https://dev-to-uploads.s3.amazonaws.com/i/wlkf7gw299nyaz3ojng1.png)

### L’inspecteur d’expressions

Un autre outil très puissant dans le débogage pas à pas est l’inspecteur d’expressions.

Celui-ci vous permet de définir des expressions complexes, ce qui peut s'avérer très utile, par exemple pour cibler un élément particulier d’un tableau, ou visualiser la taille d’une chaîne de caractère ou d’un tableau, sans modifier le code :

![](https://dev-to-uploads.s3.amazonaws.com/i/vv2ie6kgm2t0clqokb11.png)

### Avancer dans le code

Si cela s'appelle un débogueur pas à pas, c'est parce qu'il permet, une fois arrêté à un endroit du code, d'ensuite le parcourir ligne par ligne ou jusqu'au prochain point d'arrêt.

Dans les captures d'écran ci-dessus, vous aurez peut-être remarqué les barres d'outils :

| Barres d'outils des IDEs ||
|-|-|
| PHPStorm | ![](https://dev-to-uploads.s3.amazonaws.com/i/ujj6yecpcfpuqkhe3ogg.png) ![](https://dev-to-uploads.s3.amazonaws.com/i/0r9xunb497frosmr6xsq.png)
| VSCode | ![](https://dev-to-uploads.s3.amazonaws.com/i/3kprw1umsr3454ooepfh.png)

Elle servent à parcourir le code:

|                                         | Commandes de progression dans le code |
|-|-|
| ![](https://dev-to-uploads.s3.amazonaws.com/i/4lediouyqgexnf3tlcnv.png) | Avancer jusqu’au prochain point d'arrêt                 |
| ![](https://dev-to-uploads.s3.amazonaws.com/i/jdemlffadanrv7ysppsx.png) | Passer à la ligne suivante sans entrer dans la fonction |
| ![](https://dev-to-uploads.s3.amazonaws.com/i/6o3yvi76xyrdxvaxf925.png) | Entrer dans la fonction                                 |
| ![](https://dev-to-uploads.s3.amazonaws.com/i/ktwe1jff8lxirtwaxfds.png) | Sortir de la fonction                                   |
| ![](https://dev-to-uploads.s3.amazonaws.com/i/o44inpcmnptmdt2u00cl.png) | Reprendre l’exécution au début                          |
| ![](https://dev-to-uploads.s3.amazonaws.com/i/uhdmeeoxxumow314ecgv.png) | Interrompre la session de débogage                      |

## Les autres capacités de XDebug

En plus de proposer une fonctionnalité débogage pas à pas, XDebug est également capable de faire du profilage, afin de mesurer précisément les performances de votre code.

Le profilage mérite un article à lui seul, nous approfondirons ce sujet dans un autre billet.