Routeur PHP avec pages Markdown

Le routeur analyse l'URL, extrait un mot-clé, cherche un fichier .md correspondant, le convertit en HTML et l'affiche dans un template.

1. Structure des dossiers

mon-site/
├── .htaccess
├── index.php
├── template.html
└── pages/
    ├── accueil.md
    └── contact.md

2. Fichier .htaccess

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

3. Fichier index.php

<?php
// index.php - Routeur principal

define('PAGES_DIR', __DIR__ . '/pages/');

$uri = $_SERVER['REQUEST_URI'];
$uri = parse_url($uri, PHP_URL_PATH);
$uri = trim($uri, '/');
$motCle = empty($uri) ? 'accueil' : explode('/', $uri)[0];

$fichierMD = PAGES_DIR . $motCle . '.md';

if (file_exists($fichierMD)) {
    $contenuMD = file_get_contents($fichierMD);
    $contenuHTML = convertirMarkdown($contenuMD);
} else {
    http_response_code(404);
    $contenuHTML = '<h1>404 - Page non trouvée</h1>';
}

function convertirMarkdown($md) {
    $html = $md;
    $html = preg_replace('/^# (.*)$/m', '<h1>$1</h1>', $html);
    $html = preg_replace('/^## (.*)$/m', '<h2>$1</h2>', $html);
    $html = preg_replace('/^### (.*)$/m', '<h3>$1</h3>', $html);
    $html = preg_replace('/\*\*(.*?)\*\*/', '<strong>$1</strong>', $html);
    $html = preg_replace('/\*(.*?)\*/', '<em>$1</em>', $html);
    $html = preg_replace('/\[(.*?)\]\((.*?)\)/', '<a href="$2">$1</a>', $html);
    $html = preg_replace('/^>\s*(.*)$/m', '<blockquote>$1</blockquote>', $html);
    $html = preg_replace('/^---$/', '<hr>', $html);
    $html = preg_replace('/\n\s*\n/', '</p><p>', $html);
    $html = '<p>' . $html . '</p>';
    $html = preg_replace('/<p>\s*<\/p>/', '', $html);
    return $html;
}

$template = file_get_contents('template.html');
$template = str_replace('{{ CONTENU }}', $contenuHTML, $template);
echo $template;
?>

4. Fichier template.html

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Mon Site</title>
</head>
<body>
    <main>
        {{ CONTENU }}
    </main>
</body>
</html>

5. Exemple pages/accueil.md

# Accueil

Bienvenue.

[Contact](/contact)

6. Exemple pages/contact.md

# Contact

Téléphone : 04 68 00 00 00

7. Traitement de formulaire (AJAX)

À ajouter en haut de index.php.

<?php
$uri = trim($_SERVER['REQUEST_URI'], '/');

if ($uri === 'envoyer-contact' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $nom = $_POST['nom'] ?? '';
    $email = $_POST['email'] ?? '';
    $message = $_POST['message'] ?? '';

    $errors = [];
    if (empty($nom)) $errors['nom'] = 'Nom obligatoire';
    if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors['email'] = 'Email invalide';
    }
    if (empty($message)) $errors['message'] = 'Message obligatoire';

    if (!empty($errors)) {
        header('Content-Type: application/json');
        echo json_encode(['status' => 'KO', 'errors' => $errors]);
        exit;
    }

    mail('destinataire@exemple.fr', 'Contact', $message, "From: $email");
    header('Content-Type: application/json');
    echo json_encode(['status' => 'OK']);
    exit;
}
?>

8. Sécurisation

À ajouter après avoir défini $fichierMD.

<?php
$fichierReel = realpath($fichierMD);
$dossierPages = realpath(PAGES_DIR);

if ($fichierReel === false || strpos($fichierReel, $dossierPages) !== 0) {
    http_response_code(403);
    die('Accès interdit');
}
?>

9. Règles de délimitation

10. Résumé du fonctionnement

  1. Requête HTTP → /contact.
  2. .htaccess → index.php.
  3. index.php extrait "contact".
  4. Cherche pages/contact.md.
  5. Convertit en HTML.
  6. Insère dans template.html.
  7. Affiche.

FIN

// 1. Installer AltoRouter via Composer
// composer require altorouter/altorouter

// 2. index.php avec AltoRouter

<?php
require 'vendor/autoload.php';

$router = new AltoRouter();

// Définir les routes
$router->map('GET', '/', 'accueil');
$router->map('GET', '/contact', 'contact');
$router->map('POST', '/envoyer-contact', 'traiterContact');

// Exécuter
$match = $router->match();

if ($match) {
    // Appeler la fonction correspondante
    $nomFonction = $match['target'];
    $nomFonction();
} else {
    http_response_code(404);
    echo '404 - Page non trouvée';
}

function accueil() {
    echo 'Page d\'accueil';
}

function contact() {
    echo 'Page de contact';
}

function traiterContact() {
    // Traiter le formulaire
    echo 'Formulaire traité';
}
?>

Routeur PHP : principe et fonctionnement

1. Qu'est-ce qu'un routeur ?

Un routeur est un composant logiciel qui analyse l'URL demandée par un visiteur et décide quel code exécuter pour générer la réponse.

Exemple concret :

2. Le problème que résout le routeur

Sans routeur, chaque page correspond à un fichier physique :

mon-site/
├── index.php          # Page d'accueil
├── contact.php        # Page de contact
├── mentions.php       # Mentions légales
└── actualites.php     # Actualités

Inconvénients :

Avec un routeur :

mon-site/
├── index.php          # Routeur unique
├── template.html      # Structure commune
└── pages/
    ├── accueil.md
    ├── contact.md
    └── mentions.md

Avantages :

3. Fonctionnement étape par étape

Étape 1 : Redirection Apache

Toutes les requêtes sont redirigées vers le routeur (index.php).

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

Étape 2 : Analyse de l'URL

Le routeur récupère l'URL demandée et en extrait un mot-clé.

$uri = $_SERVER['REQUEST_URI'];
$uri = parse_url($uri, PHP_URL_PATH);
$uri = trim($uri, '/');
$motCle = empty($uri) ? 'accueil' : explode('/', $uri)[0];

Exemples :

URL demandée Mot-clé extrait
/ accueil
/contact contact
/mentions mentions

Étape 3 : Correspondance

Le routeur cherche un fichier correspondant au mot-clé.

$fichierMD = PAGES_DIR . $motCle . '.md';

Exemple : /contact → pages/contact.md

Étape 4 : Exécution

Si le fichier existe, son contenu est chargé, converti et affiché.

if (file_exists($fichierMD)) {
    $contenuMD = file_get_contents($fichierMD);
    $contenuHTML = convertirMarkdown($contenuMD);
} else {
    // Erreur 404
}

4. Le routeur comme table de correspondance

Le routeur peut aussi fonctionner comme une table de correspondance explicite.

$routes = [
    '/'         => 'accueil',
    '/contact'  => 'contact',
    '/mentions' => 'mentions-legales',
    '/blog'     => 'liste-articles',
    '/blog/123' => 'article-123',
];

Avec paramètres dynamiques :

$routes = [
    '/article/{id}' => 'afficherArticle',
];

5. Routeur "maison" vs solution existante

Critère Routeur maison AltoRouter Slim Framework
Complexité Simple Légère Modérée
Flexibilité Totalement modifiable Standards Standards
Courbe d'apprentissage Nulle Faible Faible
Maintenance À assurer Assurée par la communauté Assurée par la communauté
Performance Rapide Rapide Bonne

6. Exemple de routeur avec AltoRouter

<?php
// index.php avec AltoRouter

require 'vendor/autoload.php';

$router = new AltoRouter();

// Définition des routes
$router->map('GET', '/', 'accueil');
$router->map('GET', '/contact', 'contact');
$router->map('POST', '/envoyer', 'traiterFormulaire');
$router->map('GET', '/article/[i:id]', 'article');

// Exécution
$match = $router->match();

if ($match) {
    $nomFonction = $match['target'];
    $nomFonction($match['params']);
} else {
    http_response_code(404);
    echo '404 - Page non trouvée';
}

function accueil() {
    echo 'Page d\'accueil';
}

function contact() {
    echo 'Page de contact';
}

function article($params) {
    echo 'Article n°' . $params['id'];
}
?>

7. Routeur avec paramètres dynamiques

URL : /article/123

Extraction :

Code :

<?php
$motCle = explode('/', $uri)[0]; // 'article'
$param = explode('/', $uri)[1] ?? null; // '123'

if ($motCle === 'article') {
    $id = (int)$param;
    afficherArticle($id);
}
?>

8. Routeur avec contenu Markdown

Le routeur le plus simple :

<?php
// index.php

define('PAGES_DIR', __DIR__ . '/pages/');

$uri = $_SERVER['REQUEST_URI'];
$uri = parse_url($uri, PHP_URL_PATH);
$uri = trim($uri, '/');
$motCle = empty($uri) ? 'accueil' : explode('/', $uri)[0];

$fichierMD = PAGES_DIR . $motCle . '.md';

if (file_exists($fichierMD)) {
    $contenu = file_get_contents($fichierMD);
    $contenu = convertirMarkdown($contenu);
} else {
    http_response_code(404);
    $contenu = '<h1>404</h1>';
}

$template = file_get_contents('template.html');
$template = str_replace('{{ CONTENU }}', $contenu, $template);
echo $template;
?>

9. Avantages et inconvénients

Avantages Inconvénients
URLs propres et esthétiques Nécessite une configuration (.htaccess)
Code centralisé et organisé Toutes les requêtes passent par un seul fichier
Facile à modifier et à étendre Surcharge du fichier index.php si mal organisé
Séparation du contenu et du code Complexité supplémentaire pour les débutants
Nouvelles pages sans nouveau code Gestion des erreurs à prévoir

10. Résumé

Le routeur est un analyseur d'URL :

  1. Capturer : .htaccess redirige vers index.php.
  2. Analyser : extraire le mot-clé de l'URL.
  3. Correspondre : trouver le fichier .md associé.
  4. Exécuter : convertir et afficher le contenu.

C'est un mécanisme simple mais puissant qui permet de créer des sites dynamiques avec des URLs claires et une maintenance facile.


FIN DE L'EXPLICATION