PHP & HTML dynamique 🔄

Boucles, tableaux & affichage sécurisé

Formation DWWM — Générer du HTML avec PHP

2025 – 2026

Au programme 📋

  • 🤔 Le problème : HTML statique vs HTML dynamique
  • 🔄 Les 4 boucles PHP : while, for, foreach, do…while
  • 📋 foreach en profondeur : tableaux simples et associatifs
  • 🌐 PHP dans le HTML : syntaxe alternative et bonnes pratiques
  • 🛡️ htmlspecialchars() : sécuriser l'affichage
  • 🏗️ Cas concrets : listes, tableaux, cartes, menus
  • 💪 Exercices pratiques
🤔

Partie 0

HTML statique vs HTML dynamique

Pourquoi on a besoin des boucles

Le problème du HTML statique 😩

<!-- ❌ HTML STATIQUE -->
<!-- 100 produits = 100 lignes à la main -->
<ul>
    <li>Ordinateur — 899 €</li>
    <li>Souris — 25 €</li>
    <li>Clavier — 45 €</li>
    <li>Écran — 299 €</li>
    <!-- ...96 autres lignes... -->
</ul>
<!-- Problèmes :
  Modifier un produit = éditer HTML
  Données BDD → impossible en HTML
-->
<?php
// ✅ HTML DYNAMIQUE
// 100 produits = toujours 5 lignes
$produits = [
    "Ordinateur", "Souris",
    "Clavier", "Écran",
    /* ...96 autres... */
];
?>
<ul>
    <?php foreach ($produits as $p): ?>
        <li><?= htmlspecialchars($p) ?></li>
    <?php endforeach; ?>
</ul>
<!-- Données modifiées → HTML régénéré
     Peut venir d'une BDD (PDO) -->
💡 Avec PHP, la structure HTML s'écrit une seule fois — c'est le tableau de données qui change. C'est le principe fondamental du HTML dynamique.

Comment PHP génère du HTML 🔧

PHP s'exécute côté serveur avant que la page arrive dans le navigateur. Il lit le fichier .php, exécute le code, et envoie du HTML pur au client. Le navigateur ne voit jamais de PHP.
Serveur : fichier.php (PHP + HTML mélangés)
↓ PHP s'exécute, boucles tournent, variables remplacées
Réseau : HTML généré (HTML pur, zéro PHP)
↓ Envoyé au navigateur via HTTP
Client : Page affichée
<?php $nom = "Marie"; ?>
<p>Bonjour <?= $nom ?> !</p>

<!-- Le navigateur reçoit uniquement : -->
<p>Bonjour Marie !</p>
🔄

Partie 1

Les 4 boucles PHP

while, for, foreach, do…while

Vue d'ensemble 📊

BoucleQuand l'utiliserCas typique
while Répéter tant qu'une condition est vraie — nombre d'itérations inconnu à l'avance Lire un fichier ligne par ligne
for Répéter un nombre défini à l'avance de fois, avec un compteur Générer une pagination, N étoiles
foreach Parcourir chaque élément d'un tableau — la boucle du quotidien Liste de produits, utilisateurs, articles…
do…while Exécuter au moins une fois, puis répéter si la condition est vraie Menu interactif, validation avec retry
💡 En développement web PHP, foreach couvre ~80% des cas — elle est conçue pour parcourir les tableaux venant de la base de données.

La boucle while ⏳

while = "Tant que cette condition est vraie, répète le bloc." La condition est vérifiée avant chaque itération. Si elle est fausse dès le départ, le bloc n'est jamais exécuté.
<?php
$i = 1;
while ($i <= 5) {
    echo "<p>Itération n°$i</p>";
    $i++; // ← OBLIGATOIRE ! Sans ça → boucle infinie 💀
}
// Génère :
// <p>Itération n°1</p>
// <p>Itération n°2</p>  ...jusqu'à 5
?>
⚠️ Boucle infinie ! Si vous oubliez d'incrémenter $i, la condition reste toujours vraie → le serveur plante. La condition doit finir par devenir false.

La boucle for 🔢

for regroupe en une ligne : l'initialisation, la condition d'arrêt et l'incrément. Idéal quand on connaît le nombre exact de répétitions.
<?php
//   ┌─ Init      ┬─ Condition  ┬─ Incrément
for ($i = 1;      $i <= 5;     $i++) {
    echo "<p>Itération n°$i</p>";
}

// Cas concret : pagination HTML
$pageActuelle = 3;
$totalPages   = 7;
?>
<nav>
    <?php for ($p = 1; $p <= $totalPages; $p++): ?>
        <a href="?page=<?= $p ?>"
           <?= $p === $pageActuelle ? 'class="active"' : '' ?>>
            <?= $p ?>
        </a>
    <?php endfor; ?>
</nav>

La boucle do…while 🔁

do…while exécute le bloc au moins une fois, puis vérifie la condition. La condition est évaluée après chaque itération — à l'inverse de while.
<?php
$page = 1;
do {
    echo "<p>Page $page</p>";
    $page++;
} while ($page <= 3);
// Génère : <p>Page 1</p> <p>Page 2</p> <p>Page 3</p>

// ── Différence clé avec while : ──────────────────
$x = 10;

while ($x < 5) {
    echo "while : jamais affiché"; // condition fausse dès le début
}

do {
    echo "do-while : affiché une fois !"; // s'exécute toujours au moins 1x
} while ($x < 5);
?>
📋

Partie 2

foreach en profondeur

La boucle du quotidien en PHP

Anatomie de foreach 🔬

foreach parcourt automatiquement chaque élément d'un tableau du premier au dernier, sans gérer de compteur. PHP avance seul et s'arrête tout seul à la fin.
<?php
$fruits = ["Pomme", "Banane", "Orange"];

//          ┌─ Tableau à parcourir
//          │              ┌─ Variable temporaire (nom libre)
foreach ($fruits as $fruit) {
    echo "<li>$fruit</li>";
}
// Itération 1 : $fruit = "Pomme"  → <li>Pomme</li>
// Itération 2 : $fruit = "Banane" → <li>Banane</li>
// Itération 3 : $fruit = "Orange" → <li>Orange</li>
// → foreach s'arrête automatiquement
?>
$fruits
Le tableau source. 2 éléments ou 10 000 — foreach gère les deux pareil.
as $fruit
À chaque tour, $fruit reçoit la valeur courante. Nom libre, choisissez quelque chose de lisible.
Le corps
Tout ce qui est entre { } est répété autant de fois qu'il y a d'éléments.

foreach sur un tableau simple 📝

PHP — Le code

<?php
$langages = [
    "HTML", "CSS",
    "JavaScript", "PHP", "SQL"
];
?>
<h2>Langages appris</h2>
<ul>
  <?php foreach ($langages as $lang): ?>
    <li><?= htmlspecialchars($lang) ?></li>
  <?php endforeach; ?>
</ul>

Résultat ✅

HTML reçu par le navigateur : <ul>
 <li>HTML</li>
 <li>CSS</li>
 <li>JavaScript</li>
 <li>PHP</li>
 <li>SQL</li>
</ul>
Ce que voit l'utilisateur :
  • HTML
  • CSS
  • JavaScript
  • PHP
  • SQL

Tableau associatif : clé → valeur 🗝️

Un tableau associatif associe chaque valeur à une clé nommée (string). On utilise as $cle => $valeur pour accéder aux deux simultanément.
<?php
$utilisateur = [
    "nom"    => "Dupont",
    "prenom" => "Marie",
    "email"  => "marie@mail.com",
    "role"   => "admin",
];

//                      ┌─ Clé        ┌─ Valeur
foreach ($utilisateur as $cle => $valeur) {
    echo "<p><strong>$cle</strong> : $valeur</p>";
}
// <p><strong>nom</strong> : Dupont</p>
// <p><strong>prenom</strong> : Marie</p>
// <p><strong>email</strong> : marie@mail.com</p>
// <p><strong>role</strong> : admin</p>
?>
💡 $cle => $valeur est optionnel. Si vous n'avez besoin que des valeurs : foreach ($tab as $valeur). Ajoutez la clé uniquement quand vous en avez besoin.

Tableau de tableaux — structure BDD 🗄️

En pratique, les données PDO arrivent sous forme d'un tableau de tableaux associatifs : chaque élément représente une ligne de la base de données.

Représentation BDD

nomcategorieprix
Laptopinformatique899
Sourisinformatique25
T-shirtvêtements15
Jeanvêtements50

← Chaque ligne = un tableau associatif

En PHP

<?php
// Identique à fetchAll(PDO::FETCH_ASSOC)
$produits = [
  ["nom"=>"Laptop",
   "categorie"=>"informatique",
   "prix"=>899],
  ["nom"=>"Souris",
   "categorie"=>"informatique",
   "prix"=>25],
  // ...
];
// $produits[0] = ["nom"=>"Laptop"...]
// $produits[1] = ["nom"=>"Souris"...]
?>

foreach sur un tableau de tableaux 🔄

<?php
$produits = [
    ["nom" => "Laptop",  "categorie" => "informatique", "prix" => 899],
    ["nom" => "Souris",  "categorie" => "informatique", "prix" => 25],
    ["nom" => "T-shirt", "categorie" => "vêtements",    "prix" => 15],
];
?>
<table>
    <thead>
        <tr><th>Nom</th><th>Catégorie</th><th>Prix</th></tr>
    </thead>
    <tbody>
        <?php foreach ($produits as $produit): ?>
            <!-- $produit = ["nom"=>"Laptop", "categorie"=>"informatique", "prix"=>899] -->
            <tr>
                <td><?= htmlspecialchars($produit['nom']) ?></td>
                <td><?= htmlspecialchars($produit['categorie']) ?></td>
                <td><?= $produit['prix'] ?> €</td>
            </tr>
        <?php endforeach; ?>
    </tbody>
</table>
💡 À chaque tour, $produit est un tableau associatif complet. On accède à ses champs avec $produit['nom'], $produit['prix'], etc.

Index, compteur et count() 🔢

<?php
$fruits = ["Pomme", "Banane", "Orange", "Fraise"];

// ── L'index dans un foreach ──────────────────────────
// Méthode 1 : compteur manuel
$i = 0;
foreach ($fruits as $fruit) {
    echo "<li>$i. $fruit</li>";
    $i++;
}

// Méthode 2 : la clé du tableau (commence à 0)
foreach ($fruits as $index => $fruit) {
    echo "<li>" . ($index + 1) . ". $fruit</li>"; // +1 → affiche 1, 2, 3...
}

// ── Compter et gérer le tableau vide ─────────────────
$total = count($fruits); // 4
echo "<p>$total fruits dans la liste</p>";

if (empty($fruits)) {
    echo "<p>Aucun fruit disponible.</p>";
} else {
    foreach ($fruits as $fruit) {
        echo "<li>" . htmlspecialchars($fruit) . "</li>";
    }
}
?>
💡 Toujours vérifier empty() avant de boucler sur des données BDD — si la table est vide, on affiche un message utile plutôt qu'un tableau HTML vide.

Première et dernière itération 🎯

<?php
$etapes = ["Inscription", "Validation email", "Profil complété", "Terminé"];
$total  = count($etapes);
?>
<ol>
<?php foreach ($etapes as $index => $etape): ?>
    <?php
        $estPremiere = ($index === 0);
        $estDerniere = ($index === $total - 1);
    ?>
    <li
        <?php if ($estPremiere): ?>
            style="color:#f0ad4e;font-weight:bold"
        <?php elseif ($estDerniere): ?>
            style="color:#5cb85c;font-weight:bold"
        <?php endif; ?>
    >
        <?= $estPremiere ? '🚀' : ($estDerniere ? '✅' : '🔹') ?>
        <?= htmlspecialchars($etape) ?>
    </li>
<?php endforeach; ?>
</ol>

foreach imbriqués — données groupées 🗂️

<?php
$catalogue = [
    "Informatique" => [
        ["nom" => "Laptop",  "prix" => 899],
        ["nom" => "Souris",  "prix" => 25],
    ],
    "Vêtements" => [
        ["nom" => "T-shirt", "prix" => 15],
        ["nom" => "Jean",    "prix" => 50],
    ],
];
?>

<?php foreach ($catalogue as $categorie => $produits): ?>
    <section>
        <h2><?= htmlspecialchars($categorie) ?></h2>
        <ul>
            <?php foreach ($produits as $produit): ?>
                <li>
                    <?= htmlspecialchars($produit['nom']) ?>
                    — <?= $produit['prix'] ?> €
                </li>
            <?php endforeach; ?>
        </ul>
    </section>
<?php endforeach; ?>
💡 Foreach imbriqués : le premier parcourt les catégories, le second parcourt les produits de chaque catégorie. Chaque niveau a sa propre variable temporaire.
🌐

Partie 3

PHP dans le HTML

Syntaxe, bonnes pratiques et lisibilité

Deux syntaxes : laquelle choisir ? 🤔

Syntaxe accolades { }

<?php foreach ($items as $item) { ?>
    <li><?= $item ?></li>
<?php } ?>

<?php if ($ok) { ?>
    <p>Vrai</p>
<?php } else { ?>
    <p>Faux</p>
<?php } ?>

✅ Familière
⚠️ Les } seules sont difficiles à lire quand le HTML est long

✅ Syntaxe alternative (templates)

<?php foreach ($items as $item): ?>
    <li><?= $item ?></li>
<?php endforeach; ?>

<?php if ($ok): ?>
    <p>Vrai</p>
<?php else: ?>
    <p>Faux</p>
<?php endif; ?>

✅ Plus lisible dans les fichiers PHP/HTML
✅ endforeach, endif indiquent ce qui se ferme

💡 La syntaxe alternative est recommandée dès que PHP est mélangé avec du HTML — on voit immédiatement quelle structure se termine.

<?= vs <?php echo 📢

<?= $var ?> est un raccourci strict pour <?php echo $var; ?>. Préférer la forme courte dans les templates pour réduire le bruit visuel.
<!-- Forme longue -->
<p><?php echo $nom; ?></p>

<!-- Forme courte ← préférer dans les templates -->
<p><?= $nom ?></p>

<!-- Avec htmlspecialchars() -->
<p><?php echo htmlspecialchars($nom); ?></p>
<p><?= htmlspecialchars($nom) ?></p>

<!-- Avec l'alias e() — ultra concis -->
<?php function e($t) { return htmlspecialchars($t, ENT_QUOTES, 'UTF-8'); } ?>
<p><?= e($nom) ?></p>
💡 <?= est disponible depuis PHP 5.4 et toujours activé. Utilisez-le sans restriction dans tous vos projets PHP modernes.

Bonne pratique : séparer logique et affichage ✂️

<!-- ❌ Logique mélangée dans le HTML -->
<ul>
  <?php
  $tous = [["nom"=>"Pomme","stock"=>5],
           ["nom"=>"Poire","stock"=>0],
           ["nom"=>"Kiwi", "stock"=>3]];
  foreach ($tous as $p) {
    if ($p['stock'] > 0) {
      echo "<li>" . $p['nom'] . "</li>";
    }
  }
  ?>
</ul>
<?php
// ✅ LOGIQUE en haut du fichier
$tous = [["nom"=>"Pomme","stock"=>5],
         ["nom"=>"Poire","stock"=>0],
         ["nom"=>"Kiwi", "stock"=>3]];

$resultats = array_filter(
    $tous, fn($p) => $p['stock'] > 0
);
?>

<!-- AFFICHAGE — HTML propre -->
<ul>
  <?php foreach ($resultats as $p): ?>
    <li><?= htmlspecialchars($p['nom']) ?></li>
  <?php endforeach; ?>
</ul>
💡 Règle simple : tout le PHP de traitement en haut de la page, le HTML d'affichage en bas. Les templates ne doivent contenir que des foreach, if et des <?= e($var) ?>.
🛡️

Partie 4

htmlspecialchars()

Pourquoi c'est indispensable dans les boucles

Le danger XSS dans une boucle 🦠

Dans une boucle, chaque élément affiché est une porte d'entrée XSS. Si les données viennent d'une BDD ou d'un formulaire, n'importe lequel peut contenir du JavaScript malveillant.
<?php
$commentaires = [
    "Super article !",
    "<script>alert('Piraté!')</script>",
    "Merci pour le partage.",
];
?>
<ul>
  <?php foreach ($commentaires as $c): ?>
    <!-- ❌ Le script s'EXÉCUTE ! -->
    <li><?= $c ?></li>
  <?php endforeach; ?>
</ul>
<?php
$commentaires = [
    "Super article !",
    "<script>alert('Piraté!')</script>",
    "Merci pour le partage.",
];
?>
<ul>
  <?php foreach ($commentaires as $c): ?>
    <!-- ✅ Affiché comme texte brut -->
    <li><?= htmlspecialchars($c) ?></li>
  <?php endforeach; ?>
</ul>

Ce que fait htmlspecialchars() 🔍

Entrée (données brutes)Sortie (HTML sécurisé)Affiché
Bonjour MarieBonjour MarieBonjour Marie ✅
<b>Gras</b>&lt;b&gt;Gras&lt;/b&gt;<b>Gras</b> (texte)
<script>hack()</script>&lt;script&gt;...&lt;/script&gt;Texte brut ✅
5 & 10 €5 &amp; 10 €5 & 10 €
<?php
// Syntaxe complète recommandée
function e(string $text): string {
    return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    //                             ↑           ↑
    //               Échappe aussi '      Encodage du fichier
}

// Usage dans une boucle
foreach ($produits as $p) {
    echo "<li>" . e($p['nom']) . " — " . e($p['categorie']) . "</li>";
}
?>

Où utiliser htmlspecialchars() ? 🎯

<?php function e($t) { return htmlspecialchars($t, ENT_QUOTES, 'UTF-8'); } ?>

<!-- ✅ Dans le contenu d'un élément -->
<?php foreach ($articles as $a): ?>
    <h2><?= e($a['titre']) ?></h2>
    <p><?= e($a['contenu']) ?></p>
<?php endforeach; ?>

<!-- ✅ Dans les attributs HTML -->
<?php foreach ($produits as $p): ?>
    <img src="img/<?= e($p['image']) ?>" alt="<?= e($p['nom']) ?>">
    <a href="?id=<?= (int)$p['id'] ?>"><?= e($p['nom']) ?></a>
<?php endforeach; ?>

<!-- ⚠️ Données numériques : cast suffit, pas besoin de e() -->
<?php foreach ($produits as $p): ?>
    <td><?= (int)$p['id'] ?></td>
    <td><?= (float)$p['prix'] ?> €</td>
    <td><?= e($p['nom']) ?></td>   <!-- String → e() obligatoire -->
<?php endforeach; ?>
Règle d'or : Sécuriser à la sortie (affichage), jamais à l'entrée. Stockez les données brutes en BDD, appliquez e() uniquement au moment d'afficher.
🏗️

Partie 5

Cas concrets

Listes, tableaux, cartes, menus — les structures du quotidien

Générer une liste <ul> / <ol> 📋

<?php
$technologies = ["HTML", "CSS", "JavaScript", "PHP", "MySQL"];
$etapes       = ["Analyse", "Maquette", "Développement", "Tests", "Livraison"];
?>

<h3>Technologies maîtrisées</h3>
<ul>
    <?php foreach ($technologies as $tech): ?>
        <li><?= htmlspecialchars($tech) ?></li>
    <?php endforeach; ?>
</ul>

<h3>Étapes du projet</h3>
<ol>
    <?php foreach ($etapes as $etape): ?>
        <li><?= htmlspecialchars($etape) ?></li>
    <?php endforeach; ?>
</ol>

<!-- Toujours gérer la liste vide -->
<?php if (empty($technologies)): ?>
    <p>Aucune technologie disponible.</p>
<?php else: ?>
    <ul>
        <?php foreach ($technologies as $tech): ?>
            <li><?= htmlspecialchars($tech) ?></li>
        <?php endforeach; ?>
    </ul>
<?php endif; ?>

Générer un tableau <table> 📊

<?php
$employes = [
    ["id"=>1, "nom"=>"Dupont",  "prenom"=>"Marie",  "salaire"=>2800],
    ["id"=>2, "nom"=>"Martin",  "prenom"=>"Jean",   "salaire"=>2600],
    ["id"=>3, "nom"=>"Bernard", "prenom"=>"Sophie", "salaire"=>3200],
];
?>
<table border="1">
    <thead>
        <tr><th>#</th><th>Nom</th><th>Prénom</th><th>Salaire</th></tr>
    </thead>
    <tbody>
        <?php foreach ($employes as $e): ?>
            <tr>
                <td><?= (int)$e['id'] ?></td>
                <td><?= htmlspecialchars($e['nom']) ?></td>
                <td><?= htmlspecialchars($e['prenom']) ?></td>
                <td><?= number_format($e['salaire'], 0, ',', ' ') ?> €</td>
            </tr>
        <?php endforeach; ?>
    </tbody>
    <tfoot>
        <tr>
            <td colspan="3"><strong>Total</strong></td>
            <td><?= number_format(array_sum(array_column($employes,'salaire')), 0, ',', ' ') ?> €</td>
        </tr>
    </tfoot>
</table>

Lignes alternées — zebra striping 🦓

L'opérateur modulo % retourne le reste de la division entière. $i % 2 === 0 est vrai pour les index pairs (0, 2, 4…) et faux pour les impairs (1, 3, 5…) — idéal pour alterner deux couleurs.
<?php
$commandes = [
    ["id"=>1001, "client"=>"Marie D.",  "montant"=>89.90,  "statut"=>"Livré"],
    ["id"=>1002, "client"=>"Jean M.",   "montant"=>245.00, "statut"=>"En cours"],
    ["id"=>1003, "client"=>"Sophie B.", "montant"=>12.50,  "statut"=>"Livré"],
    ["id"=>1004, "client"=>"Pierre L.", "montant"=>399.99, "statut"=>"Annulé"],
];
?>
<table>
    <?php foreach ($commandes as $i => $cmd): ?>
        <tr style="background: <?= $i % 2 === 0 ? '#1a1a2e' : '#16213e' ?>">
            <td><?= (int)$cmd['id'] ?></td>
            <td><?= htmlspecialchars($cmd['client']) ?></td>
            <td><?= number_format($cmd['montant'], 2, ',', ' ') ?> €</td>
            <td><?= htmlspecialchars($cmd['statut']) ?></td>
        </tr>
    <?php endforeach; ?>
</table>

Cartes produits avec conditions 🃏

<?php
$produits = [
    ["nom"=>"Laptop",  "prix"=>1299, "stock"=>5,  "badge"=>"Nouveau"],
    ["nom"=>"Souris",  "prix"=>49,   "stock"=>0,  "badge"=>""],
    ["nom"=>"Clavier", "prix"=>89,   "stock"=>12, "badge"=>"Promo"],
];
?>
<div class="grille">
<?php foreach ($produits as $p): ?>
    <div class="carte <?= $p['stock'] === 0 ? 'epuise' : '' ?>">

        <?php if (!empty($p['badge'])): ?>
            <span class="badge"><?= htmlspecialchars($p['badge']) ?></span>
        <?php endif; ?>

        <h3><?= htmlspecialchars($p['nom']) ?></h3>
        <p><?= number_format($p['prix'], 2, ',', ' ') ?> €</p>

        <?php if ($p['stock'] === 0): ?>
            <p style="color:red">Épuisé</p>
        <?php else: ?>
            <p style="color:green"><?= $p['stock'] ?> en stock</p>
            <button>Ajouter au panier</button>
        <?php endif; ?>

    </div>
<?php endforeach; ?>
</div>

Menu de navigation dynamique 🧭

<?php
$pageCourante = "services";
$menu = [
    ["label"=>"Accueil",   "url"=>"/",          "slug"=>"accueil"],
    ["label"=>"Services",  "url"=>"/services",  "slug"=>"services"],
    ["label"=>"Portfolio", "url"=>"/portfolio", "slug"=>"portfolio"],
    ["label"=>"Contact",   "url"=>"/contact",   "slug"=>"contact"],
];
?>
<nav>
    <ul>
        <?php foreach ($menu as $lien): ?>
            <?php $estActif = ($lien['slug'] === $pageCourante); ?>
            <li>
                <a href="<?= htmlspecialchars($lien['url']) ?>"
                   <?= $estActif ? 'class="actif" aria-current="page"' : '' ?>>
                    <?= htmlspecialchars($lien['label']) ?>
                </a>
            </li>
        <?php endforeach; ?>
    </ul>
</nav>
💡 aria-current="page" indique aux lecteurs d'écran quelle page est active — attribut d'accessibilité important à systématiser dans vos menus.

Générer un <select> dynamique 📥

<?php
$categories = [
    "informatique" => "Informatique",
    "vetements"    => "Vêtements",
    "sport"        => "Sport & Loisirs",
    "maison"       => "Maison & Jardin",
];
$selectionne = $_GET['categorie'] ?? 'informatique';
?>

<select name="categorie">
    <option value="">-- Choisir une catégorie --</option>

    <?php foreach ($categories as $valeur => $label): ?>
        <option
            value="<?= htmlspecialchars($valeur) ?>"
            <?= $valeur === $selectionne ? 'selected' : '' ?>>
            <?= htmlspecialchars($label) ?>
        </option>
    <?php endforeach; ?>
</select>
<!--
  Génère (si $selectionne === 'vetements') :
  <option value="vetements" selected>Vêtements</option>
-->
💡 C'est la même logique que pour les radio/checkbox : comparer la valeur reçue avec chaque valeur possible, et ajouter l'attribut selected sur la bonne option.
💪

Partie 6

Exercices pratiques

À vous de jouer !

Exercice 1 : Liste de films 🎬

📝 Énoncé : À partir du tableau $films, générez :
• Un <h1> avec le nombre de films : "5 films disponibles"
• Une <ul> : chaque <li> affiche <strong>Titre</strong> (Année) — Genre
• htmlspecialchars() sur toutes les strings
$films = [
    ["titre"=>"Inception",    "annee"=>2010, "genre"=>"Science-fiction"],
    ["titre"=>"Le Parrain",   "annee"=>1972, "genre"=>"Drame"],
    ["titre"=>"Interstellar", "annee"=>2014, "genre"=>"Science-fiction"],
    ["titre"=>"Pulp Fiction", "annee"=>1994, "genre"=>"Thriller"],
    ["titre"=>"Les Évadés",   "annee"=>1994, "genre"=>"Drame"],
];

Exercice 1 : Corrigé ✅

<?php
$films = [
    ["titre"=>"Inception",    "annee"=>2010, "genre"=>"Science-fiction"],
    ["titre"=>"Le Parrain",   "annee"=>1972, "genre"=>"Drame"],
    ["titre"=>"Interstellar", "annee"=>2014, "genre"=>"Science-fiction"],
    ["titre"=>"Pulp Fiction", "annee"=>1994, "genre"=>"Thriller"],
    ["titre"=>"Les Évadés",   "annee"=>1994, "genre"=>"Drame"],
];
$total = count($films);
?>

<h1><?= $total ?> film<?= $total > 1 ? 's' : '' ?> disponible<?= $total > 1 ? 's' : '' ?></h1>

<?php if (empty($films)): ?>
    <p>Aucun film disponible.</p>
<?php else: ?>
    <ul>
        <?php foreach ($films as $film): ?>
            <li>
                <strong><?= htmlspecialchars($film['titre']) ?></strong>
                (<?= (int)$film['annee'] ?>)
                — <?= htmlspecialchars($film['genre']) ?>
            </li>
        <?php endforeach; ?>
    </ul>
<?php endif; ?>
💡 $total > 1 ? 's' : '' gère le pluriel automatiquement — "1 film disponible" vs "5 films disponibles".

Exercice 2 : Tableau des étudiants 🎓

📝 Énoncé : Générez un <table> avec :
• Colonnes : #, Nom, Prénom, Note, Mention
• Mention selon note : ≥16 Très Bien · ≥14 Bien · ≥12 Assez Bien · sinon Passable
• Lignes alternées (index pair / impair)
$etudiants = [
    ["nom"=>"Dupont",  "prenom"=>"Marie",  "note"=>17.5],
    ["nom"=>"Martin",  "prenom"=>"Jean",   "note"=>13.0],
    ["nom"=>"Bernard", "prenom"=>"Sophie", "note"=>15.5],
    ["nom"=>"Leroy",   "prenom"=>"Pierre", "note"=>11.0],
];

Exercice 2 : Corrigé ✅

<?php
$etudiants = [
    ["nom"=>"Dupont",  "prenom"=>"Marie",  "note"=>17.5],
    ["nom"=>"Martin",  "prenom"=>"Jean",   "note"=>13.0],
    ["nom"=>"Bernard", "prenom"=>"Sophie", "note"=>15.5],
    ["nom"=>"Leroy",   "prenom"=>"Pierre", "note"=>11.0],
];

function getMention(float $note): string {
    if ($note >= 16) return "Très Bien";
    if ($note >= 14) return "Bien";
    if ($note >= 12) return "Assez Bien";
    return "Passable";
}
?>
<table border="1" style="border-collapse:collapse;width:100%">
    <thead>
        <tr style="background:#4F86C6;color:white">
            <th>#</th><th>Nom</th><th>Prénom</th><th>Note</th><th>Mention</th>
        </tr>
    </thead>
    <tbody>
        <?php foreach ($etudiants as $i => $e): ?>
            <tr style="background:<?= $i % 2 === 0 ? '#f9f9f9' : '#fff' ?>;color:#222">
                <td><?= $i + 1 ?></td>
                <td><?= htmlspecialchars($e['nom']) ?></td>
                <td><?= htmlspecialchars($e['prenom']) ?></td>
                <td><?= number_format($e['note'], 1, ',', '') ?>/20</td>
                <td><?= getMention($e['note']) ?></td>
            </tr>
        <?php endforeach; ?>
    </tbody>
</table>

Exercice 3 : Catalogue avec recherche 🛍️

📝 Énoncé : Créez catalogue.php qui :
• Affiche un formulaire GET avec un champ texte
• Filtre $produits sur le nom avec stripos()
• Affiche les résultats en cartes (nom, prix, stock)
• Si stock = 0 → "Épuisé" en rouge, sinon bouton "Ajouter"
• Toutes les strings passent par htmlspecialchars()
$produits = [
    ["nom"=>"Laptop",   "prix"=>899, "stock"=>3],
    ["nom"=>"Souris",   "prix"=>25,  "stock"=>0],
    ["nom"=>"Clavier",  "prix"=>45,  "stock"=>7],
    ["nom"=>"Écran 4K", "prix"=>399, "stock"=>2],
];

Exercice 3 : Corrigé ✅

<?php
$produits  = [
    ["nom"=>"Laptop",   "prix"=>899, "stock"=>3],
    ["nom"=>"Souris",   "prix"=>25,  "stock"=>0],
    ["nom"=>"Clavier",  "prix"=>45,  "stock"=>7],
    ["nom"=>"Écran 4K", "prix"=>399, "stock"=>2],
];
$recherche = trim($_GET['q'] ?? '');
$resultats = $recherche !== ''
    ? array_filter($produits, fn($p) => stripos($p['nom'], $recherche) !== false)
    : $produits;
?>
<form method="GET">
    <input type="text" name="q" value="<?= htmlspecialchars($recherche) ?>" placeholder="Rechercher...">
    <button>Filtrer</button>
    <?php if ($recherche): ?><a href="?">✕ Effacer</a><?php endif; ?>
</form>
<p><?= count($resultats) ?> produit(s) trouvé(s)</p>
<div style="display:flex;gap:16px;flex-wrap:wrap">
    <?php foreach ($resultats as $p): ?>
        <div style="border:1px solid #ccc;padding:16px;border-radius:8px;width:150px">
            <h3><?= htmlspecialchars($p['nom']) ?></h3>
            <p><?= number_format($p['prix'], 2, ',', ' ') ?> €</p>
            <?php if ($p['stock'] === 0): ?>
                <p style="color:red">⚠️ Épuisé</p>
            <?php else: ?>
                <p style="color:green">✅ <?= $p['stock'] ?> en stock</p>
                <button>Ajouter</button>
            <?php endif; ?>
        </div>
    <?php endforeach; ?>
</div>

Exercice 4 BONUS : Menu avec page active 🧭

📝 Énoncé : Menu horizontal depuis $menu. La page courante vient de $_GET['page'] ?? 'accueil'.
• Lien actif = <span> (non cliquable) avec classe actif
• Valider la page reçue par liste blanche (slugs connus seulement)
<?php
$menu = [
    ["label"=>"Accueil",   "slug"=>"accueil"],
    ["label"=>"Services",  "slug"=>"services"],
    ["label"=>"Portfolio", "slug"=>"portfolio"],
    ["label"=>"Contact",   "slug"=>"contact"],
];
$slugsValides = array_column($menu, 'slug');
$pageRecue    = $_GET['page'] ?? 'accueil';
// Liste blanche : on n'accepte que les slugs connus
$pageCourante = in_array($pageRecue, $slugsValides) ? $pageRecue : 'accueil';
?>
<nav><ul style="display:flex;list-style:none;gap:20px;padding:0">
    <?php foreach ($menu as $lien): ?>
        <?php $actif = ($lien['slug'] === $pageCourante); ?>
        <li>
            <?php if ($actif): ?>
                <span style="color:#f0ad4e;font-weight:bold" aria-current="page">
                    <?= htmlspecialchars($lien['label']) ?>
                </span>
            <?php else: ?>
                <a href="?page=<?= htmlspecialchars($lien['slug']) ?>">
                    <?= htmlspecialchars($lien['label']) ?>
                </a>
            <?php endif; ?>
        </li>
    <?php endforeach; ?>
</ul></nav>

🎯 Récapitulatif

  • ✅ PHP → HTML : exécuté côté serveur, navigateur reçoit du HTML pur
  • ✅ while : condition vérifiée avant chaque tour
  • ✅ for : compteur + condition + incrément
  • ✅ foreach : parcourt chaque élément d'un tableau
  • ✅ do…while : exécuté au moins une fois
  • ✅ foreach ($tab as $val) : tableau simple
  • ✅ foreach ($tab as $cle => $val) : associatif
  • ✅ Syntaxe alternative : endforeach dans les templates
  • ✅ htmlspecialchars() sur toutes les strings affichées
  • ✅ empty() avant de boucler sur des données BDD

Les règles à retenir 📝

🔄 Boucles

  • Toujours incrémenter dans while
  • Vérifier empty() avant de boucler
  • Préférer foreach pour les tableaux
  • Syntaxe : endforeach dans le HTML
  • Séparer logique (PHP) et affichage (HTML)

🛡️ Sécurité

  • htmlspecialchars() sur toute string
  • Cast (int) / (float) pour les nombres
  • Alias function e($t) pour aller vite
  • Validation par liste blanche
  • Jamais afficher $_GET/$_POST bruts

Prochaines étapes 🚀

  • 🗄️ PDO & BDD : fetchAll(PDO::FETCH_ASSOC) retourne exactement un tableau de tableaux associatifs — votre foreach fonctionnera sans modification !
  • 🔧 Fonctions tableau : array_filter(), array_map(), usort() pour transformer avant d'afficher
  • 🧩 Includes & templates : séparer en fichiers réutilisables (header, footer, composants)
  • 📦 Sessions : stocker des données entre les pages (panier, utilisateur connecté)
  • 🏗️ MVC : Modèle-Vue-Contrôleur — logique et affichage dans des fichiers séparés

Merci ! 👏

Des questions ?

🔄 foreach · 🛡️ htmlspecialchars · 🌐 HTML dynamique

= Le cœur du développement web PHP !