From 0409bb1327c7dfcb50a0ed751953a9f06fee3feb Mon Sep 17 00:00:00 2001 From: skycel Date: Wed, 4 Feb 2026 17:06:28 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Story=203.2:=20router=20php=20urls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .htaccess | 7 +++ docs/stories/3.2.router-php-urls.md | 66 +++++++++++++++-------------- includes/router.php | 38 +++++++++++++++++ index.php | 15 ++++++- pages/404.php | 23 ++++++++++ pages/about.php | 16 +++++++ pages/contact.php | 16 +++++++ pages/project-single.php | 24 +++++++++++ pages/projects.php | 16 +++++++ pages/skills.php | 16 +++++++ tests/router.test.php | 33 +++++++++++++++ tests/router.test.ps1 | 15 +++++++ tests/run.ps1 | 2 + 13 files changed, 253 insertions(+), 34 deletions(-) create mode 100644 .htaccess create mode 100644 includes/router.php create mode 100644 pages/404.php create mode 100644 pages/about.php create mode 100644 pages/contact.php create mode 100644 pages/project-single.php create mode 100644 pages/projects.php create mode 100644 pages/skills.php create mode 100644 tests/router.test.php create mode 100644 tests/router.test.ps1 diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..8197699 --- /dev/null +++ b/.htaccess @@ -0,0 +1,7 @@ +RewriteEngine On +RewriteBase / + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d + +RewriteRule ^(.*)$ index.php [QSA,L] \ No newline at end of file diff --git a/docs/stories/3.2.router-php-urls.md b/docs/stories/3.2.router-php-urls.md index d726103..c435b3f 100644 --- a/docs/stories/3.2.router-php-urls.md +++ b/docs/stories/3.2.router-php-urls.md @@ -2,7 +2,7 @@ ## Status -Ready for Dev +review ## Story @@ -21,34 +21,34 @@ Ready for Dev ## Tasks / Subtasks -- [] **Task 1 : Créer le router PHP** (AC: 2, 6) - - [] Créer `includes/router.php` - - [] Implémenter la classe Router - - [] Méthode add() pour ajouter des routes - - [] Méthode resolve() pour matcher une URL - - [] Méthode dispatch() pour exécuter la route +- [x] **Task 1 : Créer le router PHP** (AC: 2, 6) + - [x] Créer `includes/router.php` + - [x] Implémenter la classe Router + - [x] Méthode add() pour ajouter des routes + - [x] Méthode resolve() pour matcher une URL + - [x] Méthode dispatch() pour exécuter la route -- [] **Task 2 : Configurer les routes** (AC: 3, 4) - - [] Route `/` → pages/home.php - - [] Route `/projets` → pages/projects.php - - [] Route `/projet/{slug}` → pages/project-single.php - - [] Route `/competences` → pages/skills.php - - [] Route `/a-propos` → pages/about.php - - [] Route `/contact` → pages/contact.php +- [x] **Task 2 : Configurer les routes** (AC: 3, 4) + - [x] Route `/` → pages/home.php + - [x] Route `/projets` → pages/projects.php + - [x] Route `/projet/{slug}` → pages/project-single.php + - [x] Route `/competences` → pages/skills.php + - [x] Route `/a-propos` → pages/about.php + - [x] Route `/contact` → pages/contact.php -- [] **Task 3 : Créer la page 404** (AC: 5) - - [] Créer `pages/404.php` - - [] Design cohérent avec le site - - [] Lien retour vers l'accueil +- [x] **Task 3 : Créer la page 404** (AC: 5) + - [x] Créer `pages/404.php` + - [x] Design cohérent avec le site + - [x] Lien retour vers l'accueil -- [] **Task 4 : Configurer le serveur** (AC: 1) - - [] Créer `.htaccess` pour Apache - - [] Documenter la config nginx équivalente +- [x] **Task 4 : Configurer le serveur** (AC: 1) + - [x] Créer `.htaccess` pour Apache + - [x] Documenter la config nginx équivalente -- [] **Task 5 : Mettre à jour index.php** - - [] Inclure le router - - [] Définir toutes les routes - - [] Appeler dispatch() +- [x] **Task 5 : Mettre à jour index.php** + - [x] Inclure le router + - [x] Définir toutes les routes + - [x] Appeler dispatch() ## Dev Notes @@ -215,36 +215,38 @@ if (!$project) { ## Dev Agent Record ### Agent Model Used -Claude Opus 4.5 (claude-opus-4-5-20251101) +GPT-5 Codex ### File List | File | Action | Description | |------|--------|-------------| -| `includes/router.php` | Created | Router PHP simple (43 lignes) | +| `includes/router.php` | Created | Router PHP simple | | `index.php` | Modified | Converti en front controller | | `.htaccess` | Created | Réécriture URLs Apache | -| `pages/home.php` | Created | Page d'accueil | | `pages/projects.php` | Created | Page liste projets (placeholder) | | `pages/project-single.php` | Created | Page projet individuel | | `pages/skills.php` | Created | Page compétences (placeholder) | | `pages/about.php` | Created | Page à propos (placeholder) | | `pages/contact.php` | Created | Page contact (placeholder) | | `pages/404.php` | Created | Page erreur 404 | +| `tests/router.test.php` | Created | Tests du router | +| `tests/router.test.ps1` | Created | Vérif .htaccess | +| `tests/run.ps1` | Modified | Ajout tests router | ### Completion Notes -- Router PHP léger (43 lignes < 50 requis) +- Router PHP léger (< 50 lignes) - Support des paramètres dynamiques {slug} - Trailing slash normalisé automatiquement - 404 pour routes inconnues - Pages placeholder créées pour futures stories -- Tous les tests du router passent (8/8) +- Tests: `powershell -ExecutionPolicy Bypass -File tests/run.ps1` ### Debug Log References -Aucun problème rencontré. +Aucun problème bloquant. ## Change Log | Date | Version | Description | Author | |------|---------|-------------|--------| | 2026-01-22 | 0.1 | Création initiale | Sarah (PO) | -| 2026-01-23 | 1.0 | Implémentation complète | James (Dev) | +| 2026-02-04 | 1.0 | Implémentation complète | Amelia | diff --git a/includes/router.php b/includes/router.php new file mode 100644 index 0000000..cf335a7 --- /dev/null +++ b/includes/router.php @@ -0,0 +1,38 @@ +routes['#^' . $regex . '$#'] = $handler; + return $this; + } + + public function resolve(string $uri): array + { + $path = parse_url($uri, PHP_URL_PATH); + $path = rtrim($path, '/') ?: '/'; + + foreach ($this->routes as $regex => $handler) { + if (preg_match($regex, $path, $matches)) { + array_shift($matches); + return [$handler, $matches]; + } + } + + return ['pages/404.php', []]; + } + + public function dispatch(): void + { + $uri = $_SERVER['REQUEST_URI'] ?? '/'; + [$handler, $params] = $this->resolve($uri); + $GLOBALS['routeParams'] = $params; + require __DIR__ . '/../' . $handler; + } +} \ No newline at end of file diff --git a/index.php b/index.php index 0aad55c..2124e42 100644 --- a/index.php +++ b/index.php @@ -1,4 +1,15 @@ add('/', 'pages/home.php') + ->add('/projets', 'pages/projects.php') + ->add('/projet/{slug}', 'pages/project-single.php') + ->add('/competences', 'pages/skills.php') + ->add('/a-propos', 'pages/about.php') + ->add('/contact', 'pages/contact.php'); + +$router->dispatch(); \ No newline at end of file diff --git a/pages/404.php b/pages/404.php new file mode 100644 index 0000000..58af6ea --- /dev/null +++ b/pages/404.php @@ -0,0 +1,23 @@ + + +
+
+

404

+

+ Oups ! Cette page n'existe pas. +

+ + Retour à l'accueil + +
+
+ + \ No newline at end of file diff --git a/pages/about.php b/pages/about.php new file mode 100644 index 0000000..df3f89e --- /dev/null +++ b/pages/about.php @@ -0,0 +1,16 @@ + + +
+
+

Me Découvrir

+

Le parcours arrive bientôt.

+
+
+ + \ No newline at end of file diff --git a/pages/contact.php b/pages/contact.php new file mode 100644 index 0000000..47b3815 --- /dev/null +++ b/pages/contact.php @@ -0,0 +1,16 @@ + + +
+
+

Contact

+

Le formulaire arrive bientôt.

+
+
+ + \ No newline at end of file diff --git a/pages/project-single.php b/pages/project-single.php new file mode 100644 index 0000000..f9ac679 --- /dev/null +++ b/pages/project-single.php @@ -0,0 +1,24 @@ + + +
+
+

+

Page projet en construction.

+
+
+ + \ No newline at end of file diff --git a/pages/projects.php b/pages/projects.php new file mode 100644 index 0000000..9fc2183 --- /dev/null +++ b/pages/projects.php @@ -0,0 +1,16 @@ + + +
+
+

Projets

+

La liste des projets arrive bientôt.

+
+
+ + \ No newline at end of file diff --git a/pages/skills.php b/pages/skills.php new file mode 100644 index 0000000..89d347d --- /dev/null +++ b/pages/skills.php @@ -0,0 +1,16 @@ + + +
+
+

Compétences

+

Le détail des compétences arrive bientôt.

+
+
+ + \ No newline at end of file diff --git a/tests/router.test.php b/tests/router.test.php new file mode 100644 index 0000000..5c7c8c3 --- /dev/null +++ b/tests/router.test.php @@ -0,0 +1,33 @@ +add('/', 'pages/home.php') + ->add('/projets', 'pages/projects.php') + ->add('/projet/{slug}', 'pages/project-single.php'); + +[$handler, $params] = $router->resolve('/'); +assertTrue($handler === 'pages/home.php', 'home route failed'); + +[$handler, $params] = $router->resolve('/projets'); +assertTrue($handler === 'pages/projects.php', 'projects route failed'); + +[$handler, $params] = $router->resolve('/projet/ecommerce-xyz'); +assertTrue($handler === 'pages/project-single.php', 'project route failed'); +assertTrue(($params[0] ?? '') === 'ecommerce-xyz', 'slug param failed'); + +[$handler, $params] = $router->resolve('/unknown'); +assertTrue($handler === 'pages/404.php', '404 route failed'); + +[$handler, $params] = $router->resolve('/projets/'); +assertTrue($handler === 'pages/projects.php', 'trailing slash failed'); + +fwrite(STDOUT, "OK\n"); \ No newline at end of file diff --git a/tests/router.test.ps1 b/tests/router.test.ps1 new file mode 100644 index 0000000..bea2b49 --- /dev/null +++ b/tests/router.test.ps1 @@ -0,0 +1,15 @@ +$ErrorActionPreference = 'Stop' + +function Assert-True { + param( + [bool]$Condition, + [string]$Message + ) + if (-not $Condition) { throw $Message } +} + +Assert-True (Test-Path '.htaccess') 'Missing .htaccess' +$ht = Get-Content -Raw '.htaccess' +Assert-True ($ht -match 'RewriteRule') 'Missing RewriteRule in .htaccess' + +'OK' \ No newline at end of file diff --git a/tests/run.ps1 b/tests/run.ps1 index 40ff60c..5743549 100644 --- a/tests/run.ps1 +++ b/tests/run.ps1 @@ -8,5 +8,7 @@ $here = Split-Path -Parent $MyInvocation.MyCommand.Path & (Join-Path $here 'cta.test.ps1') & (Join-Path $here 'home.test.ps1') & (Join-Path $here 'quicknav.test.ps1') +& (Join-Path $here 'router.test.ps1') php (Join-Path $here 'projects.test.php') +php (Join-Path $here 'router.test.php') 'OK' \ No newline at end of file