Comment comprendre ce qu’il se passe dans une application ? Il arrive que nous ayons un fonctionnement inattendu, que nous tentions de comprendre le fonctionnement d’un bundle tiers ou que nous ayons à faire de la rétro-ingénierie sur des applications que l’on prend en main.

Difficile de cerner certains comportements, surtout avec le découplage des applications. Les évènements sortent du chemin traditionnel et il est plus ardu de savoir lesquels ont été appelés ou combien de fois ? L’erreur intervient-elle au premier appel ? ou au suivant ?

Le débogage pas à pas permet d’étudier le cheminement du processus PHP, de suivre le contexte d’exécution, tout ça en live !

La théorie

La session de débogage

Il existe plusieurs manières de mettre en place une session de débogage. En local par exemple, le serveur web et votre IDE sont sur votre machine. Ou à distance, votre IDE est sur votre machine, mais le serveur web et l’interpréteur PHP sont sur un serveur distant. Il est possible que le serveur distant puisse gérer plusieurs développeurs souhaitant faire du débogage a distance.

Dans notre cas, nous allons nous concentrer sur le débogage à distance.

L’interpréteur PHP a besoin d’une extension qui va assurer le suivi des différents appels lors du traitement et de restituer le contexte d’exécution. Elle doit implémenter le protocole DBGp qui définit les communications et interactions entre le moteur de débogage et les IDE.

Ainsi, vous allez devoir indiquer à votre IDE les endroits que vous souhaitez étudier. Vous allez donc poser des points d’arrêt, ou “breakpoints”, sur les lignes à étudier. Ils sont matérialisés par une petite pastille rouge qui apparaît quand vous cliquez près du numéro de ligne.

Ensuite, lorsqu’une session de débogage est démarrée, le moteur de débogage va contacter l’IDE pour obtenir la liste des points d’arrêt. Si l’interpréteur arrive sur une ligne qui a un point d’arrêt, alors l’exécution est mise en attente. Les informations de contexte sont ensuite envoyées à l’IDE qui les affiche à l’utilisateur.

L’utilisateur peut alors analyser la situation et faire un choix, soit :

  • modifier le contexte, une variable par exemple puis faire un des autres choix ci-dessous
  • reprendre une exécution normale, le script reprend normalement jusqu’au prochain point d’arrêt ou jusqu’à la fin
  • d’exécuter la ligne et d’attendre à la suivante, pas à pas
  • d’entrer dans le détail, si la ligne fait appel à une méthode, on va aller dans la définition de celle-ci pour suivre l’exécution
  • sortir du block de code courant pour aller dans le contexte supérieur

Et voilà comment vous pouvez étudier le fonctionnement en live.

Les échanges

Voici un schéma des échanges et des informations qui transitent lors d’une session de débogage :

Echanges lors d'une session de débogage
Echanges lors d’une session de débogage

  1. Le navigateur effectue une requête HTTP qui contient des informations, un paramètre GET ou un entête particulier, qui va démarrer une session de débogage.
  2. Le serveur HTTP transmet les informations à PHP pour le traitement.
  3. Il s’agit de plusieurs échanges, dans un premier temps, le moteur de débogage demande les informations concernant la localisation des points d’arrêts, puis transmet le contexte PHP quand l’un d’entre eux est atteint. Le moteur de débogage attend alors le choix de l’utilisateur. Ces deux dernières opérations sont répétées autant de fois qu’il le faut.
  4. Quand le traitement est fait, PHP transmet la réponse au serveur HTTP.
  5. La réponse est alors retournée au navigateur.

Déboguer une application

Nous allons étudier une petite application qui gère des films et des articles. Elle est composée d’une page d’accueil et d’une administration simple. Elle est à votre disposition sur Github : FlobTestApp.

Pour notre cas, nous allons étudier le débogage à distance avec un seul développeur et les différentes IP/DNS sont connues. Un container Docker va jouer le rôle de serveur distant.

Pré-requis

Docker

L’application contient des images Docker afin d’être plus simple à utiliser. Si vous avez Docker et docker-compose, vous n’avez pas grand chose à installer !

Docker Dns-gen

Afin d’accèder plus facilement à vos différents containers, Docker Dns-gen rend disponible en local des noms de domaine basés sur le nom du container et avec l’extension docker. L’image est réalisée et maintenue par Jérémy DERUSSÉ

IDE

Les IDE les plus courants, tel que PHPStorm, NetBean ou Zend Studio, proposent les fonctionnalités de débogage. Dans cet article, je vais donner les indications pour PHPStorm, toutefois, sur les autres IDE les fonctionnalités sont très similaires et les explications seront également valables.

Compagnon dans le navigateur

L’installation est optionnelle, mais lorsque vous voudrez vous lancer dans une session de débogage, vous devrez ajouter un paramètre GET, POST ou un COOKIE particulier. les compagnons simplifient l’ajout de ce paramètre et quelques-uns gèrent également les redirections.

Installation

Tout d’abord, vous pouvez cloner l’application FlobTestApp depuis le repository Github : git clone https://github.com/florianbelhomme/FlobTestApp.git

Ensuite je vous invite à suivre la procédure d’installation décrite dans le fichier README.

Configuration et mise en place

Côté serveur

Pour faire du débogage pas à pas, vous avez besoin d’une extension PHP, dans notre cas c’est Xdebug qui est utilisé. Vous pouvez ouvrir le fichier docker/php/Dockerfile, ce dernier définit ce que contient l’image qui va exécuter l’application, vous pourrez constater qu’elle est bien ajoutée et activée.

Le fichier docker/php/xdebug.ini, inclu dans l’image lors de sa construction, définit la configuration de Xdebug. Détaillions les clefs de configuration de ce fichier :

  • Zend_extension est un paramètre interne à PHP, étant donné que l’on peut utiliser différentes extensions, il définit le chemin vers celle qui va être utilisée.
  • xdebug.remote_autostart : permet de définir si une session de débogage doit être activée à chaque requête. Il est préférable de laisser ce paramètre à 0. Toutefois, lors de redirection multiple, comme lors d’échanges OAuth, vous ne pouvez pas forcément rajouter d’informations aux requêtes pour déclencher des sessions de débogage. Pour ne rien rater vous pourrez mettre ce paramètre à 1.
  • xdebug.remote_enable : autorise le déclenchement d’une session de débogage à distance.
  • xdebug.remote_connect_back : lors de l’utilisation d’un serveur sur lequel plusieurs personnes travaillent, ce paramètre permet d’utiliser l’IP de la personne effectuant la requête comme IP de rappel pour le débogage.
  • xdebug.remote_host / xdebug.remote_port : étant donné que nous sommes dans le cas où nous n’avons qu’un développeur, nous forçons le rappel sur une IP précise, ici, l’IP de l’hôte Docker. Le port de rappel est également paramètrable.

Il n’y a pas besoin d’ouvrir des ports supplémentaires (en dehors du port 80 et 443) sur le serveur, car c’est lui qui va initier la communication avec l’IDE.

Xdebug permet de définir une clef qui permet de filtrer les appels, il est recommandé de la mettre en place sur les serveurs distants pour des raisons de sécurité. Notre container Docker n’étant accessible que par l’hôte, nous ne la mettons pas en place.

Vous pouvez maintenant vérifier que Xdebug est bien activé avec les bons paramètres. Symfony vous aide : consultez l’url http://flobphp.docker/app_dev.php/. Dans la WebDebugToolbar de SF, vous avez un onglet “PHP” qui vous indique si l’extension est active et si vous cliquez, vous êtes redirigé vers une page qui affiche le contenu de la méthode phpinfo().

Côté client

Dans notre configuration, avec Docker, vous n’avez pas besoin d’ouvrir de port. En revanche, si vous utilisez Xdebug sur un serveur distant, vous devez faire en sorte que le port 9000 (par défaut) de votre machine soit atteignable de l’extérieur.

Votre code étant déployé à deux endroits, il existe deux chemins racine du projet. Celui local utilisé par l’IDE et celui du serveur. Afin que PHPStorm puisse faire correctement le rapprochement entre les fichiers locaux et distants, il faut lui indiquer les équivalences. Vous devez donc créer un “serveur” dans PHPStorm.

Vous trouverez cette possibilité dans : Languages & Frameworks > PHP > Servers

Configuration du serveur dans PHPStorm
Configuration du serveur sur PHPStorm

  1. Cliquez le bouton d’ajout
  2. Vous pouvez alors préciser le nom du serveur / de l’application et son nom de domaine
  3. Réglez le “debugger” sur Xdebug
  4. Cochez la case “Use path mappings”
  5. PHPStorm affiche alors l’arborescence de votre projet et permet de saisir un chemin correspondant sur le serveur. Dans notre cas, l’arborescence interne du projet sera rigoureusement identique, il suffit donc de préciser la correspondance du dossier racine. Dans le container Docker le chemin de l’application est /app/.

PHPStorm est maintenant prêt.

Débogons

Tout d’abord, mettons en place un point d’arrêt. Vous pouvez le faire à n’importe quel moment, mais c’est un bon début.

Ensuite, indiquez à PHPStorm d’écouter les connexions de débogage. L’icône est un téléphone avec un insecte.

Icônes de débogage
Icônes de débogage

Vous pouvez maintenant consulter l’url http://flobphp.docker/app_dev.php/. Tout d’abord, il ne se passe rien, car nous n’avons pas encore demandé à Xdebug de démarrer le débogage. En revanche, si vous consultez l’url avec le paramètre GET : http://flobphp.docker/app_dev.php/?XDEBUG_SESSION_START= et que votre point d’arrêt se situe dans le chemin que va emprunter PHP, PHPStorm se met en premier plan. Il vous montre alors le point d’arrêt atteint et l’onglet de débug. Ce dernier contient les informations de contexte de PHP ainsi que les différents choix que vous pouvez effectuer.

Bon à savoir : l’exécution s’arrête juste avant que la ligne d’arrêt ne soit traitée.

La session de débogage commence
La session de débogage commence

Sur cet écran, on voit que PHPStorm indique la ligne atteinte. Dans l’onglet de débogage, on trouve une section “frames” qui montre la pile d’appels. Sur le côté, la section “variables” liste les différentes variables locales et globales.

Les principaux boutons sont entourés en rouge. Ce sont ceux qui vont indiquer votre choix suite à l’arrêt. Sur la gauche vous pouvez trouver le bouton “resume” qui permet de reprendre une exécution normale et le bouton “terminate” qui met directement fin au processus.

Dans la section “variables”, pour pouvez consulter le contenu de celles-ci mais pas seulement, en faisant un clic-droit plusieurs options s’offrent à vous. Vous pouvez les modifier en live. Si vous souhaitez apporter une attention particulière à une variable entre deux points d’arrêt, vous pouvez la suivre en l’ajoutant aux variables surveillées avec “add to watches”.

Maintenant, à vous d’installer une extension de débogage puis d’analyser vos scripts pour tirer vos conclusions.

Les alternatives

Blackfire

Même si ce n’est pas exactement une alternative, Blackfire.io analyse le fonctionnement de votre application et vous représente graphiquement le cheminement de votre application. Il propose d’autres informations, telles que les temps passés dans les différentes fonctions.

Exemple d'analyse BlackFire
Exemple d’analyse BlackFire

Si vous désirez le tester, les images Docker de l’application sont déjà pré-configurées, vous pouvez ajouter vos identifiants à votre fichier .bashrc comme ceci :

export BLACKFIRE_SERVER_ID=VOTREBLACKFIRESERVERID
export BLACKFIRE_SERVER_TOKEN=VOTREBLACKFIRESERVERTOKEN

export BLACKFIRE_CLIENT_ID=VOTREBLACKFIRECLIENTID
export BLACKFIRE_CLIENT_TOKEN=VOTREBLACKFIRECLIENTTOKEN

Une fois vos containers Docker relancés, vous n’avez plus qu’à utiliser le compagnon Chrome de Blackfire pour déclencher une analyse.

phpdbg

Pour les amoureux de la ligne de commande, il existe un client de débogage basé sur la ligne de commande.

phpdbg
phpdbg

http://phpdbg.com/

Liens

Documentation et configuration de Xdebug : https://Xdebug.org/docs/ Le protocole DBGp : https://github.com/derickr/dbgp
La partie débug du confluence de PHPStorm : https://confluence.jetbrains.com/
Documentation de Docker & Docker-compose : https://docs.docker.com/