Résumé sur PHP par COMADEV RDC
Dispensé par Luc Makwani
Blog : https://nazateprod.blogspot.com
Lien de la plateforme pour en savoir plus : https://www.w3schools.com
1. Qu’est-ce que PHP ? - Le tout premier script
Idée clé
PHP est un langage de script côté serveur : le code s’exécute sur le serveur web et renvoie du HTML au navigateur.
<?php
// premier script :
echo "Bonjour, monde !";
?>
Quiz
1. Où s’exécute le code PHP ?
2. Quel mot-clé affiche du texte ?
Réponses
1. Sur le serveur (côté serveur).
2. echo.
2. Installer et lancer PHP localement
Idée clé
• Windows/Mac/Linux : installez [XAMPP] ou [MAMP] qui embarquent Apache + PHP + MySQL.
• Les fichiers .php vont dans le dossier htdocs (XAMPP) ou Sites.
• Démarrez Apache puis ouvrez http://localhost/nomDuFichier.php.
Quiz
1. Quel logiciel lance le serveur web local ?
2. Quelle URL utilisez-vous pour tester ?
Réponses
1. Apache (via XAMPP/MAMP).
2. http://localhost/….
3. Syntaxe de base : balises, commentaires, variables
Idée clé
• Tout code PHP est placé entre <?php ... ?>.
• Commentaires : // (ligne) ou /* ... */ (bloc).
• Variables : toujours préfixées par $.
<?php
$age = 25; // variable entière
$nom = "Luc"; /* variable chaîne */
echo "$nom a $age ans.";
?>
Quiz
1. Quel symbole précède une variable ?
2. Comment écrire un commentaire sur une ligne ?
Réponses
1. $.
2. // commentaire.
4. Types et conversions simples
Idée clé
Types principaux : entier (int), nombre à virgule (float), booléen (bool), chaîne (string), tableau (array), objet (object), null (null). Conversion automatique, mais on peut forcer : (int)$str.
<?php
$prix = "19.99"; // string
$total = (float)$prix * 3;
var_dump($total); // float(59.97)
?>
Quiz
1. Comment forcer $prix à devenir un nombre ?
2. Quel type retourne var_dump(true) ?
Réponses
1. (float)$prix ou (double)$prix.
2. bool(true).
5. Opérateurs essentiels
Catégorie Exemple Résultat
Arithmétique 3 ** 2 9
Affectation $x += 5 ajoute 5
Comparaison $a == $b égalité (valeur)
Identique $a === $b valeur et type
Logique && `
Quiz
1. Quelle différence entre == et === ?
2. Quel opérateur élève 3 au carré ?
Réponses
1. === compare valeur et type.
2. **.
6. Contrôle de flux : if, else, switch
<?php
$note = 14;
if ($note >= 10) {
echo "Réussi";
} elseif ($note >= 8) {
echo "Rattrapage";
} else {
echo "Échoué";
}
$jour = 3;
switch ($jour) {
case 1: echo "Lundi"; break;
case 3: echo "Mercredi"; break;
default: echo "Autre";
}
?>
Quiz
1. Quel mot-clé termine un bloc switch ?
2. Quelle structure choisir pour plusieurs conditions numériques fixes ?
Réponses
1. break (souvent).
2. switch.
7. Boucles : while, for, foreach
<?php
for ($i = 1; $i <= 3; $i++) {
echo $i;
}
$fruits = ["pomme", "banane"];
foreach ($fruits as $f) {
echo $f;
}
?>
Quiz
1. Quelle boucle parcourt facilement un tableau ?
2. Dans for, quelle partie s’exécute après chaque itération ?
Réponses
1. foreach.
2. L’incrément ($i++).
8. Fonctions
<?php
function carre(int $n): int {
return $n * $n;
}
echo carre(4); // 16
?>
Quiz
1. Comment déclarer une fonction ?
2. À quoi sert : int après la parenthèse ?
Réponses
1. Avec function nom() {}.
2. À indiquer le type de retour (PHP 7+).
9. Tableaux (arrays)
<?php
// indexé
$nums = [10, 20];
// associatif
$personne = ["nom" => "Luc", "age" => 30];
// multidimensionnel
$notes = [
"Math" => [12, 15],
"Info" => [18, 20]
];
echo $personne["nom"]; // Luc
?>
Quiz
1. Comment accéder à la 2ᵉ note d’Info ?
2. Quelle fonction compte les éléments d’un tableau ?
Réponses
1. $notes["Info"][1] (index 0-based).
2. count($notes).
10. Variables superglobales (formulaires & URL)
Superglobale Usage rapide
$_GET données envoyées via URL
$_POST données de formulaire
$_SERVER infos serveur/entête
$_SESSION stockage par session
$_COOKIE stockage côté client
<?php
// url : exemple.php?nom=Luc
echo $_GET['nom'];
?>
Quiz
1. Quelle superglobale contient l’IP du client (REMOTE_ADDR) ?
2. Formulaire méthode POST : où lire les champs ?
Réponses
1. $_SERVER.
2. Dans $_POST.
11. Traiter un formulaire basique
HTML (form.html)
<form action="traitement.php" method="post">
<input type="text" name="nom">
<button>Envoyer</button>
</form>
PHP (traitement.php)
<?php
$nom = htmlspecialchars($_POST['nom'] ?? '');
echo "Bonjour $nom";
?>
Quiz
1. Pourquoi utiliser htmlspecialchars ?
2. Quelle méthode de formulaire masque les données dans l’URL ?
Réponses
1. Prévenir les attaques XSS.
2. POST.
12. Lire & écrire dans un fichier
<?php
file_put_contents("demo.txt", "Ligne 1\n", FILE_APPEND);
$contenu = file_get_contents("demo.txt");
echo nl2br($contenu);
?>
Quiz
1. Quel drapeau ajoute du texte sans écraser ?
2. Quelle fonction lit un fichier en chaîne ?
Réponses
1. FILE_APPEND.
2. file_get_contents.
13. Programmation orientée objet (OOP) en 60 s
<?php
class Compte {
public float $solde = 0;
public function deposer(float $mnt) {
$this->solde += $mnt;
}
}
$monCompte = new Compte();
$monCompte->deposer(50);
echo $monCompte->solde; // 50
?>
Quiz
1. Comment appelle-t-on une variable d’objet ?
2. Que signifie $this ?
Réponses
1. Propriété.
2. L’instance courante de la classe.
14. Gérer les erreurs
<?php
try {
$x = 10 / 0;
} catch (DivisionByZeroError $e) {
echo "Erreur : " . $e->getMessage();
}
?>
Quiz
1. Quel bloc entoure le code à risque ?
2. Quelle fonction définit le niveau d’affichage d’erreurs ?
Réponses
1. try { … }.
2. error_reporting() ou ini_set('display_errors',...).
15. Se connecter à MySQL en toute sécurité (PDO)
<?php
$pdo = new PDO("mysql:host=localhost;dbname=test;charset=utf8", "user", "pass");
$sql = "SELECT * FROM users WHERE email = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$_POST['email']]);
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
Quiz
1. Pourquoi utiliser prepare() ?
2. Quelle constante retourne un tableau associatif ?
Réponses
1. Pour éviter les injections SQL (requêtes préparées).
2. PDO::FETCH_ASSOC.
16. Premiers réflexes sécurité
• Échapper toute sortie HTML : htmlspecialchars.
• Valider / filtrer les entrées : filter_input.
• Préparer vos requêtes SQL.
• CSRF : tokens cachés dans les formulaires.
• Sessions : session_start(); avant tout accès.
Quiz
1. Quelle fonction filtre une adresse e-mail ?
2. Où place-t-on session_start() ?
Réponses
1. filter_var($email, FILTER_VALIDATE_EMAIL).
2. Tout en haut du script, avant tout HTML.
17. Et après ?
• Plongez dans la documentation officielle (php.net).
• Testez les katas sur codewars.com ou exercism.org.
• Explorez un micro-framework (Slim) puis Laravel.
• Si vous visez WordPress, étudiez son API (hooks, thèmes, plugins).
Quiz final
1. Quelle ressource en ligne répertorie toutes les fonctions PHP ?
2. Quel framework MVC populaire repose sur PHP ?
Réponses
1. Le manuel officiel sur php.net.
2. Laravel.
Exercice complet : « Ajouter un contact »
(tout tient dans trois fichiers, fonctionne sous XAMPP/MAMP, MySQL ≥ 8.0 et Bootstrap 5.3.7)
1. Pré-requis (rapide)
Outil Pourquoi Où le trouver
XAMPP / MAMP Lance Apache + PHP + MySQL localement apachefriends.org / mamp.info
phpMyAdmin Créer vos bases/tables via l’interface graphique déjà inclus dans XAMPP/MAMP
Bootstrap 5.3.7 Mise en page réactive sans CSS « from scratch » CDN : https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css
Navigateur Tester http://localhost/projet/index.php –
2. Base de données à 2 champs
-- À exécuter dans phpMyAdmin (onglet SQL)
CREATE DATABASE tutoriel CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE tutoriel;
CREATE TABLE contacts (
id INT PRIMARY KEY AUTO_INCREMENT,
nom VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE
);
Le mot-clé AUTO_INCREMENT crée un identifiant unique automatiquement.
3. Architecture du mini-projet
/projet
│ config.php ← Connexion PDO
│ index.php ← Formulaire Bootstrap
└ insert.php ← Traitement + insertion
4. Fichier config.php
<?php
// ➜ adaptez login / mot de passe
$dsn = 'mysql:host=localhost;dbname=tutoriel;charset=utf8mb4';
$user = 'root';
$pass = '';
try {
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
} catch (PDOException $e) {
exit('Erreur de connexion : ' . $e->getMessage());
}
?>
Fonctionnalités clés
• PDO fournit une interface unique pour MySQL, PostgreSQL, etc.
• ATTR_ERRMODE = EXCEPTION déclenche une exception plutôt qu’un simple warning.
• UTF-8 est forcé au niveau DSN pour éviter les soucis d’accents.
(voir la doc officielle de PDO::prepare pour le reste des bonnes pratiques)
5. Fichier index.php – le formulaire Bootstrap
<?php require 'config.php'; ?>
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Ajouter un contact</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container py-4">
<h1 class="mb-4">Nouveau contact</h1>
<form action="insert.php" method="post" class="row g-3 needs-validation" novalidate>
<div class="col-md-6">
<label for="nom" class="form-label">Nom</label>
<input type="text" name="nom" id="nom" class="form-control" required>
<div class="invalid-feedback">Nom obligatoire</div>
</div>
<div class="col-md-6">
<label for="email" class="form-label">E-mail</label>
<input type="email" name="email" id="email" class="form-control" required>
<div class="invalid-feedback">E-mail valide requis</div>
</div>
<div class="col-12">
<button class="btn btn-primary">Enregistrer</button>
</div>
</form>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.bundle.min.js"></script>
<!-- validation côté client Bootstrap -->
<script>
(() => {
const forms = document.querySelectorAll('.needs-validation');
Array.from(forms).forEach(form => {
form.addEventListener('submit', e => {
if (!form.checkValidity()) { e.preventDefault(); e.stopPropagation(); }
form.classList.add('was-validated');
}, false);
});
})();
</script>
</body>
</html>
Les balises <form> / <input> sont standardisées par le W3C ; voir MDN pour le détail des attributs.
6. Fichier insert.php – traitement sécurisé
<?php
require 'config.php';
$nom = trim($_POST['nom'] ?? '');
$email = trim($_POST['email'] ?? '');
if ($nom === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
exit('Données invalides.');
}
$sql = "INSERT INTO contacts (nom, email) VALUES (?, ?)";
$stmt = $pdo->prepare($sql); // requête préparée → protège des injections SQL
$stmt->execute([$nom, $email]); // lie les paramètres et exécute
header('Location: index.php?ok=1');
exit;
?>
Fonctionnalités expliquées
Ligne Rôle
filter_var Valide côté serveur que l’e-mail est conforme RFC
prepare + execute Séparent la requête du contenu utilisateur ⇒ sécurité anti-injection SQL
Redirection HTTP Empêche le double envoi si l’utilisateur recharge la page
7. Points essentiels pour un·e débutant·e
1. Affichage des erreurs
2. ini_set('display_errors', 1);
3. error_reporting(E_ALL);
Activez-les en local mais désactivez-les en production.
4. Structure MVC plus tard – Séparez la logique (PHP), la présentation (HTML/Bootstrap) et la base de données.
5. Composer :
6. composer init # crée composer.json
7. composer require phpmailer/phpmailer
Gère les dépendances automatiquement.
8. Sécurité minimale
o htmlspecialchars() sur toute sortie issue d’un utilisateur.
o Jeton CSRF caché dans chaque formulaire.
o HTTPS obligatoire en production.
9. Charsets
o Base, tables et connexion en utf8mb4.
o Toujours préciser charset=utf8mb4 dans votre DSN.
10. Débogage PDO
11. $stmt->debugDumpParams(); // affiche la requête réelle + les valeurs liées
12. ``` 5
13.
8. Bibliothèques & ressources conseillées
Domaine Lib / Ressource Pourquoi
PHP PHP Manual (php.net) Référence exhaustive officielle
Carbon (dates), PHPMailer (e-mails), Guzzle (HTTP) Outils courants, installables via Composer
MySQL MySQL 8.4 Reference Manual Syntaxe SQL, indexation, transactions
mysqlsh (shell JSON, import/export) Administration moderne
HTML / DOM MDN Web Docs (HTML & Forms) Tutoriels, accessibilité, compatibilité
Bootstrap getbootstrap.com docs (v5.3.x) Composants, utilitaires, JS toast, etc.
Outils bonus VS Code + extensions PHP IntelliSense, PHP Debug Autocomplétion et pas-à-pas
🔧 1. STRUCTURE DU PROJET
/projet-crud/
├── config.php ← connexion PDO
├── index.php ← liste des contacts
├── ajouter.php ← formulaire d’ajout
├── inserer.php ← traitement ajout
├── modifier.php ← formulaire prérempli
├── mettre_a_jour.php ← traitement mise à jour
└── supprimer.php ← suppression d’un contact
🛢️ 2. BASE DE DONNÉES
CREATE DATABASE tutoriel CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE tutoriel;
CREATE TABLE contacts (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE
);
⚙️ 3. config.php
<?php
$dsn = 'mysql:host=localhost;dbname=tutoriel;charset=utf8mb4';
$user = 'root'; // ton utilisateur
$pass = ''; // ton mot de passe
try {
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
} catch (PDOException $e) {
exit("Erreur de connexion : " . $e->getMessage());
}
?>
📄 4. index.php – Liste des contacts
<?php require 'config.php'; ?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Liste des contacts</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container py-5">
<h1 class="mb-4">📋 Liste des contacts</h1>
<a href="ajouter.php" class="btn btn-success mb-3">➕ Ajouter un contact</a>
<table class="table table-bordered table-striped">
<thead class="table-dark">
<tr><th>ID</th><th>Nom</th><th>Email</th><th>Actions</th></tr>
</thead>
<tbody>
<?php
$stmt = $pdo->query("SELECT * FROM contacts ORDER BY id DESC");
foreach ($stmt as $row) {
echo "<tr>
<td>{$row['id']}</td>
<td>{$row['nom']}</td>
<td>{$row['email']}</td>
<td>
<a href='modifier.php?id={$row['id']}' class='btn btn-sm btn-warning'>✏️</a>
<a href='supprimer.php?id={$row['id']}' class='btn btn-sm btn-danger' onclick='return confirm(\"Supprimer ce contact ?\")'>🗑️</a>
</td>
</tr>";
}
?>
</tbody>
</table>
</body>
</html>
🆕 5. ajouter.php – Formulaire d’ajout
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Ajouter un contact</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container py-5">
<h2>➕ Nouveau contact</h2>
<form action="inserer.php" method="post">
<div class="mb-3">
<label>Nom</label>
<input type="text" name="nom" class="form-control" required>
</div>
<div class="mb-3">
<label>Email</label>
<input type="email" name="email" class="form-control" required>
</div>
<button class="btn btn-primary">Enregistrer</button>
</form>
</body>
</html>
✅ 6. inserer.php – Insertion
<?php
require 'config.php';
$nom = trim($_POST['nom']);
$email = trim($_POST['email']);
if ($nom && filter_var($email, FILTER_VALIDATE_EMAIL)) {
$sql = "INSERT INTO contacts (nom, email) VALUES (?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$nom, $email]);
}
header("Location: index.php");
exit;
✏️ 7. modifier.php – Formulaire de modification
<?php
require 'config.php';
$id = $_GET['id'];
$stmt = $pdo->prepare("SELECT * FROM contacts WHERE id = ?");
$stmt->execute([$id]);
$contact = $stmt->fetch();
if (!$contact) exit("Contact introuvable !");
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Modifier contact</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container py-5">
<h2>✏️ Modifier contact</h2>
<form action="mettre_a_jour.php" method="post">
<input type="hidden" name="id" value="<?= $contact['id'] ?>">
<div class="mb-3">
<label>Nom</label>
<input type="text" name="nom" class="form-control" value="<?= htmlspecialchars($contact['nom']) ?>" required>
</div>
<div class="mb-3">
<label>Email</label>
<input type="email" name="email" class="form-control" value="<?= htmlspecialchars($contact['email']) ?>" required>
</div>
<button class="btn btn-primary">Mettre à jour</button>
</form>
</body>
</html>
🔁 8. mettre_a_jour.php – Traitement de mise à jour
<?php
require 'config.php';
$id = $_POST['id'];
$nom = trim($_POST['nom']);
$email = trim($_POST['email']);
if ($id && $nom && filter_var($email, FILTER_VALIDATE_EMAIL)) {
$sql = "UPDATE contacts SET nom = ?, email = ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$nom, $email, $id]);
}
header("Location: index.php");
exit;
❌ 9. supprimer.php – Suppression
<?php
require 'config.php';
$id = $_GET['id'] ?? null;
if ($id) {
$stmt = $pdo->prepare("DELETE FROM contacts WHERE id = ?");
$stmt->execute([$id]);
}
header("Location: index.php");
exit;
📚 À Apprendre pour approfondir
MySQL
• MySQL Officiel
• Types de données : INT, VARCHAR, TEXT, DATE, ENUM, etc.
• Index, clés primaires et étrangères
• Fonctions utiles : NOW(), CONCAT(), LEFT(), LIKE, GROUP BY, JOIN
PHP
• Documentation PHP Officielle
• Fonctions de manipulation de chaînes (strtoupper, substr, str_replace)
• Sessions ($_SESSION)
• Fichiers (fopen, file_put_contents, move_uploaded_file)
• Sécurité : htmlspecialchars, password_hash, filter_var
HTML + Bootstrap
• Bootstrap 5
• MDN HTML
• Formulaires : input, textarea, select, required, type=email
• Classes utiles Bootstrap : container, form-control, btn, table, alert
1. Nouvelle arborescence
/projet-crud/
│ config.php
│ auth.php ← helpers session + protection
│ login.php ← formulaire de connexion
│ register.php ← inscription
│ logout.php
│ index.php ← liste + recherche + pagination (protégé)
│ ajouter.php inserer.php
│ modifier.php mettre_a_jour.php
└ supprimer.php
2. Schéma MySQL (ajout de la table users)
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Le mot de passe est stocké chiffré avec password_hash
3. auth.php – sessions et garde-barrière
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start(); // lance / reprend la session
}
function is_logged_in(): bool {
return isset($_SESSION['user_id']);
}
function require_login(): void {
if (!is_logged_in()) {
header('Location: login.php');
exit;
}
}
?>
session_start() crée ou reprend la session en toute sécurité ; toutes les pages protégées l’appellent.
4. register.php – inscription
<?php
require 'config.php';
require 'auth.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$nom = trim($_POST['nom'] ?? '');
$email = trim($_POST['email'] ?? '');
$pass = $_POST['password'] ?? '';
if ($nom && filter_var($email, FILTER_VALIDATE_EMAIL) && strlen($pass) >= 6) {
$hash = password_hash($pass, PASSWORD_DEFAULT);
$stmt = $pdo->prepare('INSERT INTO users (nom,email,password_hash) VALUES (?,?,?)');
$stmt->execute([$nom, $email, $hash]);
header('Location: login.php?inscrit=1');
exit;
}
}
?>
<!doctype html><html lang="fr"><head>
<meta charset="utf-8"><title>Inscription</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
</head><body class="container py-5">
<h2>Créer un compte</h2>
<form method="post" class="col-md-6">
<div class="mb-3"><label>Nom</label><input class="form-control" name="nom" required></div>
<div class="mb-3"><label>E-mail</label><input class="form-control" type="email" name="email" required></div>
<div class="mb-3"><label>Mot de passe (≥ 6 car.)</label><input class="form-control" type="password" name="password" required></div>
<button class="btn btn-primary">S’inscrire</button>
<a href="login.php" class="btn btn-link">Déjà inscrit ?</a>
</form>
</body></html>
5. login.php / logout.php
login.php
<?php
require 'config.php';
require 'auth.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = trim($_POST['email'] ?? '');
$pass = $_POST['password'] ?? '';
$stmt = $pdo->prepare('SELECT id,password_hash FROM users WHERE email = ?');
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user && password_verify($pass, $user['password_hash'])) { // vérifie le hash
$_SESSION['user_id'] = $user['id'];
header('Location: index.php');
exit;
}
$erreur = 'Identifiants invalides';
}
?>
<!doctype html><html lang="fr"><head>
<meta charset="utf-8"><title>Connexion</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
</head><body class="container py-5">
<h2>Se connecter</h2>
<?php if (!empty($erreur)) echo "<div class='alert alert-danger'>$erreur</div>"; ?>
<form method="post" class="col-md-6">
<div class="mb-3"><label>E-mail</label><input class="form-control" type="email" name="email" required></div>
<div class="mb-3"><label>Mot de passe</label><input class="form-control" type="password" name="password" required></div>
<button class="btn btn-primary">Connexion</button>
<a href="register.php" class="btn btn-link">Créer un compte</a>
</form>
</body></html>
logout.php
<?php
require 'auth.php';
session_destroy();
header('Location: login.php');
exit;
6. index.php – recherche + pagination (protégé)
<?php
require 'config.php';
require 'auth.php';
require_login(); // ▶︎ redirige vers login si non connecté
$limit = 5; // 5-lignes par page
$page = max(1, (int)($_GET['p'] ?? 1));
$offset = ($page - 1) * $limit;
$search = trim($_GET['q'] ?? '');
$params = [];
$where = '';
if ($search !== '') {
$where = 'WHERE nom LIKE ? OR email LIKE ?';
$like = "%$search%";
$params = [$like, $like];
}
/* total pour pagination */
$stmt = $pdo->prepare("SELECT COUNT(*) FROM contacts $where");
$stmt->execute($params);
$total = (int)$stmt->fetchColumn();
$pages = (int)ceil($total / $limit);
/* données à afficher */
$sql = "SELECT * FROM contacts $where ORDER BY id DESC LIMIT $limit OFFSET $offset";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$contacts = $stmt->fetchAll();
?>
<!doctype html><html lang="fr"><head>
<meta charset="utf-8"><title>Contacts</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
</head><body class="container py-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="m-0">📋 Contacts</h1>
<div>
<a href="ajouter.php" class="btn btn-success">➕ Ajouter</a>
<a href="logout.php" class="btn btn-outline-secondary">Déconnexion</a>
</div>
</div>
<form class="input-group mb-3" method="get">
<input type="text" class="form-control" placeholder="Rechercher nom ou e-mail…" name="q" value="<?= htmlspecialchars($search) ?>">
<button class="btn btn-outline-secondary">🔍</button>
</form>
<table class="table table-bordered">
<thead class="table-dark"><tr><th>ID</th><th>Nom</th><th>Email</th><th>Actions</th></tr></thead>
<tbody>
<?php foreach ($contacts as $c): ?>
<tr>
<td><?= $c['id'] ?></td>
<td><?= htmlspecialchars($c['nom']) ?></td>
<td><?= htmlspecialchars($c['email']) ?></td>
<td>
<a href="modifier.php?id=<?= $c['id'] ?>" class="btn btn-sm btn-warning">✏️</a>
<a href="supprimer.php?id=<?= $c['id'] ?>"
class="btn btn-sm btn-danger"
onclick="return confirm('Supprimer ce contact ?')">🗑️</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<!-- pagination Bootstrap -->
<nav aria-label="Pagination">
<ul class="pagination">
<?php if ($page > 1): ?>
<li class="page-item"><a class="page-link" href="?q=<?= urlencode($search) ?>&p=<?= $page-1 ?>">«</a></li>
<?php endif; ?>
<?php for ($i = 1; $i <= $pages; $i++): ?>
<li class="page-item <?= $i == $page ? 'active' : '' ?>">
<a class="page-link" href="?q=<?= urlencode($search) ?>&p=<?= $i ?>"><?= $i ?></a>
</li>
<?php endfor; ?>
<?php if ($page < $pages): ?>
<li class="page-item"><a class="page-link" href="?q=<?= urlencode($search) ?>&p=<?= $page+1 ?>">»</a></li>
<?php endif; ?>
</ul>
</nav>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.bundle.min.js"></script>
</body></html>
La structure suit le composant « Pagination » Bootstrap 5.
7. Aides-mémoire & bonnes pratiques
Sujet Point-clés Références
Hash mot de passe password_hash, password_verify, re-hash via password_needs_rehash
Sessions Démarrer tôt, toujours régénérer l’ID après connexion (session_regenerate_id)
Recherche LIKE Utiliser ? + %val% (requête préparée) ⇒ protège contre injections (voir code)
Pagination SQL LIMIT … OFFSET … ; compter total via COUNT(*) –
Commentaires