PHP Packages Graph
Explorer l’écosystème PHP à partir des paquets publiés sur packagist.org

PHP Packages Graph est né d’un projet de recherche visant à comprendre l’écosystème PHP au-delà des noms de paquets, du nombre de téléchargements et des métadonnées propres à chaque dépôt.
L’objectif n’était pas simplement de collecter des données depuis Packagist. Il s’agissait surtout de transformer les métadonnées des paquets Composer en un modèle de graphe permettant d’étudier les relations de dépendance, l’influence des paquets, les pratiques de licence, les indicateurs de stabilité et les tendances de maintenance au sein d’un même écosystème connecté.
PHP Packages Graph est conçu comme un pipeline allant des données à l’analyse. Il associe scripts backend, ingénierie des données, bases de données orientées graphes, analyse des dépendances, recherche sur les écosystèmes logiciels et analyse exploratoire des données.
Le problème
Le problème central de PHP Packages Graph n’était pas l’accès aux paquets PHP, mais la compréhension de la structure de l’écosystème open source PHP.
Packagist et Composer facilitent l’installation des paquets, mais ne répondent pas directement aux questions plus profondes sur l’écosystème :
Un gestionnaire de paquets sait très bien résoudre les dépendances d’un projet. Il n’est pas conçu pour expliquer la structure des dépendances d’un écosystème entier.
Cette distinction compte, car les applications PHP modernes dépendent souvent de dizaines, voire de centaines de paquets directs et transitifs. Certaines bibliothèques deviennent une infrastructure invisible. Elles ne sont pas nécessairement visibles dans le produit, mais soutiennent une grande partie de l’écosystème.
En pratique, comprendre l’écosystème PHP devient un problème de graphe.
Un paquet peut en requérir un autre. Un fournisseur peut posséder de nombreux paquets. Un paquet peut entrer en conflit avec un autre, le remplacer, le fournir, le suggérer ou n’en dépendre que pour le développement. Ces relations ne sont pas des métadonnées secondaires : elles constituent la structure même de l’écosystème.
PHP Packages Graph est ainsi passé d’un simple script de collecte de données Packagist à un système de recherche fondé sur les graphes.
Orientation de la recherche
PHP Packages Graph est parti d’une idée simple :
Construire un graphe de dépendances des paquets PHP publiés sur Packagist.
Cette idée a rapidement pris de l’ampleur. Pour être utile, le système devait faire plus que récupérer les fichiers JSON des paquets. Il devait :
-
collecter les noms des paquets depuis Packagist ;
-
récupérer les métadonnées détaillées des paquets ;
-
stocker le jeu de données brut localement afin de pouvoir le rejouer et l’examiner ;
-
normaliser les données des paquets entre leurs différentes versions ;
-
extraire les dépendances depuis
require,require-dev,conflict,provide,replaceetsuggest; -
modéliser les fournisseurs, les paquets et leurs relations de dépendance dans Neo4j ;
-
conserver les métadonnées utiles, notamment les licences, les auteurs, les téléchargements, les liens vers les dépôts, le type de paquet, son statut d’abandon et ses dates de mise à jour ;
-
rendre le graphe accessible au moyen de requêtes Cypher ;
-
répondre à des questions de recherche sur l’influence, la stabilité, les licences et la maintenance.
Le défi d’ingénierie consistait à concevoir un pipeline reproductible capable de transformer les métadonnées de Packagist en une base de données orientée graphe, interrogeable comme une carte de l’écosystème.
Architecture du système
PHP Packages Graph s’organise autour de quatre couches principales :
Chaque couche a une responsabilité claire.
La couche de collecte assure l’acquisition des données. La couche de modélisation prend en charge leur validation et leur normalisation. La base orientée graphe assure la persistance et la modélisation des relations. Enfin, la couche d’analyse traduit le graphe en questions de recherche.
Le système suit ce flux :
Cette structure facilite l’examen, la réexécution, le débogage et l’extension du projet.
Couche de collecte des données
La couche de collecte des données est le premier composant technique majeur de PHP Packages Graph.
Son rôle est de transformer Packagist, registre public de paquets, en un jeu de données de recherche local. Packagist fournit les noms et métadonnées des paquets, leurs versions, dépendances, téléchargements, dépôts, mainteneurs, licences et types.
Le collecteur repose sur une idée centrale :
La collecte des données doit être reproductible, pouvoir reprendre après une interruption et rester séparée de l’import dans le graphe.
Au lieu de mêler directement les appels d’API aux écritures dans Neo4j, PHP Packages Graph crée d’abord un jeu de données JSON local. Le pipeline est ainsi plus facile à examiner, déboguer, réexécuter et étendre.
Le processus de collecte est simple :
Cette séparation est importante, car récupérer des données à l’échelle d’un écosystème est lent, fragile et exposé aux pannes réseau.
Un import dans le graphe ne devrait pas échouer à cause d’une seule requête HTTP. Un notebook de recherche ne devrait pas dépendre d’appels d’API en direct. Le jeu de données local fournit au projet une couche intermédiaire stable.
Collecte de la liste des paquets
La première étape récupère la liste complète des paquets depuis Packagist :
Elle fournit les noms de paquets au format standard de Composer :
vendor/packageCe format devient l’identifiant de référence pour le reste du système.
Le collecteur utilise ensuite le nom de chaque paquet pour récupérer ses métadonnées détaillées :
/packages/{vendor}/{package}.jsonChaque paquet est enregistré dans un chemin qui reprend son identité Composer :
packages/vendor/package.jsonC’est une décision de conception modeste, mais utile. La structure du système de fichiers suit celle des noms de paquets, ce qui facilite la navigation manuelle dans le jeu de données.
Récupération incrémentale
Avant chaque récupération, le collecteur vérifie si les informations du paquet existent déjà.
Le pipeline bénéficie ainsi d’un comportement incrémental simple :
C’est important, car Packagist contient un grand nombre de paquets. Tout récupérer à nouveau à chaque exécution ferait perdre du temps et de la bande passante.
La version actuelle conserve une stratégie simple, fondée sur des délais d’expiration des requêtes, la vérification de l’existence des fichiers locaux et des options de ligne de commande :
--fetch-list
--fetch-info
--force-updateCela suffit pour un pipeline de recherche.
Pourquoi la conception de la collecte est importante
La décision d’ingénierie essentielle est de distinguer la collecte de l’analyse.
Le collecteur ne cherche pas à répondre aux questions de recherche. Il crée une frontière fiable autour du jeu de données.
Le reste du projet gagne ainsi en clarté :
Cette approche est préférable à un unique script volumineux qui téléchargerait les paquets, analyserait le JSON, écrirait les nœuds, créerait les relations et produirait des graphiques au cours d’une même exécution.
Couche de base de données orientée graphe
Une fois les métadonnées des paquets collectées, le défi suivant concerne leur représentation.
Une table relationnelle peut stocker les enregistrements des paquets. Une base documentaire peut stocker leur JSON. Cependant, les écosystèmes de dépendances sont, par nature, largement structurés par leurs relations.
Neo4j est donc particulièrement adapté à ce projet.
La base de données orientée graphe repose sur une idée centrale :
Modéliser l’écosystème PHP comme un réseau de relations, et non comme une simple liste de paquets.
Le nœud Package se trouve au centre du graphe.
Un paquet appartient à un fournisseur et se connecte à d’autres paquets au moyen des relations de dépendance de Composer :
Ce modèle permet d’interroger l’écosystème sous une forme fidèle au domaine réel des dépendances.
Un paquet n’est pas seulement un enregistrement. C’est un nœud dans un réseau de dépendances.
Choix technologique
PHP Packages Graph utilise Neo4j comme base de données orientée graphe.
Le projet exécute Neo4j avec Docker Compose et active les plugins APOC et Graph Data Science. Ceux-ci constituent une base utile pour l’analyse de graphes et de futurs algorithmes de centralité.
Le service Neo4j local expose :
7474 -> Neo4j Browser
7687 -> Bolt protocolLe projet dispose ainsi de deux interfaces :
Cette répartition convient bien à un projet de recherche : Python traite les données, tandis que Neo4j assure le stockage du graphe et l’analyse interactive.
Modèle principal du graphe
Le premier import dans le graphe crée deux types de nœuds :
Vendor
Packageainsi qu’une relation de propriété :
(v:Vendor)-[:OWNS]->(p:Package)Le nœud du paquet stocke son nom Composer canonique :
full_name: vendor/packageCette propriété devient la clé de recherche principale pour l’enrichissement ultérieur et la mise en correspondance des dépendances.
L’importateur crée également des contraintes d’unicité :
Vendor.name
Package.full_nameC’est important, car les imports dans le graphe doivent pouvoir être réexécutés sans risque.
Sans contraintes d’unicité, la base pourrait créer silencieusement des fournisseurs ou des nœuds de paquets en double. Grâce aux contraintes et à MERGE, les imports deviennent plus prévisibles et idempotents.
Schéma du graphe
Le schéma du graphe peut être représenté comme un modèle compact de l’écosystème :
Ce schéma maintient un modèle simple tout en préservant les relations nécessaires à l’analyse de l’écosystème.
Le paquet comme objet de connaissance canonique
Après la création des nœuds initiaux, la couche de mise en correspondance enrichit chaque nœud de paquet avec des métadonnées :
description
published_at
updated_at
licenses
versions
authors
repository
github_stars
github_watchers
github_forks
github_open_issues
language
abandoned
downloads
type
has_stable_release
is_custom_typeChaque nœud Package devient ainsi un objet de connaissance compact.
Le graphe ne sait pas seulement qu’un paquet existe. Il peut aussi stocker son type, la présence de versions stables, les licences utilisées selon les versions, son nombre de téléchargements, ses dates de publication et de dernière mise à jour, ainsi que son éventuel abandon.
C’est cette combinaison qui rend le graphe utile à la recherche.
Les relations de dépendance expliquent la structure. Les propriétés des paquets apportent le contexte.
Couche de modélisation et de normalisation
Les métadonnées des paquets Packagist sont imbriquées, versionnées et ne sont pas uniformes d’un paquet à l’autre.
Un paquet peut avoir de nombreuses versions. Chacune peut définir ses propres dépendances, licences, auteurs, son type, sa stabilité et ses règles de remplacement.
La couche de modélisation transforme cette complexité propre aux versions en indicateurs au niveau des paquets.
Le modèle repose sur une idée centrale :
Préserver les métadonnées propres à chaque version, tout en exposant des indicateurs au niveau du paquet pour l’analyse du graphe.
PHP Packages Graph utilise des modèles Pydantic pour valider et structurer les données des paquets avant leur import dans Neo4j.
Le modèle comprend des objets au niveau du paquet, tels que :
Package
Downloads
Maintainer
Author
VersionLe modèle Version recueille notamment les champs Composer suivants :
require
require_dev
suggest
conflict
provide
replace
license
authors
version
version_normalized
abandonedLe modèle Package agrège ensuite ces champs sur l’ensemble des versions.
Indicateurs agrégés des paquets
Le modèle calcule des valeurs au niveau du paquet, telles que :
aggregate_versions()
aggregate_licenses()
aggregate_authors()
aggregate_require()
aggregate_require_dev()
aggregate_suggest()
aggregate_conflict()
aggregate_provide()
aggregate_replace()
has_stable_version()
last_updated_time()
is_custom_type()C’est à cette étape que le jeu de données devient analysable.
Au lieu de demander à chaque requête Cypher d’examiner tous les objets de version, la couche Python prépare les propriétés et listes de relations utiles au niveau du paquet avant leur écriture dans Neo4j.
Le graphe reste ainsi lisible.
Neo4j stocke les relations. Python prend en charge la normalisation plus complexe.
Mise en correspondance des relations de dépendance
La couche de mise en correspondance des dépendances crée des relations explicites dans le graphe à partir des métadonnées Composer :
REQUIRES
DEV_REQUIRES
CONFLICTS
PROVIDES
REPLACES
SUGGESTSCette distinction est importante.
Une dépendance de production n’est pas équivalente à une dépendance de développement. Un paquet souvent requis dans require-dev peut être central pour les tests, l’analyse statique, les normes de code, le débogage ou les outils de développement, sans pour autant intervenir lors de l’exécution.
En séparant ces types de relations, PHP Packages Graph peut répondre à des questions plus précises :
Une table de paquets à plat ne permet pas de répondre clairement à ce type de question.
Pourquoi la conception de la modélisation est importante
La principale valeur de la couche de modélisation réside dans la simplification sans perte de sens.
Les métadonnées Composer sont riches, mais cette richesse brute peut devenir du bruit. Un graphe de recherche a besoin des bonnes abstractions.
PHP Packages Graph conserve les distinctions importantes :
Il en résulte un graphe assez expressif pour analyser l’écosystème, tout en restant suffisamment simple pour être interrogé directement.
Couche d’analyse
Une fois le graphe construit, Cypher devient l’interface de recherche.
La couche d’analyse interroge directement le graphe sur des questions à l’échelle de l’écosystème.
La couche d’analyse repose sur une idée centrale :
Transformer les métadonnées des paquets en questions à l’échelle de l’écosystème.
Le projet comprend des requêtes Cypher sur la répartition des licences, les paquets les plus requis, les dépendances de développement, les paquets présents dans les deux catégories de dépendances, les téléchargements, les moyennes par auteur, les années de publication et les paquets maintenus sur une longue période.
Ces requêtes ne sont pas de simples vérifications de la base de données. Elles représentent l’orientation de recherche du projet.
Répartition des licences
Les licences constituent l’un des indicateurs les plus importants à l’échelle de l’écosystème.
Le graphe peut répondre aux questions suivantes :
C’est important, car les licences influencent l’adoption, la conformité, la redistribution et les risques pour les organisations.
Un graphe de dépendances dépourvu d’analyse des licences ne donne qu’une vision partielle.
Influence des dépendances
La question la plus importante pour le graphe concerne l’influence des paquets.
Un paquet qui reçoit de nombreuses relations REQUIRES est structurellement important, car beaucoup d’autres paquets en dépendent.
Le graphe peut répondre aux questions suivantes :
Cette analyse révèle la couche d’infrastructure de l’écosystème PHP.
Certains paquets peuvent sembler peu visibles en tant que produits, alors qu’ils sont profondément intégrés à la chaîne d’approvisionnement logicielle. Ils méritent une attention particulière, car leur maintenance, leur stabilité et leur sécurité affectent de nombreux projets en aval.
Téléchargements et centralité des dépendances
Les téléchargements et la centralité des dépendances sont liés, mais ne mesurent pas la même chose.
Les téléchargements mesurent le volume d’utilisation.
Les relations de dépendance entrantes mesurent l’influence structurelle.
Un paquet peut compter de nombreux téléchargements parce que beaucoup de projets l’installent directement. Un autre peut être structurellement important parce qu’il soutient d’autres paquets populaires.
PHP Packages Graph permet de comparer ces indicateurs au lieu de réduire la popularité à une seule dimension.
C’est important, car la santé d’un écosystème dépend à la fois de son infrastructure visible et invisible.
Stabilité et maintenance
Le projet étudie aussi la stabilité et la maintenance à partir de champs tels que :
has_stable_release
published_at
updated_at
abandoned
versionsCes champs aident à répondre à des questions comme :
Les métadonnées des paquets deviennent ainsi des indicateurs de pérennité.
La taille ne suffit pas à déterminer la santé d’un écosystème de dépendances. Celui-ci est sain lorsque ses paquets importants sont maintenus, stables, compréhensibles et utilisables dans un cadre légal clair.
Exemples de requêtes
Le projet utilise des requêtes Cypher pour explorer le graphe.
Par exemple, la répartition des licences peut être interrogée avec :
MATCH (p:Package)
UNWIND p.licenses AS license
RETURN license, COUNT(p) AS package_count
ORDER BY package_count DESC;Les principales dépendances d’exécution peuvent être interrogées avec :
MATCH (:Package)-[:REQUIRES]->(p:Package)
RETURN p.full_name AS package, COUNT(*) AS times_required
ORDER BY times_required DESC
LIMIT 100;Les principales dépendances de développement peuvent être interrogées avec :
MATCH (:Package)-[:DEV_REQUIRES]->(p:Package)
RETURN p.full_name AS package, COUNT(*) AS times_required
ORDER BY times_required DESC
LIMIT 100;Les paquets importants à la fois pour l’exécution et le développement peuvent être interrogés avec :
MATCH (p:Package)
OPTIONAL MATCH (:Package)-[:REQUIRES]->(p)
WITH p, COUNT(*) AS required_count
OPTIONAL MATCH (:Package)-[:DEV_REQUIRES]->(p)
WITH p, required_count, COUNT(*) AS dev_required_count
WHERE required_count > 0 AND dev_required_count > 0
RETURN p.full_name, required_count, dev_required_count
ORDER BY required_count DESC, dev_required_count DESC
LIMIT 10;Les anciens paquets toujours maintenus peuvent être interrogés avec :
MATCH (p:Package)
WHERE (p.abandoned IS NULL OR p.abandoned = false)
AND p.published_at IS NOT NULL
AND p.updated_at IS NOT NULL
WITH DISTINCT p,
duration.between(datetime(p.published_at), datetime(p.updated_at)) AS lifespan
ORDER BY lifespan.years DESC
LIMIT 10
RETURN DISTINCT p.full_name AS package_name, p.published_at, p.updated_at, lifespan.days;Grâce à ces requêtes, le graphe devient un outil de recherche, et non un simple système de stockage.
Notebook de recherche et figures
Le dépôt comprend également un notebook et des figures, en cohérence avec l’orientation du projet.
Le code ne se limite pas à un importateur. Il constitue aussi un environnement de recherche exploratoire.
Le notebook permet d’examiner les résultats, de créer des graphiques, de comparer des mesures et de transformer les requêtes du graphe en explications visuelles.
Les figures constituent la couche de communication de la recherche.
Une base orientée graphe aide à poser les questions. Un notebook aide à interpréter les réponses. Les figures rendent ces réponses compréhensibles.
Le processus de recherche se présente ainsi :
Ce processus ancre le projet dans des données réelles tout en rendant les résultats compréhensibles en dehors de la base de données.
Principales décisions d’ingénierie
Partir de la question sur l’écosystème, pas de l’outil
Le projet est parti d’une question de recherche :
À quoi ressemble l’écosystème PHP lorsque les paquets Packagist sont modélisés sous forme de graphe de dépendances ?
Cette question a guidé l’architecture.
L’objectif n’était ni « d’utiliser Neo4j » ni de « récupérer les données de Packagist ». Il s’agissait de comprendre la structure des dépendances, l’influence des paquets, leur stabilité, leurs licences et les tendances de maintenance.
Neo4j est utile parce que le problème prend naturellement la forme d’un graphe.
Utiliser une base orientée graphe pour les relations de dépendance
Les écosystèmes de paquets sont des réseaux.
Un paquet peut en requérir, suggérer, remplacer ou fournir un autre, ou entrer en conflit avec lui. Il s’agit de relations, pas seulement de colonnes.
Neo4j fait de ces relations des éléments de premier plan.
C’est le modèle de stockage adapté à l’analyse des dépendances.
Séparer la collecte de l’import
Le collecteur écrit un jeu de données JSON local avant l’entrée des données dans Neo4j.
C’est une décision solide pour la recherche.
Elle facilite la réexécution, l’examen, le débogage et l’extension du système. Elle évite aussi que l’import dans le graphe dépende de la disponibilité de l’API en direct.
Garder des types de relations explicites
Le projet ne réduit pas toutes les arêtes à une relation générique DEPENDS_ON.
Il conserve séparément les types de relations de Composer :
REQUIRES
DEV_REQUIRES
CONFLICTS
PROVIDES
REPLACES
SUGGESTSCe choix de modélisation est pertinent, car chaque relation a une signification différente.
L’influence des dépendances d’exécution et celle des dépendances de développement ne doivent pas être confondues trop tôt.
Agréger les métadonnées des paquets avant leur insertion dans le graphe
Le modèle Pydantic agrège les licences, les auteurs, les versions, les dépendances, la stabilité et les horodatages de mise à jour avant l’écriture dans Neo4j.
Le graphe reste ainsi plus simple et les requêtes plus claires.
La base de données ne devrait pas avoir à comprendre chaque détail imbriqué du JSON de Packagist pour répondre à des questions élémentaires sur l’écosystème.
Utiliser Cypher comme interface de recherche
Cypher convient bien, car les questions de recherche portent sur les relations.
Des requêtes comme « paquets les plus requis » ou « paquets requis à la fois lors de l’exécution et du développement » s’expriment naturellement dans un graphe.
La base de données ne sert donc plus seulement à la persistance : elle devient une interface d’analyse.
Préparer l’utilisation d’algorithmes de graphe
La configuration de Neo4j comprend le plugin Graph Data Science, ce qui ouvre la voie à des analyses ultérieures plus approfondies.
L’étape suivante naturelle consiste à calculer :
Le projet passerait ainsi d’une analyse descriptive à une recherche structurelle sur l’écosystème.
Considérer la maintenance comme un indicateur de l’écosystème
Le projet ne se limite pas aux téléchargements ou au nombre de dépendances.
Il intègre aussi des champs tels que le statut d’abandon, les dates de publication et de mise à jour, la présence d’une version stable, les versions et le type de paquet.
C’est important, car la santé d’une dépendance ne dépend pas seulement de sa popularité.
Un paquet abandonné dont dépendent de nombreux projets représente un risque. Un ancien paquet toujours maintenu constitue une infrastructure. Un paquet sans version stable peut être utile, mais il transmet un autre type d’indicateur d’adoption.
Ce que j’ai appris
La principale leçon de PHP Packages Graph est qu’un écosystème logiciel ne se résume pas à un ensemble de paquets.
C’est un réseau d’infrastructure.
Certains paquets sont visibles parce que les développeurs les installent directement. D’autres restent invisibles, car ils se trouvent en profondeur dans l’arbre des dépendances. Les deux peuvent pourtant être importants.
La modélisation en graphe rend cette structure visible.
J’ai également appris que les métadonnées des paquets gagnent en valeur lorsqu’elles sont reliées. Un champ de licence, un nombre de téléchargements ou un indicateur d’abandon sont utiles. Mais l’analyse devient réellement éclairante lorsque ces signaux sont combinés aux relations de dépendance.
Par exemple, un paquet abandonné avec peu de dépendants pose un certain type de problème. Un paquet abandonné qui reçoit des milliers de relations de dépendance en pose un tout autre.
Le projet a aussi montré que la recherche sur un écosystème exige une préparation rigoureuse des données. Les métadonnées de Packagist sont riches, mais imbriquées entre les versions et pas toujours uniformes. Avant toute analyse, les données doivent être validées, normalisées, agrégées et associées à la structure appropriée.
Enfin, Neo4j n’est pas ici un simple outil de visualisation. Il change la nature des questions que le projet peut poser. Dès lors que les paquets deviennent des nœuds et les dépendances des arêtes, l’écosystème PHP peut être étudié comme un réseau.
Conclusion
PHP Packages Graph est un système de recherche fondé sur les graphes pour analyser l’écosystème des paquets PHP.
Sa principale valeur d’ingénierie ne réside pas dans la récupération des métadonnées depuis Packagist, mais dans le pipeline construit autour d’elles : collecte, création d’un jeu de données local, validation, normalisation, modélisation du graphe, mise en correspondance des dépendances, analyse Cypher et visualisation des résultats de recherche.
Le système transforme les données des paquets Packagist en un graphe connecté dans lequel les paquets peuvent être analysés selon leur influence, leur rôle comme dépendance, leur licence, leur stabilité, leurs téléchargements, leur ancienneté, leur maintenance et la structure de leurs relations.
Voici l’idée centrale de PHP Packages Graph :
Rendre la structure cachée de l’écosystème PHP visible, interrogeable et analysable.