
Pour déboguer les programmes PHP, utilisez souvent Xdebug . Cependant, les fonctionnalités standard de l'IDE et de Xdebug ne sont pas toujours suffisantes. Certains des problèmes peuvent être résolus en utilisant le proxy Xdebug - pydbgpproxy, mais toujours pas tous. Par conséquent, j'ai implémenté le proxy PHP Xdebug basé sur le cadre asynchrone amphp.
Sous la coupe, je vais vous dire ce qui ne va pas avec pydbgpproxy, ce qui manque et pourquoi je ne l'ai pas modifié. Je vais également expliquer comment fonctionne le proxy PHP Xdebug et montrer comment l'étendre à l'aide d'un exemple.
Pydbgpproxy vs proxy PHP Xdebug
Le proxy Xdebug est un service intermédiaire entre l'IDE et Xdebug (les demandes de proxy de Xdebug à l'IDE et vice versa). Le plus souvent, il est utilisé pour le débogage multi-utilisateur . C'est quand vous avez un serveur Web et plusieurs développeurs.
En tant que proxy, pydbgpproxy est généralement utilisé. Mais il a quelques problèmes:
- pas de page officielle;
- difficile de trouver où le télécharger; il s'avère que cela peut être fait ici - tout d'un coup, Python Remote Debugging Client;
- Je n'ai pas trouvé le dépôt officiel;
- en raison du paragraphe précédent, il n'est pas clair où introduire la demande de retrait;
- proxy, comme son nom l'indique, est écrit en Python, ce que tous les développeurs PHP ne connaissent pas, ce qui signifie que son expansion est un problème;
- suite du paragraphe précédent: s'il y a du code en PHP, et qu'il devra être utilisé en proxy, alors il devra être porté en Python, et la duplication de code n'est pas toujours très bonne.
Une recherche d'un proxy Xdebug écrit en PHP sur GitHub et sur Internet n'a donné aucun résultat. J'ai donc écrit le proxy PHP Xdebug . Sous le capot, j'ai utilisé le cadre asynchrone amphp .
Les principaux avantages du proxy PHP Xdebug par rapport à pydbgpproxy:
- Le proxy PHP Xdebug est écrit dans un langage familier aux développeurs PHP, ce qui signifie:
- il est plus facile d'y résoudre des problèmes;
- il est plus facile à étendre;
- Le proxy PHP Xdebug a un référentiel public, ce qui signifie:
- Vous pouvez le bifurquer et le finir selon vos besoins;
- Vous pouvez envoyer une demande d'extraction avec une fonctionnalité manquante ou une solution à un problème.
Comment travailler avec le proxy PHP Xdebug
L'installation
Le proxy PHP Xdebug peut être installé en tant que dépendance de dev via le composeur :
composer.phar require mougrim/php-xdebug-proxy --dev
Mais si vous ne souhaitez pas faire glisser des dépendances supplémentaires dans votre projet, le proxy PHP Xdebug peut être installé en tant que projet via le même compositeur:
composer.phar create-project mougrim/php-xdebug-proxy cd php-xdebug-proxy
Le proxy PHP Xdebug est extensible, mais ext-dom est requis par défaut (l'extension est activée par défaut en PHP) pour analyser XML et amphp / log pour la journalisation asynchrone:
composer.phar require amphp/log '^1.0.0'
Lancement

Le proxy démarre comme suit:
bin/xdebug-proxy
Le proxy commencera avec les paramètres par défaut:
Using config path /path/to/php-xdebug-proxy/config [2019-02-14 10:46:24] xdebug-proxy.NOTICE: Use default ide: 127.0.0.1:9000 array ( ) array ( ) [2019-02-14 10:46:24] xdebug-proxy.NOTICE: Use predefined ides array ( 'predefinedIdeList' => array ( 'idekey' => '127.0.0.1:9000', ), ) array ( ) [2019-02-14 10:46:24] xdebug-proxy.NOTICE: [Proxy][IdeRegistration] Listening for new connections on '127.0.0.1:9001'... array ( ) array ( ) [2019-02-14 10:46:24] xdebug-proxy.NOTICE: [Proxy][Xdebug] Listening for new connections on '127.0.0.1:9002'... array ( ) array ( )
Dans le journal, vous pouvez voir que le proxy par défaut:
- écoute
127.0.0.1:9001
pour les connexions de connexion IDE; - écoute
127.0.0.1:9002
pour les connexions Xdebug; - utilise
127.0.0.1:9000
comme IDE par défaut et IDE prédéfini avec clé idekey.
La configuration
Si vous souhaitez configurer des ports d'écoute, etc., vous pouvez spécifier le chemin d'accès au dossier des paramètres. Copiez simplement le dossier config :
cp -r /path/to/php-xdebug-proxy/config /your/custom/path
Il y a trois fichiers dans le dossier des paramètres:
Après avoir copié les fichiers, vous pouvez modifier et exécuter le proxy:
bin/xdebug-proxy --configs=/your/custom/path/config
Débogage
De nombreux articles ont été écrits sur la façon de déboguer du code à l'aide de Xdebug. Je noterai les points principaux.
Dans php.ini, les paramètres suivants doivent se trouver dans la section [xdebug]
(corrigez-les s'ils diffèrent des paramètres standard):
- idekey = idekey
- remote_host = 127.0.0.1
- remote_port = 9002
- remote_enable = On
- remote_autostart = On
- remote_connect_back = Off
Ensuite, vous pouvez exécuter du code PHP débogué:
php /path/to/your/script.php
Si vous avez tout fait correctement, le débogage commencera à partir du premier point d'arrêt dans l'EDI. Le débogage en mode php-fpm par plusieurs développeurs dépasse le cadre de cet article, mais est décrit, par exemple, ici .
Fonctionnalités du proxy d'extension
Tout ce que nous avons examiné ci-dessus, pydbgpproxy est également capable à un degré ou à un autre.
Parlons maintenant du plus intéressant du proxy PHP Xdebug. Les proxys peuvent être étendus à l'aide de votre propre usine (créée dans la configuration factory.php
, voir ci-dessus). L'usine doit implémenter l'interface Factory\Factory
.
Les plus puissants sont les soi-disant préparateurs de requêtes. Ils peuvent modifier les requêtes de Xdebug vers l'IDE et vice versa. Pour ajouter un Factory\DefaultFactory::createRequestPreparers()
requêtes, vous devez remplacer la méthode Factory\DefaultFactory::createRequestPreparers()
. La méthode renvoie un tableau d'objets qui implémentent l'interface RequestPreparer\RequestPreparer
. Lors du proxy d'une demande de Xdebug à l'IDE, ils sont exécutés dans l'ordre direct; lors du proxy d'une requête de l'IDE à Xdebug, il est inversé.
Les gestionnaires de requêtes peuvent être utilisés, par exemple, pour modifier les chemins d'accès aux fichiers (dans les points d'arrêt et les fichiers exécutables).
Déboguer les fichiers remplacés
Afin de donner un exemple de préparateur, je vais faire une petite digression. Dans les tests unitaires, nous utilisons des soft-mocks ( GitHub ). Soft-mocks vous permet de remplacer des fonctions, des méthodes statiques, des constantes, etc. dans les tests, est une alternative pour runkit et uopz . Cela fonctionne en réécrivant les fichiers PHP à la volée. De même, AspectMock fonctionne toujours.
Mais les fonctionnalités standard de Xdebug et IDE vous permettent de déboguer réécrit (avec un chemin différent), plutôt que les fichiers d'origine.
Examinons de plus près le problème de débogage à l'aide de soft-mocks dans les tests. Tout d'abord, prenons le cas où le code PHP est exécuté localement.
Les premières difficultés apparaissent au stade de la définition des points d'arrêt (points d'arrêt). Dans l'EDI, ils sont installés dans les fichiers d'origine, pas dans les fichiers réécrits. Pour mettre un point d'arrêt via l'EDI, vous devez trouver le fichier réécrit réel. Le problème est aggravé par le fait que chaque fois que le fichier d'origine est modifié, un nouveau fichier réécrit est créé, c'est-à-dire que pour chaque contenu de fichier unique, il y aura un fichier réécrit unique.
Ce problème peut être résolu en appelant la fonction xdebug_break()
, qui est similaire à la définition d'un point d'arrêt. Dans ce cas, il n'est pas nécessaire de rechercher un fichier réécrit.
Considérez maintenant la situation plus compliquée: l'application s'exécute sur une machine distante.
Dans ce cas, vous pouvez monter le dossier avec les fichiers réécrits, par exemple, via SSHFS. Si les chemins d'accès local et distant vers le dossier sont différents, vous devez toujours enregistrer les mappages dans l'EDI.
D'une manière ou d'une autre, cette méthode est légèrement différente de la méthode habituelle et vous permet de déboguer uniquement les fichiers copiés, mais pas ceux d'origine. Mais je veux toujours éditer et déboguer les mêmes fichiers originaux.
AspectMock a résolu le problème en activant le mode débogage sans la possibilité de le désactiver:
public function init(array $options = []) { if (!isset($options['excludePaths'])) { $options['excludePaths'] = []; } $options['debug'] = true; $options['excludePaths'][] = __DIR__; parent::init($options); }
Dans un exemple de test simple, le mode de débogage est par la suite plus lent de 20%. Mais je n'ai pas assez de tests AspectMock pour donner une estimation plus précise de sa lenteur. Si vous avez de nombreux tests sur AspectMock, je serai heureux si vous partagez la comparaison dans les commentaires.
Utiliser Xdebug avec des soft-mocks

Maintenant que le problème est clair, réfléchissez à la manière de le résoudre à l'aide du proxy PHP Xdebug. La partie principale se trouve dans la RequestPreparer\SoftMocksRequestPreparer
.
Dans le constructeur de classe, définissez le chemin d'accès au script d'initialisation de soft-mocks et exécutez-le (il est supposé que soft-mocks est connecté en tant que dépendance, mais n'importe quel chemin peut être transmis au constructeur):
public function __construct(LoggerInterface $logger, string $initScript = '') { $this->logger = $logger; if (!$initScript) { $possibleInitScriptPaths = [

Pour préparer une demande de Xdebug à l'IDE, vous devez remplacer le chemin d'accès au fichier réécrit par le fichier d'origine:
public function prepareRequestToIde(XmlDocument $xmlRequest, string $rawRequest): void { $context = [ 'request' => $rawRequest, ]; $root = $xmlRequest->getRoot(); if (!$root) { return; } foreach ($root->getChildren() as $child) {

Pour préparer une demande de l'EDI à Xdebug, vous devez remplacer le chemin d'accès au fichier d'origine par le chemin d'accès au fichier réécrit:
public function prepareRequestToXdebug(string $request, CommandToXdebugParser $commandToXdebugParser): string {
Pour que le Factory\DefaultFactory
requêtes fonctionne, vous devez créer votre classe d'usine et l'hériter de Factory\DefaultFactory
ou implémenter l'interface Factory\Factory
. Pour les soft-mocks, la Factory\SoftMocksFactory
ressemble à ceci:
class SoftMocksFactory extends DefaultFactory { public function createConfig(array $config): Config {
Ici, vous avez besoin de votre propre classe de configuration pour pouvoir spécifier le chemin du script d'initialisation des soft-mocks. Ce que c'est, vous pouvez le voir dans Config \ SoftMocksConfig .
Il ne restait plus que peu: créer une nouvelle usine et indiquer le chemin vers le script d'initialisation des soft-mocks. La softMocksConfig
à softMocksConfig
peut être consultée dans softMocksConfig
.
API non bloquante
Comme je l'ai écrit ci-dessus, le proxy PHP Xdebug utilise amphp sous le capot, ce qui signifie qu'une API non bloquante doit être utilisée pour fonctionner avec les E / S. Apmphp possède déjà de nombreux composants qui implémentent cette API non bloquante. Si vous allez étendre le proxy PHP Xdebug et l'utiliser en mode multi-utilisateur, assurez-vous d'utiliser des API non bloquantes.
Conclusions
Le proxy PHP Xdebug est encore un projet assez récent, mais dans Badoo il est déjà activement utilisé pour déboguer des tests à l'aide de soft-mocks.
Proxy PHP Xdebug:
- remplace pydbgpproxy dans le débogage multi-utilisateurs;
- peut fonctionner avec des soft-mocks;
- peut être étendu:
- Vous pouvez remplacer les chemins d'accès aux fichiers provenant de l'IDE et de Xdebug;
- des statistiques peuvent être collectées: en mode débogage, au moins le contexte exécutable est disponible lors du débogage (valeurs des variables et ligne de code exécutable).
Si vous utilisez le proxy Xdebug pour autre chose que le débogage multi-utilisateur, partagez votre cas et le proxy Xdebug que vous utilisez dans les commentaires.
Si vous utilisez pydbgpproxy ou un autre proxy Xdebug, essayez le proxy PHP Xdebug, informez-vous de vos problèmes, partagez les demandes de tirage. Développons le projet ensemble! :)
PS Merci à mon collègue Yevgeny Makhrov alias eZH pour l'idée du proxy smdbgpproxy !
Liens à nouveau
- Proxy PHP Xdebug - Proxy Xdebug, qui est discuté dans l'article;
- pydbgpproxy peut être téléchargé ici - tout d'un coup, Python Remote Debugging Client;
- amphp - framework PHP non bloquant asynchrone;
- outils pour maquettes:
Merci de votre attention!
Je serai heureux de recevoir des commentaires et suggestions.
Rinat Akhmadeev, Sr. Développeur PHP
UPD : traduction publiée de l' article en anglais.