Étude de cas

CorianderPHP

Un projet personnel de R&D pour comprendre le fonctionnement interne d'un framework PHP, du routing aux tests en passant par la CLI et l'automatisation.

Capture du site de documentation CorianderPHP.

Ressources du projet

Le framework et sa documentation sont maintenus dans deux repositories séparés. La documentation dispose de son propre build, de ses tests et d'un workflow automatisé de mise à jour.

Introduction

J'ai commencé CorianderPHP car je voulais mieux comprendre ce qui se passait derrière les API des frameworks que j'utilisais déjà. Plutôt que d'apprendre uniquement à utiliser Laravel ou d'autres outils, je voulais implémenter certains de leurs mécanismes et comprendre leur fonctionnement.

L'objectif n'était pas de recréer Laravel ou Symfony. Je voulais disposer d'un framework suffisamment petit pour pouvoir le comprendre de bout en bout, tout en conservant une structure claire pour mes projets.

Cette approche me permet aussi d'avoir une base légère pour certains projets personnels, lorsque les fonctionnalités d'un framework plus complet ne sont pas nécessaires.

Pourquoi je l'ai créé

CorianderPHP est rapidement devenu un projet de recherche et d'apprentissage. Chaque nouvelle fonctionnalité est l'occasion de regarder comment d'autres frameworks répondent au même problème, puis de comprendre les raisons derrière leurs choix.

Le projet m'a amené à travailler sur le routing, le cycle requête/réponse, les middlewares, l'injection de dépendances, les tests, Composer, les standards PSR, GitHub Actions ou encore la CI/CD.

Implémenter moi-même ces mécanismes m'a également permis de mieux comprendre le rôle des abstractions. Elles peuvent simplifier un projet lorsqu'elles répondent à un besoin réel, mais aussi ajouter une complexité inutile lorsqu'elles sont introduites trop tôt.

Vues statiques et dynamiques

L'un des premiers sujets que je voulais simplifier concernait le routing des vues. Pour une page simple, CorianderPHP peut déterminer directement l'URL à partir de son emplacement dans l'arborescence. Lorsqu'une page a besoin de paramètres ou de données préparées, une route explicite et un controller prennent le relais.

Contrairement à certains systèmes de routing basés entièrement sur les fichiers, les paramètres dynamiques ne reposent pas sur des fichiers comme [id].php. Je préfère déclarer explicitement ces routes et utiliser un controller pour garder la préparation des données claire.

Vue statique

L'URL peut être déterminée directement à partir des dossiers présents dans public/public_views.

public
public_views
about
index.php
metadata.php
URL /about
Documentation des vues statiques

Vue dynamique

Une route reçoit la requête, appelle un controller, puis le controller prépare les données nécessaires à la vue.

1

Requête

Le navigateur ouvre /articles/42.

2

Route

public/routes.php reconnaît /articles/{id}.

Il appelle ArticleController::show().

3

Controller

Le controller récupère l'identifiant, charge les données nécessaires et les transmet à la vue.

4

Vue

public/public_views/articles/show/index.php affiche les variables préparées.

Documentation des vues dynamiques

Architecture et fonctionnement interne

Travailler sur CorianderPHP a changé ma manière de regarder les frameworks que j'utilise. Beaucoup de mécanismes paraissent automatiques lorsqu'on travaille uniquement avec leurs API. Les implémenter permet de mieux comprendre les responsabilités de chaque composant et l'origine de certaines abstractions.

J'ai notamment travaillé directement sur le cycle HTTP requête/réponse, les middlewares, les controllers, les vues, l'injection de dépendances, les modules, Composer ainsi que des interfaces comme PSR-7, PSR-15 et PSR-3.

Cette expérience m'a permis de mieux comprendre comment ces différents éléments interagissent pendant le traitement d'une requête.

CLI et expérience développeur

J'ai également développé une CLI pour regrouper les opérations courantes autour du framework. L'objectif est de faciliter certaines tâches répétitives sans masquer ce qui se passe derrière les commandes.

Elle permet notamment de générer des fichiers, lancer les builds front-end, gérer le cache, configurer la base de données, exécuter les migrations, vérifier la version installée ou mettre à jour le framework.

Génération

Création de vues, controllers, routes, modules, API controllers et migrations.

Assets

Exécution des tâches TypeScript et Tailwind depuis la racine du projet.

Base de données

Configuration de la connexion, utilisation de PDO et gestion des migrations.

Migrations

Suivi des batchs, consultation du statut, rollback et détection des migrations modifiées après leur exécution.

Mise à jour du framework

Prévisualisation des changements, protection des modifications locales, création de sauvegardes et restauration en cas d'erreur.

Le système de mise à jour a été particulièrement intéressant à concevoir. Il m'a amené à travailler sur le versioning, les archives de release, la détection des modifications locales, les sauvegardes et les mécanismes de rollback.

La partie base de données m'a posé une question similaire : jusqu'où simplifier l'utilisation sans masquer complètement le SQL ni rendre le comportement du framework difficile à comprendre.

Garder le framework léger

CorianderPHP reste volontairement limité dans son périmètre. Je n'ajoute pas une fonctionnalité simplement parce qu'elle existe dans d'autres frameworks : elle doit répondre à un besoin identifié et avoir une place claire dans le projet.

Une partie intéressante du travail consiste donc aussi à décider ce qui ne doit pas être ajouté.

Les frameworks plus complets sont adaptés à de nombreux projets et équipes. CorianderPHP me donne simplement un environnement plus réduit pour expérimenter, comprendre les choix d'architecture et construire mes propres projets.

CI avec GitHub Actions

CorianderPHP m'a aussi servi à approfondir GitHub Actions. Je voulais automatiser les vérifications de qualité et m'assurer que chaque changement passe par le même processus de validation.

1

Déclenchement

Push ou Pull Request vers main.

2

Validation

Vérification des fichiers Composer, mise en cache et installation des dépendances.

3

Audit

Recherche de vulnérabilités connues dans les dépendances.

4

Vérification syntaxique

Contrôle de la syntaxe des fichiers PHP.

5

Tests

Exécution de la suite PHPUnit.

Ce workflow me permet de travailler concrètement avec la CI et de sécuriser le cycle de développement sans dépendre uniquement des vérifications effectuées en local.

Automatisation des releases et de la documentation

Je voulais également expérimenter l'automatisation entre plusieurs repositories. La publication d'une nouvelle version du framework déclenche donc une chaîne de mise à jour côté documentation.

1

Release

Une nouvelle version du framework est publiée.

2

Analyse

Le workflow récupère la version actuelle, la précédente, le commit associé et les fichiers modifiés.

3

Transmission

Le contexte de la release est envoyé au repository de documentation.

4

Mise à jour

La documentation récupère la nouvelle version du framework.

5

Build

Les assets et les projets téléchargeables sont régénérés.

6

Tests

La suite de tests de la documentation est exécutée.

7

Pull Request

Une Pull Request contenant la mise à jour et les informations de release est créée automatiquement.

8

Relecture

La Pull Request reste soumise à une validation manuelle avant son intégration.

Les deux repositories communiquent à l'aide d'un événement repository_dispatch, accompagné des informations nécessaires pour identifier les changements apportés par la nouvelle version.

J'ai volontairement conservé une étape de validation humaine. L'automatisation prend en charge les opérations répétitives et prépare la mise à jour, mais la documentation n'est pas modifiée automatiquement sans relecture.

La documentation comme partie du projet

Je considère la documentation comme une composante du projet à part entière. Elle possède son propre cycle de validation et ne dépend pas uniquement d'une mise à jour manuelle après chaque changement du framework.

Son workflow installe les dépendances PHP et Node.js, reconstruit les assets front-end, régénère les projets proposés au téléchargement et exécute la suite de tests.

Cette organisation permet également aux releases du framework de préparer automatiquement les prochaines évolutions de la documentation.

Ce que j'ai appris

La principale valeur de CorianderPHP reste ce que le projet m'apprend en le développant. Je comprends aujourd'hui beaucoup mieux la manière dont une requête traverse un framework, comment les responsabilités peuvent être séparées et pourquoi certaines abstractions existent.

Le projet m'a aussi appris à être plus attentif à la complexité. Une abstraction peut rendre une base de code plus claire lorsqu'elle résout un problème réel. Introduite trop tôt, elle peut au contraire rendre un petit projet plus difficile à comprendre et à faire évoluer.

CorianderPHP reste un projet expérimental et continue de me servir de terrain de recherche. J'y teste des idées autour de l'architecture, des tests, de l'outillage et de l'automatisation. Le framework m'est utile pour mes propres projets, mais sa principale valeur reste tout ce que sa conception me permet d'apprendre.