Leçon 13 / 15

Assembler le socle et l’accès privé

45 min PHP 8 — structurer une application (PDO, POO, sessions) Gratuit

Objectif : Configurer l’application et mettre en place la connexion, la déconnexion et la protection des pages.

Un formulaire de connexion ne protège rien si ses données ne sont pas reliées au service d’authentification et à la session. Dans cette leçon, vous raccordez ces éléments dans le point d’entrée, puis vous affichez une page privée seulement après authentification.

Vous avez déjà défini les rôles du modèle, du dépôt, du contrôleur et du gabarit ; reportez-vous aux leçons précédentes pour ces responsabilités. Ici, le point d’entrée démarre la session, choisit l’action, délègue le traitement puis transmet les données au gabarit commun.

Configurer le secret et l’accès à MySQL

Le hachage attendu ne doit pas être écrit dans une page publique ni dans le dépôt de code. Placez-le dans une variable d’environnement, avec les paramètres MySQL utilisés par la classe de connexion. Vous pouvez générer un hachage depuis le terminal avec PHP, puis copier le résultat dans votre configuration locale.

Terminal
export CARNET_MOT_DE_PASSE_HASH='copiez-ici-le-hachage-produit-par-password_hash'
export DB_HOST='127.0.0.1'
export DB_NAME='carnet'
export DB_USER='carnet'
export DB_PASSWORD='mot-de-passe-mysql'

Raccorder le traitement de connexion

Le contrôleur reçoit le service d’authentification par son constructeur. Il ne rend pas de HTML : il vérifie le formulaire et renvoie un résultat au point d’entrée. En cas de succès, le point d’entrée régénère l’identifiant de session avant de marquer la session comme authentifiée.

src/Controleur/AuthentificationControleur.phpPHP
<?php

declare(strict_types=1);

namespace Carnet\Controleur;

use Carnet\Service\Authentification;

final class AuthentificationControleur
{
    public function __construct(
        private readonly Authentification $authentification
    ) {
    }

    public function verifier(string $motDePasse): bool
    {
        return $this->authentification->verifier($motDePasse);
    }
}

Faire du point d’entrée le passage obligé

Le code suivant suppose un autoload PSR-4 configuré pour le préfixe `Carnet\` vers `src/`, par exemple avec Composer. Le point d’entrée traite les formulaires POST, puis redirige avec le statut 303 afin que le navigateur effectue ensuite une requête GET. Une requête privée non authentifiée affiche le formulaire de connexion, tandis qu’une session validée accède à l’écran privé provisoire.

public/index.phpPHP
<?php

declare(strict_types=1);

use Carnet\Controleur\AuthentificationControleur;
use Carnet\Service\Authentification;

require dirname(__DIR__) . '/vendor/autoload.php';

session_start();

$action = (string) ($_POST['action'] ?? $_GET['action'] ?? '');
$message = null;

if ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'deconnexion') {
    $_SESSION = [];
    if (ini_get('session.use_cookies')) {
        $parametres = session_get_cookie_params();
        setcookie(session_name(), '', time() - 42000, $parametres['path'], $parametres['domain'], $parametres['secure'], $parametres['httponly']);
    }
    session_destroy();
    header('Location: /', true, 303);
    exit;
}

$authentifie = ($_SESSION['authentifie'] ?? false) === true;

if ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'connexion') {
    $hachage = getenv('CARNET_MOT_DE_PASSE_HASH');
    if ($hachage === false || $hachage === '') {
        http_response_code(500);
        $message = 'La connexion n’est pas configurée.';
    } else {
        $controleur = new AuthentificationControleur(new Authentification($hachage));
        $motDePasse = (string) ($_POST['mot_de_passe'] ?? '');
        if ($controleur->verifier($motDePasse)) {
            session_regenerate_id(true);
            $_SESSION['authentifie'] = true;
            header('Location: /', true, 303);
            exit;
        }
        $message = 'Mot de passe incorrect.';
    }
}

$authentifie = ($_SESSION['authentifie'] ?? false) === true;
$titre = $authentifie ? 'Mes contacts' : 'Connexion';
$recherche = '';
$contacts = [];
$contact = [];

ob_start();
if ($authentifie) {
    require dirname(__DIR__) . '/templates/contacts.php';
} else {
    require dirname(__DIR__) . '/templates/connexion.php';
}
$contenu = (string) ob_get_clean();
require dirname(__DIR__) . '/templates/gabarit.php';

La variable `$contenu` contient le rendu du gabarit adapté à l’état de session. Cette mise en place ne traite pas encore les opérations sur les contacts : la page privée constitue un écran de raccordement, que les prochaines leçons remplaceront par la liste et les formulaires fonctionnels.

Créer les gabarits de connexion et de page privée

templates/connexion.phpPHP
<h1>Connexion</h1>
<form class="carnet-formulaire" action="/" method="post">
  <input type="hidden" name="action" value="connexion">
  <label class="carnet-champ">Mot de passe
    <input type="password" name="mot_de_passe" required autocomplete="current-password">
  </label>
  <div class="carnet-actions">
    <button class="carnet-bouton" type="submit">Se connecter</button>
  </div>
</form>
templates/contacts.phpPHP
<h1>Mes contacts</h1>
<p>Vous êtes connecté. La liste des contacts sera affichée ici.</p>
<form action="/" method="post">
  <input type="hidden" name="action" value="deconnexion">
  <button class="carnet-bouton" type="submit">Déconnexion</button>
</form>
templates/gabarit.phpPHP
<!doctype html>
<html lang="fr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title><?= htmlspecialchars($titre, ENT_QUOTES, 'UTF-8') ?> — Carnet privé</title>
  <link rel="stylesheet" href="/style.css">
</head>
<body class="carnet-page">
  <header class="carnet-entete">
    <p>Carnet privé</p>
    <?php if ($authentifie): ?>
      <nav class="carnet-navigation" aria-label="Navigation principale">
        <a href="/">Contacts</a>
        <form action="/" method="post">
          <input type="hidden" name="action" value="deconnexion">
          <button class="carnet-bouton" type="submit">Déconnexion</button>
        </form>
      </nav>
    <?php endif; ?>
  </header>
  <main class="carnet-contenu">
    <?php if ($message !== null): ?>
      <p class="carnet-message" role="status"><?= htmlspecialchars($message, ENT_QUOTES, 'UTF-8') ?></p>
    <?php endif; ?>
    <?= $contenu ?>
  </main>
  <footer class="carnet-pied"><p>Carnet de contacts privé</p></footer>
</body>
</html>

Appliquer le contrat visuel

Toutes les règles de présentation restent dans l’unique feuille `public/style.css`. Le bloc ci-dessous utilise les variables et classes prévues par le contrat ; il fournit notamment un style effectif aux classes de structure et de formulaire employées par les gabarits.

public/style.cssCSS
:root {
  --carnet-couleur-texte: #20252b;
  --carnet-couleur-fond: #f4f6f8;
  --carnet-couleur-surface: #ffffff;
  --carnet-couleur-accent: #176b5b;
  --carnet-couleur-erreur: #a62929;
  --carnet-espace: 1rem;
  --carnet-espace-large: 2rem;
  --carnet-rayon: 0.5rem;
  --carnet-typographie: system-ui, sans-serif;
}

.carnet-page {
  margin: 0;
  color: var(--carnet-couleur-texte);
  background: var(--carnet-couleur-fond);
  font-family: var(--carnet-typographie);
}

.carnet-entete,
.carnet-contenu,
.carnet-pied {
  padding: var(--carnet-espace);
}

.carnet-entete,
.carnet-pied {
  background: var(--carnet-couleur-surface);
}

.carnet-contenu {
  max-width: 48rem;
  margin: var(--carnet-espace-large) auto;
  background: var(--carnet-couleur-surface);
  border-radius: var(--carnet-rayon);
}

.carnet-navigation,
.carnet-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--carnet-espace);
}

.carnet-formulaire {
  display: grid;
  gap: var(--carnet-espace);
}

.carnet-champ {
  display: grid;
  gap: 0.25rem;
}

.carnet-message {
  color: var(--carnet-couleur-erreur);
}

.carnet-bouton {
  padding: 0.5rem 1rem;
  color: var(--carnet-couleur-surface);
  background: var(--carnet-couleur-accent);
  border: 0;
  border-radius: var(--carnet-rayon);
  font: inherit;
  cursor: pointer;
}

input {
  padding: 0.5rem;
  font: inherit;
}
Aperçu du rendu
Chargement de l'aperçu…

Aperçu appliqué au document HTML de cette leçon : la règle CSS est injectée dans la page pour voir son effet réel.

Testez les deux chemins essentiels : un mot de passe invalide doit laisser la session anonyme et afficher un message ; le bon mot de passe doit rediriger vers la page privée. La déconnexion vide la session, retire son cookie et renvoie vers le formulaire de connexion. Gardez en tête que `BaseDeDonnees` et `ContactDepot` seront raccordés lorsque les pages privées liront et modifieront réellement les contacts.

À retenir

  • Le point d’entrée est le passage unique qui démarre la session, traite la connexion ou la déconnexion et contrôle l’accès.
  • Le contrôleur délègue la vérification au service ; il ne produit pas de HTML.
  • Après une connexion valide, régénérez l’identifiant de session avant d’enregistrer l’état authentifié.
  • Conservez le hachage du mot de passe et les secrets de connexion dans la configuration de l’environnement, pas dans les fichiers publics.
  • Les gabarits affichent le résultat ; la feuille CSS unique applique les classes et variables contractuelles.

Dans la leçon suivante, vous ajouterez le traitement des formulaires d’ajout et de modification ainsi que les actions de suppression au parcours désormais protégé.

Envie d'aller plus loin ?

Les cours Enovatic sont gratuits et le resteront. Si vous cherchez un accompagnement encadré, nos formations professionnelles sont animées par les mêmes formateurs.