Aller au contenu
Table des matières

TCH099

TCH099 est une petite application web PHP conçue comme démonstration à déployer sur Azure. Elle sert quelques pages HTML, une API JSON d’inscription, de connexion, de déconnexion et de détails de l’utilisateur, ainsi qu’une page d’administration qui liste tous les comptes ; une application Android parle à la même API. Son site de documentation s’adresse aux étudiants qui commencent un projet d’équipe : histoires utilisateur, référence de l’API, initiation aux DevTools du navigateur, configuration Android et étapes de déploiement Azure.

Application webAPIDocker
PHP 8MariaDBPDOnginxApacheAzure App ServiceGitHub ActionsAndroidOkHttp
  • Mis à jour

1. Aperçu

Il n’y a pas de cadriciel. Deux fichiers font le routage, quelques scripts de api/ font le travail et une table Clients contient les comptes.

  • Routes. routes.php liste chaque route sous forme d’appel, par exemple get('/login', 'frontend/pages/login.html') ou post('/api/login', 'api/login.php'). router.php définit ces fonctions et compare la requête.
  • Pages. frontend/pages/ contient du HTML statique (accueil, connexion, inscription, hello world, image aléatoire, admin, 403, 404, 50x) qui appelle l’API avec fetch.
  • API. api/login.php, signup.php, logout.php, user_details.php, clients.php, hello-world.php et random-image.php lisent des corps JSON et répondent en JSON. Les fonctions communes (loginUser, registerUser, isAdmin, aides CSRF) se trouvent dans api/utils/utils.php.
  • Données. config.php ouvre une connexion PDO vers MySQL/MariaDB. db/1create.sql crée Clients (id, nom d’utilisateur et courriel uniques, empreinte du mot de passe, role ENUM('admin','client'), horodatages) ; db/db.py exécute les trois fichiers SQL avec les identifiants du .env.
  • Mobile. MyMobileApp/ est un projet Android Studio avec des activités de connexion, d’inscription et de détails de l’utilisateur. HttpClient.java garde un seul client OkHttp avec un gestionnaire de témoins, pour que le témoin de session PHP survive d’un appel à l’autre.

2. Cas d’utilisation

Application web de démonstration en PHP et MariaDB, avec client Android, pour enseigner le routage, les sessions et Azure.

Diagramme de cas d’utilisation de TCH099 : Créer un compte; Se connecter (session); Afficher une image au hasard; Saluer selon la langue; Lister les clients (admin)TCH099Créer un compteSe connecter (session)Afficher une image au hasardSaluer selon la langueLister les clients (admin)Utilisateur web/mobileAdministrateur«système»Base MariaDB
Acteurs à gauche (personnes) et à droite (systèmes externes) ; chaque ovale est un cas d’utilisation appuyé par les fichiers cités ci-dessous.
Créer un compte
Utilisateur web/mobileBase MariaDB

La page d'inscription ou SignupActivity sur Android envoie les données à /api/signup ; api/signup.php appelle registerUser(), qui ajoute le compte à la table Clients créée par db/1create.sql.

Se connecter (session)
Utilisateur web/mobileBase MariaDB

login.html et LoginActivity envoient le nom d'utilisateur et le mot de passe à /api/login ; api/login.php les vérifie avec loginUser() et ouvre une session PHP, que /api/logout ferme.

Afficher une image au hasard
Utilisateur web/mobile

random-image.html appelle /api/random-image, qui renvoie l'URL d'un chat venant de TheCatAPI ou celle de thispersondoesnotexist.com selon imageType.

Saluer selon la langue
Utilisateur web/mobile

/api/hello-world/$langue répond par une salutation en français, anglais, espagnol ou allemand ; c'est l'exemple d'API minimal.

Lister les clients (admin)
AdministrateurBase MariaDB

La route /admin ne sert admin.html que si isAdmin() est vrai ; la page charge /api/clients, qui lit la table Clients lorsque la session a le rôle admin.

2.1. Scénario : Lancer la démo en local jusqu'à la page admin

  1. Remplir DB_HOST et les variables MYSQL_* indiquées dans le README, puis lancer docker-compose up -d --build.
  2. Charger le schéma et les données avec python3 db/db.py, ou importer db/1create.sql, 2contraines.sql et 3insert.sql dans phpMyAdmin sur le port 8060.
  3. Ouvrir http://localhost:8001/signup, créer un compte, puis se connecter sur /login.
  4. Avec un compte admin, ouvrir /admin pour voir la table Clients chargée par /api/clients.
  5. Pousser sur main pour que .github/workflows/main_tch099antoine.yml zippe l'application et la déploie sur Azure Web App.

Fichiers consultésREADME.mdroutes.phpdocker-compose.ymlDockerfileapi/login.phpapi/clients.phpapi/random-image.phpdb/db.pyMyMobileApp/MyApplication/app/build.gradle.kts.github/workflows/main_tch099antoine.ymlapi/utils/utils.php

3. Ce que couvre le site de documentation

Le site GitHub Pages est le fichier docs/index.html, publié par le flux static.yml. Il suit les étapes d’un projet d’équipe :

  1. Planifier. Une épopée, « gestion des comptes et des profils utilisateurs », découpée en sept histoires utilisateur (inscription, connexion, déconnexion, message « Hello World » avec le nom de l’utilisateur, image aléatoire, détails de l’utilisateur, connexion administrateur), chacune avec ses sous-tâches : concevoir le formulaire, écrire la logique serveur, chiffrer les mots de passe, tester.
  2. Structure du projet. Une arborescence commentée de api/, db/, frontend/, des fichiers de routage et des fichiers de déploiement.
  3. Base de données. Les variables du .env et les commandes d’initialisation de db.py.
  4. API. Point d’accès, méthode, corps de requête et réponse pour chaque route, avec un diagramme de séquence PlantUML par appel.
  5. Frontend. Une visite des DevTools (Elements, Console, Sources, Network, Performance, Memory, Application, Security) avec captures et raccourcis.
  6. Mobile. Installation d’Android Studio, URL de l’API dans buildConfigField, fichiers de mise en page, et pourquoi l’émulateur joint l’hôte à 10.0.2.2.
  7. Déploiement. Un diagramme de déploiement et sept étapes dans le portail Azure, de la création de la Web App et de la base jusqu’à la connexion SSH au conteneur et à l’écriture de son .env. Le README ajoute une huitième étape sur les liens privés.

4. Architecture

flowchart LR
  B["Pages du navigateur<br/>frontend/pages/*.html"] -->|fetch JSON| W
  A["Application Android<br/>OkHttp + témoins"] -->|JSON en HTTPS| W
  subgraph W["Serveur web"]
    N["nginx sur Azure<br/>try_files → routes.php"]
    H["Apache dans Docker<br/>.htaccess → router.php"]
  end
  W --> R["router.php + routes.php<br/>la première route qui correspond gagne"]
  R -->|route HTML| P["Page statique"]
  R -->|route API| S["api/*.php"]
  S --> U["api/utils/utils.php<br/>session et authentification"]
  U --> C["config.php<br/>connexion PDO"]
  C --> D[("MariaDB / MySQL<br/>table Clients")]
  S -->|random-image| X["TheCatAPI"]

Deux serveurs web mènent au même routeur. L’image Docker est php:8.1-apache ; son .htaccess envoie toute requête qui n’est pas un fichier statique (image, CSS, JS, SVG, ZIP ou PDF) à router.php, qui se termine par require_once 'routes.php'. Sur Azure App Service (PHP 8.2), nginx sert les fichiers existants et se rabat sur routes.php, qui commence par require_once 'router.php'. Dans les deux cas, les deux fichiers sont chargés et la liste des routes s’exécute une fois.

5. Comment le routeur reconnaît une requête

get(), post(), put(), patch(), delete() et any() vérifient chacune la méthode HTTP, puis appellent route($route, $target). Comme routes.php les appelle l’une après l’autre, la première route qui correspond inclut sa cible et appelle exit() ; rien de ce qui suit ne s’exécute. route() procède ainsi :

  1. Si la cible est une chaîne sans .php ni .html, elle ajoute .php.
  2. Elle nettoie l’URI, retire la barre oblique finale et la chaîne de requête, puis découpe la route et l’URI sur /.
  3. Une route qui finit par * accepte tout chemin plus long et transmet le reste comme un seul argument.
  4. Sinon, les deux doivent avoir le même nombre de segments. Un segment qui commence par $ copie le segment de l’URI dans une variable PHP de ce nom ($$route_part = …) ; tout autre segment doit correspondre exactement.
  5. En cas de correspondance, elle appelle la fonction anonyme avec les valeurs capturées, ou fait include_once du fichier, puis s’arrête.

La dernière ligne de routes.php est any('/404', …), et route() renvoie directement la page 404 pour /404 sans rien comparer. Toute requête qui a traversé les routes précédentes sans correspondre reçoit donc la page 404.

Exemple détaillé, POST /api/hello-world/es avec {"nom": "Tremblay", "prenom": "Ana"} :

  • les appels get() l’ignorent (mauvaise méthode) ; post('/api/login', …) et les autres échouent sur le nombre de segments ou leur texte ;
  • post('/api/hello-world/$langue', 'api/hello-world.php') a trois segments, comme l’URI. api et hello-world correspondent et $langue capture es ;
  • hello-world.php est inclus à l’intérieur de route() : il voit $langue = 'es', lit nom et prenom dans le corps (ou prend le nom d’utilisateur connecté comme prenom) et renvoie {"nom": "Tremblay", "prenom": "Ana", "langue": "es", "message": "Hola Mundo Tremblay Ana!"}.

La même URL en GET se comporte autrement : la branche GET de hello-world.php remplace $langue par $_GET['langue'] ?? 'fr'. Le segment du chemin est ignoré et la réponse est en français, sauf si l’on ajoute ?langue=es.

6. Connexion et rôles

sequenceDiagram
  participant Page as login.html
  participant Router as routes.php
  participant Login as api/login.php
  participant Utils as utils.php
  participant DB as Table Clients
  Page->>Router: POST /api/login {"username","password"}
  Router->>Login: include (première route qui correspond)
  Login->>Utils: loginUser(username, password)
  Utils->>DB: SELECT * FROM Clients WHERE username = ?
  DB-->>Utils: ligne avec l’empreinte du mot de passe et le rôle
  Utils->>Utils: password_verify(), puis $_SESSION['user_details'] = {username, email, role}
  Login-->>Page: {"status":"success","redirect":"/home"}
  Page->>Router: GET /admin
  Router->>Utils: isAdmin() lit le rôle dans la session
  Router-->>Page: admin.html, ou 403.html pour un client

registerUser calcule l’empreinte du mot de passe avec password_hash(PASSWORD_DEFAULT) et n’accepte que les rôles admin et client. Chaque requête passe par une requête préparée. C’est la session, et non un jeton, qui porte l’identité ; d’où le gestionnaire de témoins du client Android.

7. Travail d’équipe et livraison

Le guide du développeur du README donne les règles de travail de l’équipe : comment ajouter une route, une page et un point d’API, et cinq bonnes pratiques (séparer les préoccupations, valider les entrées côté serveur, renvoyer des erreurs HTTP adaptées, réutiliser utils.php, utiliser des requêtes préparées et échapper le HTML). Les histoires utilisateur du site de documentation servent aussi de carnet de produit.

flowchart LR
  Dev["Push sur main"] --> CI["main_tch099antoine.yml<br/>tâche build"]
  CI --> Z["setup-php 8.2, composer si présent,<br/>zip de tout sauf MyMobileApp/"]
  Z --> DEP["tâche deploy<br/>azure/login + webapps-deploy"]
  DEP --> AZ["Web App Azure tch099antoine<br/>démarrage : copie de nginx.conf, rechargement de nginx"]
  Dev --> PG["static.yml"] --> GP["GitHub Pages<br/>dossier docs/"]

Deux flux s’exécutent à chaque push sur main. main_tch099antoine.yml compresse l’application PHP (le projet Android est exclu) et la déploie sur l’App Service tch099antoine avec azure/webapps-deploy. static.yml publie docs/ sur GitHub Pages. Côté Azure, la commande de démarrage copie le nginx.conf du dépôt par-dessus le site par défaut et recharge nginx, pour que les chemins inconnus arrivent à routes.php.

Centre de déploiement du portail Azure pour la Web App tch099antoine, relié à la branche main du dépôt GitHub avec GitHub Actions comme fournisseur de build et PHP 8.2
Centre de déploiement Azure, tiré du guide du dépôt : source GitHub, branche main, fournisseur de build GitHub Actions, PHP 8.2.

8. Démarrage

Créez un .env avec les variables du README (DB_HOST=tch099-db, MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD, MYSQL_ROOT_PASSWORD), puis lancez la pile :

docker compose up -d --build
cd db
python3 -m pip install mysql-connector-python python-dotenv
python3 db.py

Compose démarre trois conteneurs sur un même réseau pont : web (l’image Apache, port 8001, avec le dépôt monté dans /var/www/html), db (MariaDB, port 3306, données dans le volume dbdata) et phpmyadmin (port 8060). Ouvrez http://localhost:8001.

9. Limites

  • config.php code en dur les identifiants locaux (tch099-db, user/password) et ne lit jamais l’environnement ; $isCloudEnv est calculé sans être utilisé. Le guide Azure contourne le problème en écrivant un .env par SSH, mais PHP ne le charge pas.
  • config.php affiche « Database connection successful. » à chaque inclusion. Les réponses de l’API commencent donc par ce texte avant le JSON, ce qui fait échouer response.json() dans les pages.
  • api/signup.php prend le role dans le corps de la requête : n’importe qui peut créer un compte admin, puis ouvrir /admin et /api/clients.
  • api/clients.php renvoie SELECT * FROM Clients, empreintes de mots de passe comprises.
  • router.php envoie Access-Control-Allow-Origin: * et active display_errors pour chaque requête.
  • La section « ajouter une route » du README montre un format de tableau $routes['/chemin'] = [...] que routes.php n’utilise pas ; le vrai format est celui des appels get() / post() décrits plus haut.
  • Les aides CSRF de utils.php ne sont appelées par aucun script de l’API.

10. Sources

11. Pipeline CI/CD

2 workflows GitHub Actions, 3 tâches. Chaque boîte est une tâche ; les boîtes à double bord publient ou déploient, et les flèches suivent les dépendances needs.

Mermaid Déroulement du pipeline
flowchart LR
  subgraph wf0["Build and deploy PHP app to Azure Web App -…"]
    direction LR
    wf0_build[["build<br/>ubuntu-latest"]]
    wf0_deploy[["deploy<br/>ubuntu-latest"]]
  end
  subgraph wf1["Deploy static content to Pages"]
    direction LR
    wf1_deploy[["deploy<br/>ubuntu-latest"]]
  end
  t0(["push · lancement manuel"])
  t1(["Runs · on · pushes · targeting · the · defa…"])
  d0[("GitHub Pages")]
  t0 --> wf0_build
  wf0_build --> wf0_deploy
  t1 --> wf1_deploy
  wf1_deploy -.-> d0
Workflows, déclencheurs, tâches et cibles
WorkflowDéclencheursTâchesPublie vers
Build and deploy PHP app to Azure Web App - tch099antoine
.github/workflows/main_tch099antoine.yml
push, lancement manuel
  • build ubuntu-latest
    actions/checkout · Setup PHP · Check if composer.json exists · Run composer install if composer.json exists · Zip artifact for deployment · Upload artifact for deployment job
  • deploy ← build ubuntu-latest
    Download artifact from build job · Unzip artifact for deployment · Login to Azure · Deploy to Azure Web App
—
Deploy static content to Pages
.github/workflows/static.yml
Runs, on, pushes, targeting, the, default
  • deploy ubuntu-latest
    Checkout · Setup Pages · Upload artifact · Deploy to GitHub Pages
GitHub Pages

Généré à partir du code au commit a7323c8.

12. Schéma de déploiement

Mermaid Schéma de déploiement
flowchart LR
  repo[("Dépôt GitHub")]
  ci[["GitHub Actions<br/>2 workflows"]]
  image["Image Docker<br/>FROM php:8.1-apache"]
  pub_GitHub_Pages[("GitHub Pages")]
  subgraph compose["Docker Compose"]
    svc_web["web"]
    svc_db["db"]
    svc_phpmyadmin["phpmyadmin"]
  end
  repo -->|"push ou demande de fusion"| ci
  ci -->|"docker build"| image
  ci -->|"publie"| pub_GitHub_Pages
  image -->|"docker compose up"| compose

13. Dépendances externes

8 paquets déclarés (8 à l’exécution, 0 pour le développement) dans MyMobileApp/MyApplication/app/build.gradle.kts.

Tableau des paquets 8
Dépendances externes
PaquetVersionUsageRôleRegistre
appcompat—exécution—JVM (Gradle)
material—exécution—JVM (Gradle)
activity—exécution—JVM (Gradle)
constraintlayout—exécution—JVM (Gradle)
okhttp—exécutionClient HTTPJVM (Gradle)
okio—exécution—JVM (Gradle)
dotenv—exécutionConfigurationJVM (Gradle)
loggingInterceptor—exécutionJournalisation et observabilitéJVM (Gradle)

14. Glossaire

Dépôt

Les fichiers source, la configuration et l’historique versionné d’un projet.

README

Le document d’entrée du dépôt, qui présente généralement son rôle et son installation.

Dépendance

Une bibliothèque, un outil ou un service nécessaire à la compilation ou à l’exécution d’un composant.

API

Une interface par laquelle des composants logiciels échangent des requêtes et des résultats. Une API n’utilise pas nécessairement HTTP.

HTTP

Un protocole de requête et réponse utilisé par les navigateurs et services web. Une requête comprend une méthode, un chemin, des en-têtes et parfois un corps.

Session

Un état conservé entre plusieurs requêtes, souvent pour suivre un utilisateur connecté. L’authentification et les permissions restent des vérifications distinctes.

Conteneur

Un environnement de processus isolé créé à partir d’une image. Les données persistantes nécessitent un stockage hors de son système de fichiers temporaire.

Docker Compose

Un format de configuration et un outil pour lancer des conteneurs, réseaux et volumes associés. L’ordre de démarrage ne garantit pas qu’un service est prêt.

Volume et montage de dossier

Un volume est un stockage géré par la plateforme de conteneurs ; un montage de dossier expose un chemin de l’hôte dans un conteneur.

Port publié

Un port de l’hôte associé au port d’un service dans un conteneur. Sa publication peut rendre le service accessible hors du réseau des conteneurs.

Interface en ligne de commande

Une interface textuelle où les commandes et arguments choisissent les opérations. Les codes de retour permettent aux scripts de détecter un succès ou un échec.

Compilation

Les étapes qui transforment les sources et dépendances en programme exécutable ou en livrable distribuable.

Intégration continue

Des vérifications automatiques lors des changements de source, comme la compilation, les tests et l’analyse statique. Elles ne couvrent que ce qu’elles vérifient.

Mermaid

Un langage de diagrammes textuel. Les nœuds et liens décrivent un modèle ; le diagramme n’exécute pas le projet.

Base de données

Un système de stockage et d’interrogation de données structurées. Son schéma, ses règles d’accès et sa persistance déterminent comment l’application utilise ces données.

15. Références

  1. antoinebou12. (2026, 5 novembre). TCH099. [Software repository] github.com.
  2. antoinebou12. (2026, 5 novembre). TCH099: README. [Repository documentation] github.com.
  3. antoinebou12. (2026, 5 novembre). routes.php. [Source file] github.com.
  4. antoinebou12. (2026, 5 novembre). router.php. [Source file] github.com.
  5. antoinebou12. (2026, 5 novembre). README.md. [Source file] github.com.
  6. Boucher, A. (2026, 23 octobre). TCH099 : Un routeur PHP pour les pages et l’API. [Project article] antoineboucher.info.

Les sources sans date de publication indiquent la date de leur dernière vérification, le 5 novembre 2026. Les liens des fichiers conservent la révision inspectée lorsqu’elle est disponible.