6.7 KiB
6.7 KiB
Story 3.2: Router PHP et URLs Propres
Status
Ready for Dev
Story
As a visiteur, I want des URLs lisibles et propres pour accéder aux projets, so that je comprends le contenu de la page avant même de cliquer et améliore le SEO.
Acceptance Criteria
- Un fichier
.htaccess(Apache) ou config nginx redirige toutes les requêtes versindex.php(front controller) - Un router PHP simple parse l'URL et route vers le bon fichier/action
- Les URLs des projets sont au format
/projet/{slug}(ex:/projet/site-ecommerce-xyz) - Les autres pages gardent des URLs simples :
/projets,/competences,/a-propos,/contact - Une route 404 personnalisée gère les URLs inconnues
- Le router est léger (<50 lignes de code) et sans dépendance externe
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
- [] Créer
-
[] 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
- [] Route
-
[] 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
- [] Créer
-
[] Task 4 : Configurer le serveur (AC: 1)
- [] Créer
.htaccesspour Apache - [] Documenter la config nginx équivalente
- [] Créer
-
[] Task 5 : Mettre à jour index.php
- [] Inclure le router
- [] Définir toutes les routes
- [] Appeler dispatch()
Dev Notes
Router PHP (includes/router.php)
<?php
/**
* Router simple pour URLs propres
* < 50 lignes de code
*/
class Router
{
private array $routes = [];
public function add(string $pattern, string $handler): self
{
// Convertit {param} en regex (?P<param>[^/]+)
$regex = preg_replace('/\{(\w+)\}/', '([^/]+)', $pattern);
$regex = '#^' . $regex . '$#';
$this->routes[$regex] = $handler;
return $this;
}
public function resolve(string $uri): array
{
$uri = parse_url($uri, PHP_URL_PATH);
$uri = rtrim($uri, '/') ?: '/';
foreach ($this->routes as $regex => $handler) {
if (preg_match($regex, $uri, $matches)) {
array_shift($matches); // Enlève le match complet
return [$handler, $matches];
}
}
return ['pages/404.php', []];
}
public function dispatch(): void
{
$uri = $_SERVER['REQUEST_URI'] ?? '/';
[$handler, $params] = $this->resolve($uri);
// Rend les paramètres accessibles
$GLOBALS['routeParams'] = $params;
require __DIR__ . '/../' . $handler;
}
}
Point d'entrée (index.php)
<?php
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/config.php';
require_once __DIR__ . '/includes/functions.php';
require_once __DIR__ . '/includes/router.php';
session_start();
$router = new Router();
$router
->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();
Configuration .htaccess (Apache)
RewriteEngine On
RewriteBase /
# Ne pas réécrire les fichiers et dossiers existants
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Rediriger tout vers index.php
RewriteRule ^(.*)$ index.php [QSA,L]
Configuration Nginx
location / {
try_files $uri $uri/ /index.php?$query_string;
}
Page 404 (pages/404.php)
<?php
http_response_code(404);
$pageTitle = 'Page non trouvée';
$currentPage = '';
include_template('header', compact('pageTitle'));
include_template('navbar', compact('currentPage'));
?>
<main class="min-h-screen flex items-center justify-center">
<div class="container-content text-center py-20">
<h1 class="text-display text-primary mb-4">404</h1>
<p class="text-xl text-text-secondary mb-8">
Oups ! Cette page n'existe pas.
</p>
<a href="/" class="btn-primary">
Retour à l'accueil
</a>
</div>
</main>
<?php include_template('footer'); ?>
Récupérer les paramètres de route
// Dans pages/project-single.php
$slug = $GLOBALS['routeParams'][0] ?? null;
$project = getProjectBySlug($slug);
if (!$project) {
http_response_code(404);
include __DIR__ . '/404.php';
exit;
}
Structure des URLs
| URL | Page | Paramètres |
|---|---|---|
/ |
home.php | - |
/projets |
projects.php | - |
/projet/ecommerce-xyz |
project-single.php | slug=ecommerce-xyz |
/competences |
skills.php | - |
/a-propos |
about.php | - |
/contact |
contact.php | - |
/nimporte-quoi |
404.php | - |
Testing
- []
/affiche la page d'accueil - []
/projetsaffiche la liste des projets - []
/projet/ecommerce-xyzaffiche le projet correspondant - []
/projet/inexistantaffiche la page 404 - []
/page-inexistanteaffiche la page 404 - [] Les assets (/assets/css/...) sont toujours accessibles
- [] Pas de boucle de redirection
Dev Agent Record
Agent Model Used
Claude Opus 4.5 (claude-opus-4-5-20251101)
File List
| File | Action | Description |
|---|---|---|
includes/router.php |
Created | Router PHP simple (43 lignes) |
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 |
Completion Notes
- Router PHP léger (43 lignes < 50 requis)
- 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)
Debug Log References
Aucun problème rencontré.
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) |