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.
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.phpliste chaque route sous forme d’appel, par exempleget('/login', 'frontend/pages/login.html')oupost('/api/login', 'api/login.php').router.phpdé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 avecfetch. - API.
api/login.php,signup.php,logout.php,user_details.php,clients.php,hello-world.phpetrandom-image.phplisent des corps JSON et répondent en JSON. Les fonctions communes (loginUser,registerUser,isAdmin, aides CSRF) se trouvent dansapi/utils/utils.php. - Données.
config.phpouvre une connexion PDO vers MySQL/MariaDB.db/1create.sqlcréeClients(id, nom d’utilisateur et courriel uniques, empreinte du mot de passe,role ENUM('admin','client'), horodatages) ;db/db.pyexé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.javagarde 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.
- 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
- Remplir DB_HOST et les variables MYSQL_* indiquées dans le README, puis lancer docker-compose up -d --build.
- 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.
- Ouvrir http://localhost:8001/signup, créer un compte, puis se connecter sur /login.
- Avec un compte admin, ouvrir /admin pour voir la table Clients chargée par /api/clients.
- 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 :
- 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.
- Structure du projet. Une arborescence commentée de
api/,db/,frontend/, des fichiers de routage et des fichiers de déploiement. - Base de données. Les variables du
.envet les commandes d’initialisation dedb.py. - 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.
- Frontend. Une visite des DevTools (Elements, Console, Sources, Network, Performance, Memory, Application, Security) avec captures et raccourcis.
- 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. - 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 :
- Si la cible est une chaîne sans
.phpni.html, elle ajoute.php. - 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
/. - Une route qui finit par
*accepte tout chemin plus long et transmet le reste comme un seul argument. - 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. - En cas de correspondance, elle appelle la fonction anonyme avec les valeurs capturées, ou fait
include_oncedu 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.apiethello-worldcorrespondent et$languecapturees;hello-world.phpest inclus à l’intérieur deroute(): il voit$langue = 'es', litnometprenomdans le corps (ou prend le nom d’utilisateur connecté commeprenom) 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.

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.phpcode en dur les identifiants locaux (tch099-db,user/password) et ne lit jamais l’environnement ;$isCloudEnvest calculé sans être utilisé. Le guide Azure contourne le problème en écrivant un.envpar SSH, mais PHP ne le charge pas.config.phpaffiche « Database connection successful. » à chaque inclusion. Les réponses de l’API commencent donc par ce texte avant le JSON, ce qui fait échouerresponse.json()dans les pages.api/signup.phpprend leroledans le corps de la requête : n’importe qui peut créer un compteadmin, puis ouvrir/adminet/api/clients.api/clients.phprenvoieSELECT * FROM Clients, empreintes de mots de passe comprises.router.phpenvoieAccess-Control-Allow-Origin: *et activedisplay_errorspour chaque requête.- La section « ajouter une route » du README montre un format de tableau
$routes['/chemin'] = [...]queroutes.phpn’utilise pas ; le vrai format est celui des appelsget()/post()décrits plus haut. - Les aides CSRF de
utils.phpne sont appelées par aucun script de l’API.
10. Sources
- Dépôt GitHub
- Site de documentation
- README
router.phproutes.phpapi/utils/utils.phpapi/hello-world.phpconfig.php.github/workflows/main_tch099antoine.ymldocker-compose.yml
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
| Workflow | Déclencheurs | Tâches | Publie vers |
|---|---|---|---|
Build and deploy PHP app to Azure Web App - tch099antoine.github/workflows/main_tch099antoine.yml | push, lancement manuel |
| — |
Deploy static content to Pages.github/workflows/static.yml | Runs, on, pushes, targeting, the, default |
| 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
| Paquet | Version | Usage | Rôle | Registre |
|---|---|---|---|---|
appcompat | — | exécution | — | JVM (Gradle) |
material | — | exécution | — | JVM (Gradle) |
activity | — | exécution | — | JVM (Gradle) |
constraintlayout | — | exécution | — | JVM (Gradle) |
okhttp | — | exécution | Client HTTP | JVM (Gradle) |
okio | — | exécution | — | JVM (Gradle) |
dotenv | — | exécution | Configuration | JVM (Gradle) |
loggingInterceptor | — | exécution | Journalisation 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
- antoinebou12. (2026, 5 novembre). TCH099. [Software repository] github.com.
- antoinebou12. (2026, 5 novembre). TCH099: README. [Repository documentation] github.com.
- antoinebou12. (2026, 5 novembre). routes.php. [Source file] github.com.
- antoinebou12. (2026, 5 novembre). router.php. [Source file] github.com.
- antoinebou12. (2026, 5 novembre). README.md. [Source file] github.com.
- 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.