TP PHP : Découverte des cookies
Panier d’achat persistant sans connexion

Objectifs du TP

  • Comprendre ce qu’est un cookie et à quoi il sert
  • Savoir créer, lire, modifier et supprimer un cookie avec setcookie()
  • Utiliser serialize() et unserialize() pour stocker un tableau dans un cookie
  • Découvrir les paramètres importants (expiration, HttpOnly, durée de vie)
  • Réaliser un panier d’achat qui reste même après fermeture du navigateur

Ce TP est fait avant l’étude des sessions. Nous n’utilisons donc que des cookies.

Voici le lien du résultat attendu : Store cookies

Questions à répondre dans ton Google Docs

  1. Crée un Google Docs nommé "TP Cookies - NOM Prénom"
  2. Partage ton Google Docs avecc moi (daaif@enset-media.ac.ma)
  3. Réponds-y aux questions suivantes :
  4. Ajouter des copies d'écrans justifiant vos réponses
  5. 1. Après avoir ajouté des produits, ferme complètement ton navigateur, rouvre-le et reviens sur le site. Que constate-tu concernant le panier ? Explique pourquoi.
    2. Ouvre les outils de développement (F12) → onglet ApplicationCookies. Que contient le cookie nommé panier ?
    3. À quoi servent les fonctions serialize() et unserialize() dans ce TP ?
    4. Dans la fonction setcookie(), à quoi sert le dernier paramètre true (HttpOnly) ?
    5. Comment fait-on pour supprimer définitivement un cookie ? Donne le code utilisé dans vider.php.
    6. Quelles sont les limites principales des cookies (taille, sécurité, etc.) ?
    7. Ajoute une capture d’écran de ton panier avec au moins 3 articles différents.
    8. (Bonus) Explique comment tu pourrais protéger le contenu du cookie contre une modification par l’utilisateur (ex. : signature HMAC).

Création des fichiers

Crée un dossier tp_cookies/ contenant les 6 fichiers suivants. Copie-colle le code ci-dessous.

1. db.php – Connexion à la base de données (à créer une seule fois)
<?php
try {
    $pdo = new PDO('mysql:host=localhost;dbname=nom_de_ta_base;charset=utf8', 'root', '');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(Exception $e) {
    die('Erreur de connexion : ' . $e->getMessage());
}
?>

Et la table produits :

CREATE TABLE produits (
    id INT PRIMARY KEY AUTO_INCREMENT,
    nom VARCHAR(100),
    prix DECIMAL(6,2)
);

INSERT INTO produits (nom, prix) VALUES
('Casque Bluetooth', 79.99),
('Clavier mécanique', 129.90),
('Souris gamer', 59.99),
('Écran 27" 144Hz', 299.00),
('Tapis de souris XXL', 24.90);
2. index.php – Page d’accueil et liste des produits
<?php require_once 'db.php';
$stmt = $pdo->query("SELECT * FROM produits ORDER BY nom");
$produits = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Magasin - TP Cookies</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet">
</head>
<body class="bg-light">
    <div class="container py-5">
        <h1 class="mb-4"><i class="bi bi-shop me-2"></i> Magasin en ligne</h1>
        <div class="alert alert-info">
            <i class="bi bi-cookie me-2"></i>
            Ce site utilise des <strong>cookies</strong> pour conserver votre panier même si vous fermez le navigateur !
        </div>

        <div class="text-end mb-3">
            <a href="panier.php" class="btn btn-primary btn-lg">
                <i class="bi bi-cart4 me-2"></i> Voir mon panier
            </a>
        </div>

        <div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
            <?php foreach($produits as $p): ?>
            <div class="col">
                <div class="card h-100 shadow-sm">
                    <div class="card-body d-flex flex-column">
                        <h5 class="card-title"><?= htmlspecialchars($p['nom']) ?></h5>
                        <p class="card-text mt-auto">
                            <strong class="text-success fs-4"><?= number_format($p['prix'], 2) ?> €</strong>
                        </p>
                        <form method="post" action="ajouter.php" class="mt-3">
                            <input type="hidden" name="id" value="<?= $p['id'] ?>">
                            <input type="hidden" name="nom" value="<?= htmlspecialchars($p['nom']) ?>">
                            <input type="hidden" name="prix" value="<?= $p['prix'] ?>">
                            <button type="submit" class="btn btn-success w-100">
                                <i class="bi bi-cart-plus me-2"></i> Ajouter au panier
                            </button>
                        </form>
                    </div>
                </div>
            </div>
            <?php endforeach; ?>
        </div>

        <hr class="my-5">
        <p class="text-center">
            <a href="vider.php" class="btn btn-outline-danger"><i class="bi bi-trash"></i> Vider le panier</a>
            <a href="vider.php?demo_expire=1" class="btn btn-outline-warning ms-3">
                <i class="bi bi-clock"></i> Démo expiration (30 s)
            </a>
        </p>
    </div>
</body>
</html>
3. ajouter.php – Ajouter un produit au cookie
<?php
if (!isset($_POST['id'], $_POST['nom'], $_POST['prix'])) {
    header('Location: index.php');
    exit;
}

$id   = (int)$_POST['id'];
$nom  = $_POST['nom'];
$prix = (float)$_POST['prix'];

$panier = isset($_COOKIE['panier']) ? unserialize($_COOKIE['panier'], ["allowed_classes" => false]) : [];

if (isset($panier[$id])) {
    $panier[$id]['quantite']++;
} else {
    $panier[$id] = ['nom' => $nom, 'prix' => $prix, 'quantite' => 1];
}

setcookie('panier', serialize($panier), time() + (30 * 86400), '/', '', false, true);
header('Location: panier.php');
exit;
?>
4. panier.php – Affichage du panier
<?php
$panier = isset($_COOKIE['panier']) ? unserialize($_COOKIE['panier'], ["allowed_classes" => false]) : [];
$total = 0;
?>
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Mon panier</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet">
</head>
<body class="bg-light">
    <div class="container py-5">
        <h1 class="mb-4"><i class="bi bi-cart4 me-2"></i> Votre panier</h1>
        <a href="index.php" class="btn btn-outline-primary mb-4"><i class="bi bi-arrow-left"></i> Continuer mes achats</a>

        <?php if (empty($panier)): ?>
            <div class="alert alert-secondary text-center py-5">
                <i class="bi bi-cart-x display-1"></i>
                <h3>Votre panier est vide</h3>
            </div>
        <?php else: ?>
            <div class="table-responsive">
                <table class="table table-striped table-hover align-middle">
                    <thead class="table-dark">
                        <tr>
                            <th>Produit</th>
                            <th class="text-end">Prix unitaire</th>
                            <th class="text-center">Quantité</th>
                            <th class="text-end">Total</th>
                            <th></th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach($panier as $id => $item):
                            $sousTotal = $item['prix'] * $item['quantite'];
                            $total += $sousTotal;
                        ?>
                        <tr>
                            <td><strong><?= htmlspecialchars($item['nom']) ?></strong></td>
                            <td class="text-end"><?= number_format($item['prix'], 2) ?> €</td>
                            <td class="text-center"><span class="badge bg-primary fs-6"><?= $item['quantite'] ?></span></td>
                            <td class="text-end fw-bold"><?= number_format($sousTotal, 2) ?> €</td>
                            <td>
                                <a href="supprimer.php?id=<?= $id ?>" class="btn btn-sm btn-danger">
                                    <i class="bi bi-trash"></i>
                                </a>
                            </td>
                        </tr>
                        <?php endforeach; ?>
                        <tr class="table-success">
                            <th colspan="3" class="text-end">Total général</th>
                            <th class="text-end fs-4"><?= number_format($total, 2) ?> €</th>
                            <th></th>
                        </tr>
                    </tbody>
                </table>
            </div>
        <?php endif; ?>

        <div class="card mt-4">
            <div class="card-body">
                <h5>Cookie brut (pour déboguer)</h5>
                <code class="small"><?= htmlspecialchars($_COOKIE['panier'] ?? '(aucun cookie)') ?></code>
            </div>
        </div>
    </div>
</body>
</html>
5. supprimer.php – Supprimer un article du panier
<?php
if (!isset($_GET['id'])) {
    header('Location: panier.php');
    exit;
}
$id = (int)$_GET['id'];

$panier = isset($_COOKIE['panier']) ? unserialize($_COOKIE['panier'], ["allowed_classes" => false]) : [];

if (isset($panier[$id])) {
    unset($panier[$id]);
    if (empty($panier)) {
        setcookie('panier', '', time() - 3600, '/');
    } else {
        setcookie('panier', serialize($panier), time() + (30 * 86400), '/', '', false, true);
    }
}
header('Location: panier.php');
exit;
?>
6. vider.php – Vider ou faire expirer le panier
<?php
if (isset($_GET['demo_expire'])) {
    if (isset($_COOKIE['panier'])) {
        setcookie('panier', $_COOKIE['panier'], time() + 30, '/', '', false, true);
        echo "<!DOCTYPE html><html><head><title>Expiration</title>
              <link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css' rel='stylesheet'></head>
              <body class='bg-light'><div class='container py-5 text-center'>
              <div class='alert alert-warning'>Le panier expirera dans 30 secondes !</div>
              <a href='panier.php' class='btn btn-primary'>Voir le panier</a></div></body></html>";
    }
} else {
    setcookie('panier', '', time() - 3600, '/');
    unset($_COOKIE['panier']);
    header('Location: index.php');
}
?>

Rappel des fonctions PHP utilisées

  • serialize($var) → transforme un tableau/objet en chaîne texte
  • unserialize($chaine) → restaure la chaîne en tableau/objet
  • number_format($nombre, 2) → affiche un prix avec 2 décimales
  • setcookie(name, value, expire, path, domain, secure, httponly)
  • HttpOnly = true → le cookie n’est pas accessible en JavaScript (sécurité)

Bonne découverte des cookies !