Cours Complet PHP
Cours Complet PHP
Deuxième année
11. Programmation orientée objet en PHP
12. Bases de données et MySQL
13. PDO (PHP Data Objects)
14. Sécurité en PHP
15. Gestion des erreurs et exceptions
16. API REST avec PHP
17. Frameworks PHP (introduction à Laravel)
18. Tests unitaires en PHP
19. Déploiement d'applications PHP
20. Bonnes pratiques et patterns de conception
Première année
1. Introduction à PHP
1.1 Qu'est-ce que PHP ?
PHP (Hypertext Preprocessor) est un langage de script côté serveur conçu pour le
développement web. Il peut être intégré directement dans le HTML et est largement utilisé pour
créer des sites web dynamiques.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Mon premier script PHP</title>
</head>
<body>
<h1>Bienvenue dans le monde de PHP !</h1>
<?php
echo "<p>Ceci est généré par PHP : " . phpversion() . "</p>";
?>
</body>
</html>
Exercice 1.1
Créez un script PHP qui affiche "Bonjour, [votre nom] !" en utilisant une variable pour stocker
votre nom.
Corrigé 1.1
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 1.1</title>
</head>
<body>
<?php
$nom = "Alice"; // Remplacez par votre nom
echo "<h1>Bonjour, $nom !</h1>";
?>
</body>
</html>
<?php
// Ceci est un commentaire sur une ligne
/*
Ceci est un commentaire
sur plusieurs lignes
*/
Exercice 1.2
Créez un script PHP qui calcule et affiche l'aire d'un rectangle. Utilisez des variables pour la
longueur et la largeur, et ajoutez des commentaires pour expliquer votre code.
Corrigé 1.2
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 1.2 - Calcul de l'aire d'un rectangle</title>
</head>
<body>
<?php
// Définition des dimensions du rectangle
$longueur = 5; // en mètres
$largeur = 3; // en mètres
// Calcul de l'aire
$aire = $longueur * $largeur;
// Affichage du résultat
echo "<p>L'aire d'un rectangle de longueur $longueur m et de largeur
$largeur m est de $aire m².</p>";
?>
</body>
</html>
<?php
$age = 25;
$nom = "Alice";
$estEtudiant = true;
?>
Entiers (integer)
Nombres à virgule flottante (float)
Chaînes de caractères (string)
Booléens (boolean)
Tableaux (array)
Objets (object)
NULL
<?php
$entier = 42;
$flottant = 3.14;
$chaine = "Bonjour";
$booleen = true;
$tableau = [1, 2, 3];
$objet = new stdClass();
$nul = NULL;
<?php
$nombre = "42";
$nombreEntier = (int)$nombre; // Conversion explicite en entier
2.4 Constantes
Les constantes sont des identifiants pour des valeurs simples. Contrairement aux variables,
leur valeur ne peut pas être modifiée pendant l'exécution du script.
<?php
define("PI", 3.14159);
const MA_CONSTANTE = "Valeur constante";
Exercice 2.1
Créez un script PHP qui déclare des variables de différents types (entier, flottant, chaîne,
booléen) et affichez leur type et leur valeur.
Corrigé 2.1
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 2.1 - Types de variables</title>
</head>
<body>
<?php
$entier = 42;
$flottant = 3.14;
$chaine = "Hello, PHP!";
$booleen = true;
Exercice 2.2
Créez un script PHP qui calcule le périmètre et l'aire d'un cercle. Utilisez une constante pour π
et une variable pour le rayon. Affichez les résultats avec 2 décimales.
Corrigé 2.2
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 2.2 - Calcul du cercle</title>
</head>
<body>
<?php
// Définition de la constante PI
define("PI", 3.14159);
// Rayon du cercle
$rayon = 5;
// Calculs
$perimetre = 2 * PI * $rayon;
$aire = PI * $rayon * $rayon;
3. Structures de contrôle
Les structures de contrôle permettent de modifier le flux d'exécution d'un script PHP en fonction
de certaines conditions.
<?php
$age = 18;
if ($age < 18) {
echo "Vous êtes mineur.";
} elseif ($age == 18) {
echo "Vous venez d'atteindre la majorité.";
} else {
echo "Vous êtes majeur.";
}
?>
3.2 Switch
<?php
$jour = "Mardi";
switch ($jour) {
case "Lundi":
echo "C'est le début de la semaine.";
break;
case "Mardi":
case "Mercredi":
case "Jeudi":
echo "C'est le milieu de la semaine.";
break;
case "Vendredi":
echo "C'est bientôt le week-end.";
break;
default:
echo "C'est le week-end !";
}
?>
<?php
// Boucle while
$i = 0;
while ($i < 5) {
echo $i . " ";
$i++;
}
// Boucle do-while
$j = 0;
do {
echo $j . " ";
$j++;
} while ($j < 5);
// Boucle for
for ($k = 0; $k < 5; $k++) {
echo $k . " ";
}
// Boucle foreach
$fruits = ["pomme", "banane", "orange"];
foreach ($fruits as $fruit) {
echo $fruit . " ";
}
?>
<?php
for ($i = 0; $i < 10; $i++) {
if ($i == 5) {
continue; // Saute l'itération quand $i est 5
}
if ($i == 8) {
break; // Sort de la boucle quand $i est 8
}
echo $i . " ";
}
?>
Exercice 3.1
Écrivez un script PHP qui affiche les nombres de 1 à 100. Pour les multiples de 3, affichez
"Fizz" au lieu du nombre, et pour les multiples de 5, affichez "Buzz". Pour les nombres qui sont
à la fois des multiples de 3 et 5, affichez "FizzBuzz".
Corrigé 3.1
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 3.1 - FizzBuzz</title>
</head>
<body>
<h2>FizzBuzz</h2>
<?php
for ($i = 1; $i <= 100; $i++) {
if ($i % 3 == 0 && $i % 5 == 0) {
echo "FizzBuzz ";
} elseif ($i % 3 == 0) {
echo "Fizz ";
} elseif ($i % 5 == 0) {
echo "Buzz ";
} else {
echo $i . " ";
}
}
?>
</body>
</html>
Exercice 3.2
Créez un script PHP qui génère une table de multiplication pour les nombres de 1 à 10. Utilisez
des boucles imbriquées pour créer un tableau HTML.
Corrigé 3.2
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 3.2 - Table de multiplication</title>
<style>
table { border-collapse: collapse; }
td { border: 1px solid black; padding: 5px; text-align: center; }
</style>
</head>
<body>
<h2>Table de multiplication</h2>
<table>
<?php
for ($i = 1; $i <= 10; $i++) {
echo "<tr>";
for ($j = 1; $j <= 10; $j++) {
echo "<td>" . ($i * $j) . "</td>";
}
echo "</tr>";
}
?>
</table>
</body>
</html>
4. Fonctions
Les fonctions sont des blocs de code réutilisables qui effectuent une tâche spécifique.
<?php
// Définition d'une fonction
function saluer($nom) {
return "
### 4. Fonctions
Les fonctions sont des blocs de code réutilisables qui effectuent une tâche
spécifique. Elles permettent d'organiser et de structurer le code, facilitant
ainsi sa maintenance et sa réutilisation.
```php
<?php
// Définition d'une fonction
function saluer($nom) {
return "Bonjour, " . $nom . " !";
}
// Appel de la fonction
echo saluer("Alice"); // Affiche : Bonjour, Alice !
?>
<?php
function additionner($a, $b) {
return $a + $b;
}
<?php
function puissance($base, $exposant = 2) {
return pow($base, $exposant);
}
<?php
function calculerStatistiques($nombres) {
$somme = array_sum($nombres);
$moyenne = $somme / count($nombres);
$max = max($nombres);
$min = min($nombres);
return [
'somme' => $somme,
'moyenne' => $moyenne,
'max' => $max,
'min' => $min
];
}
<?php
$variableGlobale = "Je suis globale";
function testPortee() {
$variableLocale = "Je suis locale";
global $variableGlobale;
echo $variableGlobale; // Accessible
echo $variableLocale; // Accessible
}
testPortee();
echo $variableGlobale; // Accessible
echo $variableLocale; // Non accessible (erreur)
?>
Les fonctions anonymes, également appelées closures, sont des fonctions sans nom qui
peuvent être assignées à des variables.
<?php
$dire = function($mot) {
echo "Vous avez dit : $mot";
};
$dire("Bonjour"); // Affiche : Vous avez dit : Bonjour
?>
Exercice 4.1
Créez une fonction calculerIMC qui prend en paramètres le poids (en kg) et la taille (en m)
d'une personne et retourne son IMC (Indice de Masse Corporelle). Ensuite, créez une fonction
interpreterIMC qui prend l'IMC en paramètre et retourne une interprétation selon l'échelle
suivante :
Corrigé 4.1
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 4.1 - Calcul de l'IMC</title>
</head>
<body>
<?php
function calculerIMC($poids, $taille) {
return $poids / ($taille * $taille);
}
function interpreterIMC($imc) {
if ($imc < 18.5) {
return "Insuffisance pondérale";
} elseif ($imc < 25) {
return "Corpulence normale";
} elseif ($imc < 30) {
return "Surpoids";
} else {
return "Obésité";
}
}
$poids = 70; // en kg
$taille = 1.75; // en m
Exercice 4.2
Créez une fonction genererMotDePasse qui génère un mot de passe aléatoire. La fonction doit
prendre en paramètres la longueur du mot de passe et des booléens indiquant si le mot de
passe doit contenir des majuscules, des chiffres et des caractères spéciaux. Utilisez une
fonction anonyme pour définir les caractères possibles.
Corrigé 4.2
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 4.2 - Générateur de mot de passe</title>
</head>
<body>
<?php
function genererMotDePasse($longueur, $inclureMajuscules = true,
$inclureChiffres = true, $inclureSpeciaux = true) {
$getCaracteres = function() use ($inclureMajuscules, $inclureChiffres,
$inclureSpeciaux) {
$chars = 'abcdefghijklmnopqrstuvwxyz';
if ($inclureMajuscules) $chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
if ($inclureChiffres) $chars .= '0123456789';
if ($inclureSpeciaux) $chars .= '!@#$%^&*()_+-=[]{}|;:,.<>?';
return $chars;
};
$caracteres = $getCaracteres();
$motDePasse = '';
$max = strlen($caracteres) - 1;
for ($i = 0; $i < $longueur; $i++) {
$motDePasse .= $caracteres[rand(0, $max)];
}
return $motDePasse;
}
$longueur = 12;
$motDePasse = genererMotDePasse($longueur, true, true, true);
5. Tableaux
Les tableaux en PHP sont des structures de données très flexibles qui peuvent contenir
plusieurs valeurs dans une seule variable.
<?php
// Création d'un tableau indexé
$fruits = array("pomme", "banane", "orange");
// ou
$legumes = ["carotte", "brocoli", "tomate"];
<?php
$fruits = ["pomme", "banane", "orange"];
echo $fruits[0]; // Affiche : pomme
$personne = ["nom" => "Dupont", "prenom" => "Jean", "age" => 30];
echo $personne["prenom"]; // Affiche : Jean
?>
<?php
$fruits = ["pomme", "banane", "orange"];
<?php
$etudiants = [
["nom" => "Dupont", "prenom" => "Jean", "notes" => [15, 12, 18]],
["nom" => "Martin", "prenom" => "Sophie", "notes" => [14, 16, 13]],
["nom" => "Bernard", "prenom" => "Emma", "notes" => [17, 15, 19]]
];
Exercice 5.1
Créez un tableau associatif représentant un panier d'achats. Chaque élément du panier doit
avoir un nom, un prix unitaire et une quantité. Calculez et affichez le total du panier.
Corrigé 5.1
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 5.1 - Panier d'achats</title>
</head>
<body>
<h2>Panier d'achats</h2>
<?php
$panier = [
["nom" => "Livre PHP", "prix" => 29.99, "quantite" => 2],
["nom" => "Clé USB", "prix" => 14.99, "quantite" => 3],
["nom" => "Casque audio", "prix" => 79.99, "quantite" => 1],
["nom" => "Souris sans fil", "prix" => 24.99, "quantite" => 1]
];
$total = 0;
echo "<tr>";
echo "<td>" . $article["nom"] . "</td>";
echo "<td>" . number_format($article["prix"], 2) . " €</td>";
echo "<td>" . $article["quantite"] . "</td>";
echo "<td>" . number_format($sousTotal, 2) . " €</td>";
echo "</tr>";
}
Exercice 5.2
Créez un tableau multidimensionnel représentant une grille de jeu du morpion (3x3).
Remplissez-le aléatoirement avec des 'X', des 'O' et des cases vides (' '). Affichez la grille et
vérifiez s'il y a un gagnant (3 symboles identiques alignés) ou si la partie est nulle.
Corrigé 5.2
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 5.2 - Morpion</title>
<style>
table { border-collapse: collapse; }
td { width: 50px; height: 50px; text-align: center; font-size: 24px;
border: 1px solid black; }
</style>
</head>
<body>
<h2>Jeu du Morpion</h2>
<?php
function creerGrille() {
$symboles = ['X', 'O', ' '];
$grille = [];
for ($i = 0; $i < 3; $i++) {
for ($j = 0; $j < 3; $j++) {
$grille[$i][$j] = $symboles[array_rand($symboles)];
}
}
return $grille;
}
function afficherGrille($grille) {
echo "<table>";
foreach ($grille as $ligne) {
echo "<tr>";
foreach ($ligne as $cellule) {
echo "<td>" . $cellule . "</td>";
}
echo "</tr>";
}
echo "</table>";
}
function verifierGagn
function verifierGagnant($grille) {
// Vérifier les lignes et les colonnes
for ($i = 0; $i < 3; $i++) {
if ($grille[$i][0] != ' ' && $grille[$i][0] == $grille[$i][1] &&
$grille[$i][1] == $grille[$i][2]) {
return $grille[$i][0];
}
if ($grille[0][$i] != ' ' && $grille[0][$i] == $grille[1][$i] &&
$grille[1][$i] == $grille[2][$i]) {
return $grille[0][$i];
}
}
// Vérifier les diagonales
if ($grille[0][0] != ' ' && $grille[0][0] == $grille[1][1] &&
$grille[1][1] == $grille[2][2]) {
return $grille[0][0];
}
if ($grille[0][2] != ' ' && $grille[0][2] == $grille[1][1] &&
$grille[1][1] == $grille[2][0]) {
return $grille[0][2];
}
// Vérifier s'il y a match nul
foreach ($grille as $ligne) {
if (in_array(' ', $ligne)) {
return null; // Le jeu n'est pas terminé
}
}
return 'Nul';
}
$grille = creerGrille();
afficherGrille($grille);
$resultat = verifierGagnant($grille);
if ($resultat === null) {
echo "<p>Le jeu n'est pas terminé.</p>";
} elseif ($resultat === 'Nul') {
echo "<p>Match nul !</p>";
} else {
echo "<p>Le joueur $resultat a gagné !</p>";
}
?>
</body>
</html>
<?php
$prenom = "Jean";
$nom = "Dupont";
$nomComplet = $prenom . " " . $nom; // Utilisation de l'opérateur de
concaténation
echo $nomComplet; // Affiche : Jean Dupont
$age = 30;
echo "Je m'appelle $prenom et j'ai $age ans."; // Interpolation de variables
?>
<?php
$phrase = "Bonjour tout le monde";
$mot = "PHP";
echo str_repeat($mot, 3); // Répète une chaîne (PHPPHPPHP)
<?php
$texte = "PHP est un langage de programmation puissant";
<?php
$phrase = "PHP,HTML,CSS,JavaScript";
$langages = explode(",", $phrase); // Convertit la chaîne en tableau
print_r($langages);
<?php
$texte = "Mon adresse email est [email protected]";
$pattern = "/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/";
Exercice 6.1
Créez une fonction formatTelephone qui prend en paramètre une chaîne représentant un
numéro de téléphone (par exemple "0123456789") et la formate selon le modèle "01 23 45 67
89".
Corrigé 6.1
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 6.1 - Formatage de numéro de téléphone</title>
</head>
<body>
<h2>Formatage de numéro de téléphone</h2>
<?php
function formatTelephone($numero) {
$numero = preg_replace("/[^0-9]/", "", $numero); // Supprime tous les
caractères non numériques
if (strlen($numero) != 10) {
return "Numéro invalide";
}
return implode(" ", str_split($numero, 2));
}
$numeros = [
"0123456789",
"01 23 45 67 89",
"0123 45 67 89",
"012345678", // Invalide
"01234567890" // Invalide
];
Exercice 6.2
Créez une fonction censurerMots qui prend en paramètre une phrase et un tableau de mots à
censurer. La fonction doit remplacer chaque occurrence des mots à censurer par des
astérisques (*) de même longueur.
Corrigé 6.2
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 6.2 - Censure de mots</title>
</head>
<body>
<h2>Censure de mots</h2>
<?php
function censurerMots($phrase, $motsCensures) {
foreach ($motsCensures as $mot) {
$remplacement = str_repeat("*", strlen($mot));
$phrase = str_ireplace($mot, $remplacement, $phrase);
}
return $phrase;
}
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$nom = $_POST["nom"];
$email = $_POST["email"];
$message = $_POST["message"];
GET : Les données sont envoyées via l'URL. Utile pour les requêtes qui ne modifient pas
les données.
POST : Les données sont envoyées dans le corps de la requête HTTP. Préférable pour les
formulaires avec des données sensibles ou volumineuses.
7.4 Sécurisation des données de formulaire
<?php
$nom = htmlspecialchars($_POST["nom"]);
$email = filter_var($_POST["email"], FILTER_SANITIZE_EMAIL);
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo "Adresse email invalide";
}
?>
Exercice 7.1
Créez un formulaire d'inscription avec les champs suivants : nom, prénom, email, mot de
passe, confirmation du mot de passe. Ajoutez une validation côté serveur pour s'assurer que
tous les champs sont remplis, que l'email est valide et que les deux mots de passe
correspondent.
Corrigé 7.1
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice 7.1 - Formulaire d'inscription</title>
</head>
<body>
<h2>Formulaire d'inscription</h2>
<?php
$erreurs = [];
$succes = false;
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$nom = htmlspecialchars($_POST["nom"] ?? "");
$prenom = htmlspecialchars($_POST["prenom"] ?? "");
$email = filter_var($_POST["email"] ?? "", FILTER_SANITIZE_EMAIL);
$motDePasse = $_POST["mot_de_passe"] ?? "";
$confirmationMotDePasse = $_POST["confirmation_mot_de_passe"] ?? "";
// Validation
if (empty($nom)) $erreurs[] = "Le nom est requis.";
if (empty($prenom)) $erreurs[] = "Le prénom est requis.";
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL))
$erreurs[] = "L'email est invalide.";
if (empty($motDePasse)) $erreurs[] = "Le mot de passe est requis.";
if ($motDePasse !== $confirmationMotDePasse) $erreurs[] = "Les mots de
passe ne correspondent pas.";
if (empty($erreurs)) {
$succes = true;
// Ici, vous pourriez enregistrer les données dans une base de
données
}
}
?>
Une classe est un modèle pour créer des objets. Un objet est une instance d'une classe.
<?php
class Voiture {
// Propriétés
public $marque;
public $modele;
public $couleur;
// Méthode
public function afficherInfos() {
echo "Cette voiture est une $this->marque $this->modele de couleur
$this->couleur.";
}
}
$maVoiture->afficherInfos();
?>
11.1.2 Constructeur
Le constructeur est une méthode spéciale appelée lors de la création d'un objet.
<?php
class Voiture {
public $marque;
public $modele;
public $couleur;
11.1.3 Encapsulation
<?php
class CompteBancaire {
private $solde;
11.2 Héritage
L'héritage permet à une classe d'hériter des propriétés et méthodes d'une autre classe.
<?php
class Animal {
protected $nom;
11.3 Polymorphisme
Le polymorphisme permet à des objets de classes différentes d'être traités de manière
uniforme.
<?php
abstract class Forme {
abstract public function calculerAire();
}
$formes = [
new Cercle(5),
new Rectangle(4, 6)
];
foreach ($formes as $forme) {
echo "L'aire est : " . $forme->calculerAire() . "<br>";
}
?>
11.4 Interfaces
Une interface définit un contrat que les classes doivent respecter.
<?php
interface Deplacement {
public function avancer();
public function reculer();
}
11.5 Traits
Les traits permettent de réutiliser du code dans plusieurs classes.
<?php
trait Logger {
public function log($message) {
echo date("Y-m-d H:i:s") . " : " . $message . "<br>";
}
}
class Utilisateur {
use Logger;
class Produit {
use Logger;
Exercice 11.1
Créez une classe Etudiant avec les propriétés suivantes : nom, prénom, notes (tableau).
Ajoutez des méthodes pour calculer la moyenne et déterminer si l'étudiant a réussi (moyenne
>= 10). Créez ensuite une classe Promotion qui contient un tableau d'étudiants et des
méthodes pour ajouter un étudiant, calculer la moyenne de la promotion et afficher le
classement des étudiants.
Corrigé 11.1
<?php
class Etudiant {
private $nom;
private $prenom;
private $notes;
class Promotion {
private $etudiants = [];
// Utilisation
$promotion = new Promotion();
$etudiants = [
new Etudiant("Dupont", "Jean", [12, 14, 10, 15, 11]),
new Etudiant("Martin", "Sophie", [16, 15, 17, 14, 18]),
new Etudiant("Bernard", "Luc", [9, 11, 10, 13, 12]),
new Etudiant("Petit", "Marie", [14, 13, 15, 16, 12])
];
Exercice 11.2
Créez une hiérarchie de classes pour représenter différents types de véhicules. Commencez
par une classe abstraite Vehicule , puis créez des sous-classes comme Voiture , Moto , et
Camion . Chaque véhicule doit avoir une méthode pour calculer sa consommation de carburant.
Utilisez le polymorphisme pour créer une flotte de véhicules et calculer la consommation totale.
Corrigé 11.2
<?php
abstract class Vehicule {
protected $marque;
protected $modele;
protected $kilometrage;
class Flotte {
private $vehicules = [];
// Utilisation
$flotte = new Flotte();
<?php
class Compteur {
private static $count = 0;
Compteur::incrementer();
Compteur::incrementer();
echo Compteur::getCount(); // Affiche 2
?>
<?php
class Math {
const PI = 3.14159;
<?php
// Fichier: Animaux/Chien.php
namespace Animaux;
class Chien {
public function aboyer() {
echo "Woof!";
}
}
// Fichier: main.php
require_once 'Animaux/Chien.php';
use Animaux\Chien;
<?php
spl_autoload_register(function($className) {
include_once $className . '.php';
});
11.10.1 Singleton
Le pattern Singleton garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès
global à cette instance.
<?php
class Database {
private static $instance = null;
private $connection;
$db = Database::getInstance();
$db->query("SELECT * FROM users");
?>
11.10.2 Factory
Le pattern Factory définit une interface pour créer un objet, mais laisse les sous-classes
décider quelle classe instancier.
<?php
interface Animal {
public function parler();
}
class AnimalFactory {
public static function creerAnimal($type) {
switch ($type) {
case 'chien':
return new Chien();
case 'chat':
return new Chat();
default:
throw new Exception("Animal inconnu");
}
}
}
$animal = AnimalFactory::creerAnimal('chien');
echo $animal->parler(); // Affiche "Woof!"
?>
Exercice 11.3
Créez un système de gestion de logs utilisant le pattern Singleton. La classe Logger doit avoir
des méthodes pour logger des messages d'information, d'avertissement et d'erreur. Les logs
doivent être écrits dans un fichier avec la date et l'heure de chaque message.
Corrigé 11.3
<?php
class Logger {
private static $instance = null;
private $logFile;
// Utilisation
$logger = Logger::getInstance();
$logger->info("L'application a démarré");
$logger->warning("Attention, l'espace disque est faible");
$logger->error("Erreur critique : impossible de se connecter à la base de
données");
// Vérifiez le contenu du fichier application.log
?>
Exercice 11.4
Implémentez un système de gestion de produits en utilisant le pattern Factory. Créez une
interface Produit et plusieurs classes de produits (par exemple, Livre , DVD , Vetement ).
Utilisez une ProduitFactory pour créer les différents types de produits. Ajoutez une méthode
pour afficher les détails de chaque produit.
Corrigé 11.4
<?php
interface Produit {
public function afficherDetails();
}
class ProduitFactory {
public static function creerProduit($type, $details) {
switch ($type) {
case 'livre':
return new Livre($details['titre'], $details['auteur'],
$details['isbn']);
case 'dvd':
return new DVD($details['titre'], $details['realisateur'],
$details['duree']);
case 'vetement':
return new Vetement($details['type'], $details['taille'],
$details['couleur']);
default:
throw new Exception("Type de produit inconnu");
}
}
}
// Utilisation
$produits = [
ProduitFactory::creerProduit('livre', [
'titre' => '1984',
'auteur' => 'George Orwell',
'isbn' => '978-0451524935'
]),
ProduitFactory::creerProduit('dvd', [
'titre' => 'Inception',
'realisateur' => 'Christopher Nolan',
'duree' => 148
]),
ProduitFactory::creerProduit('vetement', [
'type' => 'T-shirt',
'taille' => 'M',
'couleur' => 'Bleu'
])
];
<?php
try {
$pdo = new PDO('mysql:host=localhost;dbname=ma_base', 'utilisateur',
'mot_de_passe');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "Connexion réussie";
} catch(PDOException $e) {
echo "Erreur de connexion : " . $e->getMessage();
}
?>
<?php
$query = "SELECT * FROM utilisateurs";
$stmt = $pdo->query($query);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['nom'] . " " . $row['prenom'] . "<br>";
}
?>
Les requêtes préparées sont plus sécurisées et performantes pour les requêtes répétitives.
<?php
$query = "INSERT INTO utilisateurs (nom, prenom, email) VALUES (?, ?, ?)";
$stmt = $pdo->prepare($query);
$stmt->execute(['Dupont', 'Jean', '[email protected]']);
?>
<?php
try {
$pdo->beginTransaction();
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
echo "Erreur : " . $e->getMessage();
}
?>
<?php
$stmt = $pdo->query("SELECT * FROM utilisateurs");
<?php
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
// Code susceptible de générer une erreur
} catch (PDOException $e) {
echo "Erreur : " . $e->getMessage();
}
?>
Exercice 13.1
Créez une classe DatabaseManager qui encapsule la connexion PDO et fournit des méthodes
pour exécuter des requêtes courantes (select, insert, update, delete). Utilisez cette classe pour
créer un système simple de gestion des utilisateurs (ajout, modification, suppression, liste).
Corrigé 13.1
<?php
class DatabaseManager {
private $pdo;
// Ajouter un utilisateur
$id = $db->insert('utilisateurs', [
'nom' => 'Dupont',
'prenom' => 'Jean',
'email' => '[email protected]'
]);
echo "Nouvel utilisateur ajouté avec l'ID : $id\n";
// Supprimer un utilisateur
$deleted = $db->delete('utilisateurs', ['id' => $id]);
echo "Utilisateur supprimé : $deleted ligne(s) affectée(s)\n";
?>
Exercice 13.2
Créez une application simple de gestion de tâches (todo list) utilisant PDO. L'application doit
permettre d'ajouter des tâches, de les marquer comme terminées, de les supprimer et d'afficher
la liste des tâches en cours et terminées. Utilisez des transactions pour vous assurer que les
opérations critiques sont atomiques.
Corrigé 13.2
<?php
class TodoManager {
private $pdo;
$username = $_POST['username'];
$query = "SELECT * FROM users WHERE username = '$username'";
$result = $mysqli->query($query);
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$password = 'mot_de_passe_utilisateur';
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Vérification
if (password_verify($password, $hashed_password)) {
echo "Mot de passe correct";
}
14.5 Gestion des sessions
14.5.1 Configuration sécurisée des sessions
ini_set('session.cookie_httponly', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.cookie_secure', 1);
session_start();
session_regenerate_id(true);
session_start();
if (!isset($_SESSION['login_attempts'])) {
$_SESSION['login_attempts'] = 0;
}
if ($_SESSION['login_attempts'] > 5) {
die('Trop de tentatives. Réessayez plus tard.');
}
header("X-XSS-Protection: 1; mode=block");
header("X-Frame-Options: SAMEORIGIN");
header("X-Content-Type-Options: nosniff");
header("Strict-Transport-Security: max-age=31536000; includeSubDomains");
Exercice 14.1
Créez une classe SecurityHelper qui fournit des méthodes statiques pour :
Corrigé 14.1
<?php
class SecurityHelper {
public static function generateCsrfToken() {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// Utilisation
session_start();
// Valider un email
$email = "[email protected]";
if (SecurityHelper::validateEmail($email)) {
echo "Email valide\n";
} else {
echo "Email invalide\n";
}
if (SecurityHelper::verifyPassword($password, $hashedPassword)) {
echo "Mot de passe vérifié avec succès\n";
} else {
echo "Échec de la vérification du mot de passe\n";
}
?>
Exercice 14.2
Créez une classe LoginManager qui gère la connexion des utilisateurs de manière sécurisée.
Cette classe doit :
Corrigé 14.2
<?php
class LoginManager {
private $pdo;
private $maxAttempts = 5;
private $lockoutTime = 900; // 15 minutes
if ($this->isLockedOut()) {
return "Compte verrouillé. Réessayez plus tard.";
}
$user = $this->getUserByUsername($username);
if ($user && password_verify($password, $user['password'])) {
$this->resetLoginAttempts();
$this->setLoggedInSession($user['id']);
return "Connexion réussie";
} else {
$this->incrementLoginAttempts();
return "Identifiants invalides";
}
}
// Utilisation
try {
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Exemple de connexion
echo $loginManager->login("username", "password") . "\n";
// Exemple de déconnexion
$loginManager->logout();
echo "Déconnexion effectuée\n";
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// Dans le formulaire HTML
<input type="hidden" name="csrf_token" value="<?php echo
$_SESSION['csrf_token']; ?>">
$password = 'mot_de_passe_utilisateur';
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Vérification
if (password_verify($password, $hashed_password)) {
echo "Mot de passe correct";
}
ini_set('session.cookie_httponly', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.cookie_secure', 1);
session_start();
session_regenerate_id(true);
session_start();
if (!isset($_SESSION['login_attempts'])) {
$_SESSION['login_attempts'] = 0;
}
if ($_SESSION['login_attempts'] > 5) {
die('Trop de tentatives. Réessayez plus tard.');
}
header("X-XSS-Protection: 1; mode=block");
header("X-Frame-Options: SAMEORIGIN");
header("X-Content-Type-Options: nosniff");
header("Strict-Transport-Security: max-age=31536000; includeSubDomains");
Exercice 14.1
Créez une classe SecurityHelper qui fournit des méthodes statiques pour :
Corrigé 14.1
<?php
class SecurityHelper {
public static function generateCsrfToken() {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// Utilisation
session_start();
if (SecurityHelper::verifyPassword($password, $hashedPassword)) {
echo "Mot de passe vérifié avec succès\n";
} else {
echo "Échec de la vérification du mot de passe\n";
}
?>
Exercice 14.2
Créez une classe LoginManager qui gère la connexion des utilisateurs de manière sécurisée.
Cette classe doit :
Corrigé 14.2
<?php
class LoginManager {
private $pdo;
private $maxAttempts = 5;
private $lockoutTime = 900; // 15 minutes
if ($this->isLockedOut()) {
return "Compte verrouillé. Réessayez plus tard.";
}
$user = $this->getUserByUsername($username);
if ($user && password_verify($password, $user['password'])) {
$this->resetLoginAttempts();
$this->setLoggedInSession($user['id']);
return "Connexion réussie";
} else {
$this->incrementLoginAttempts();
return "Identifiants invalides";
}
}
// Utilisation
try {
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Exemple de connexion
echo $loginManager->login("username", "password") . "\n";
// Exemple de déconnexion
$loginManager->logout();
echo "Déconnexion effectuée\n";
<?php
// Lecture du contenu entier d'un fichier
$contenu = file_get_contents('mon_fichier.txt');
echo $contenu;
<?php
$fichier = fopen('mon_fichier.txt', 'r');
if ($fichier) {
while (($ligne = fgets($fichier)) !== false) {
echo $ligne;
}
fclose($fichier);
}
?>
<?php
$contenu = "Ceci est un nouveau contenu.";
file_put_contents('nouveau_fichier.txt', $contenu);
<?php
$fichier = fopen('mon_fichier.txt', 'w');
if ($fichier) {
fwrite($fichier, "Première ligne\n");
fwrite($fichier, "Deuxième ligne\n");
fclose($fichier);
}
?>
<?php
$dossier = 'mon_dossier';
if (is_dir($dossier)) {
if ($handle = opendir($dossier)) {
while (($fichier = readdir($handle)) !== false) {
if ($fichier != "." && $fichier != "..") {
echo $fichier . "\n";
}
}
closedir($handle);
}
}
?>
<?php
// Changer les permissions d'un fichier
chmod('mon_fichier.txt', 0644);
<?php
$chemin = "/home/user/documents/fichier.txt";
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
if (isset($_FILES["fichier"]) && $_FILES["fichier"]["error"] == 0) {
$dossier_cible = "uploads/";
$fichier_cible = $dossier_cible . basename($_FILES["fichier"]
["name"]);
if (move_uploaded_file($_FILES["fichier"]["tmp_name"],
$fichier_cible)) {
echo "Le fichier ". basename($_FILES["fichier"]["name"]) . " a été
uploadé.";
} else {
echo "Erreur lors de l'upload du fichier.";
}
}
}
?>
<?php
// Vérification du type MIME
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$type = finfo_file($finfo, $_FILES['fichier']['tmp_name']);
finfo_close($finfo);
$types_autorises = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($type, $types_autorises)) {
die("Type de fichier non autorisé");
}
Exercice 15.1
Créez une classe FileManager qui offre des méthodes pour :
Assurez-vous que la classe gère les erreurs et les exceptions de manière appropriée.
Corrigé 15.1
<?php
class FileManager {
public function readFile($filename) {
if (!file_exists($filename)) {
throw new Exception("Le fichier $filename n'existe pas.");
}
return file_get_contents($filename);
}
// Utilisation
try {
$fm = new FileManager();
// Lire un fichier
echo $fm->readFile('test.txt') . "\n";
// Lister un répertoire
print_r($fm->listDirectory('.'));
// Créer un répertoire
$fm->createDirectory('nouveau_dossier');
// Supprimer un fichier
$fm->delete('fichier_a_supprimer.txt');
// Supprimer un répertoire
$fm->delete('dossier_vide');
Exercice 15.2
Créez un script PHP qui implémente un gestionnaire de fichiers web simple. Ce script doit
permettre :
Assurez-vous d'implémenter des mesures de sécurité de base (par exemple, limiter l'accès à
un répertoire spécifique).
Corrigé 15.2
<?php
session_start();
class WebFileManager {
private $root_dir;
// Utilisation
$root_dir = './files'; // Répertoire racine pour le gestionnaire de fichiers
$fm = new WebFileManager($root_dir);
if (isset($_GET['download'])) {
try {
$fm->downloadFile($_GET['download'], $_GET['dir']);
} catch (Exception $e) {
echo "Erreur : " . $e->getMessage();
}
}
```html
<?php if ($file['type'] === 'dir'): ?>
<a href="?dir=<?php echo urlencode($currentDir . '/' .
$file['name']); ?>">Ouvrir</a>
<?php else: ?>
<a href="?download=<?php echo urlencode($file['name']); ?
>&dir=<?php echo urlencode($currentDir); ?>">Télécharger</a>
<?php endif; ?>
<form method="post" style="display:inline;">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="name" value="<?php echo
htmlspecialchars($file['name']); ?>">
<input type="hidden" name="dir" value="<?php echo
htmlspecialchars($currentDir); ?>">
<input type="submit" value="Supprimer">
</form>
</td>
</tr>
<?php endforeach; ?>
</table>
<h3>Uploader un fichier</h3>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="dir" value="<?php echo
htmlspecialchars($currentDir); ?>">
<input type="file" name="upload" required>
<input type="submit" value="Uploader">
</form>
Notez que ce script est un exemple de base et devrait être amélioré pour une utilisation en
production, notamment en termes de sécurité et de gestion des erreurs.
Toujours valider et sanitiser les chemins de fichiers pour éviter les attaques de type "directory
traversal".
<?php
function securePath($path) {
$path = realpath($path);
$root = realpath('/chemin/vers/racine/autorisee');
if ($path === false || strpos($path, $root) !== 0) {
throw new Exception("Chemin non autorisé");
}
return $path;
}
?>
<?php
// Définir des permissions restrictives pour les nouveaux fichiers
umask(0077);
Pour les fichiers volumineux, utilisez des flux pour une meilleure gestion de la mémoire.
<?php
$handle = fopen("gros_fichier.txt", "r");
if ($handle) {
while (($line = fgets($handle)) !== false) {
// Traiter la ligne
}
fclose($handle);
}
?>
<?php
try {
$content = file_get_contents('fichier.txt');
if ($content === false) {
throw new Exception("Impossible de lire le fichier");
}
// Traiter le contenu
} catch (Exception $e) {
error_log("Erreur de lecture de fichier : " . $e->getMessage());
// Gérer l'erreur de manière appropriée
}
?>
Exercice 15.3
Créez une classe LogManager qui gère un système de logs pour une application. Cette classe
doit :
Corrigé 15.3
<?php
class LogManager {
private $logFile;
private $maxFileSize;
if (!file_exists($this->logFile)) {
touch($this->logFile);
chmod($this->logFile, 0644);
}
}
// Utilisation
$logManager = new LogManager('./logs');
$logManager->log("Application démarrée");
$logManager->log("Attention : espace disque faible", LogManager::LOG_WARNING);
$logManager->log("Erreur critique : base de données inaccessible",
LogManager::LOG_ERROR);
echo $logManager->getLogContent();
?>
16. Frameworks PHP populaires
Les frameworks PHP sont des outils essentiels pour le développement web moderne, offrant
une structure organisée et des fonctionnalités prêtes à l'emploi pour accélérer le
développement d'applications.
Un framework PHP est un ensemble de composants et d'outils qui fournit une structure de base
pour développer des applications web. Il offre généralement :
Accélération du développement
Code plus organisé et maintenable
Sécurité renforcée
Respect des bonnes pratiques de développement
Communauté active et support
1. Laravel
2. Symfony
3. CodeIgniter
4. Yii
5. CakePHP
// routes/web.php
Route::get('/', function () {
return view('welcome');
});
Route::get('/utilisateurs', 'UserController@index');
16.2.4 Contrôleurs
Les contrôleurs gèrent la logique de l'application.
// app/Http/Controllers/UserController.php
namespace App\Http\Controllers;
use App\Models\User;
// app/Models/User.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
// Utilisation
$users = User::where('active', 1)->get();
@section('content')
<h1>Utilisateurs</h1>
<ul>
@foreach($users as $user)
<li>{{ $user->name }}</li>
@endforeach
</ul>
@endsection
16.2.7 Migrations
Les migrations permettent de gérer la structure de la base de données.
// database/migrations/2023_01_01_create_users_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Léger et rapide
Facile à apprendre
Moins de fonctionnalités que Laravel ou Symfony
16.3.3 Yii
Performances élevées
Génération de code intégrée
Bonne pour les applications à grande échelle
Exercice 16.1
Créez une application Laravel simple de gestion de tâches (Todo List). L'application doit
permettre de :
Utilisez les migrations pour créer la table des tâches, un modèle Eloquent pour interagir avec la
base de données, et les vues Blade pour l'interface utilisateur.
Corrigé 16.1
Contenu de la migration :
// database/migrations/xxxx_xx_xx_create_tasks_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Exécutez la migration :
Contenu du modèle :
// app/Models/Task.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
Contenu du contrôleur :
// app/Http/Controllers/TaskController.php
namespace App\Http\Controllers;
use App\Models\Task;
use Illuminate\Http\Request;
6. Création de la vue :
<ul>
@foreach($tasks as $task)
<li>
<form action="{{ route('tasks.update', $task) }}"
method="POST" style="display: inline;">
@csrf
@method('PUT')
<input type="checkbox" onChange="this.form.submit()" {{
$task->completed ? 'checked' : '' }}>
<span style="{{ $task->completed ? 'text-decoration: line-
through;' : '' }}">
{{ $task->title }}
</span>
</form>
<form action="{{ route('tasks.destroy', $task) }}"
method="POST" style="display: inline;">
@csrf
@method('DELETE')
<button type="submit">Supprimer</button>
</form>
</li>
@endforeach
</ul>
</body>
</html>
7. Lancement de l'application :
Cet exercice couvre les bases de Laravel, incluant le routage, les contrôleurs, les modèles
Eloquent, les migrations et les vues Blade. Il démontre comment créer rapidement une
application CRUD simple avec Laravel.
17.1 Cookies
Les cookies sont de petits fichiers de données stockés côté client (dans le navigateur de
l'utilisateur).
Paramètres :
Nom du cookie
Valeur du cookie
Temps d'expiration (timestamp Unix)
Chemin sur le serveur
Domaine
Secure (HTTPS seulement)
HttpOnly (inaccessible via JavaScript)
if(isset($_COOKIE['nom_du_cookie'])) {
$valeur = $_COOKIE['nom_du_cookie'];
echo "Valeur du cookie : " . $valeur;
}
17.2 Sessions
Les sessions permettent de stocker des données côté serveur pour un utilisateur spécifique.
session_start();
if(isset($_SESSION['nom_utilisateur'])) {
echo "Bienvenue, " . $_SESSION['nom_utilisateur'];
}
session_start();
if (!isset($_SESSION['created'])) {
$_SESSION['created'] = time();
} else if (time() - $_SESSION['created'] > 1800) {
// Régénérer l'ID de session après 30 minutes
session_regenerate_id(true);
$_SESSION['created'] = time();
}
setcookie("auth_token", $token, [
'expires' => time() + 3600,
'path' => '/',
'domain' => '',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict'
]);
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
Exercice 17.1
Créez un système simple d'authentification utilisant les sessions. Le système doit permettre à
un utilisateur de se connecter, de voir une page protégée, et de se déconnecter. Utilisez des
cookies pour implémenter une fonctionnalité "Se souvenir de moi".
Corrigé 17.1
<?php
// config.php
session_start();
ini_set('session.cookie_lifetime', 3600);
ini_set('session.gc_maxlifetime', 3600);
// login.php
require_once 'config.php';
checkRememberMe();
if ($remember) {
setcookie('remember_me', 'valid_token', time() + 30 * 24 * 60 *
60, '/', '', true, true);
}
header('Location: protected.php');
exit;
} else {
$error = "Identifiants invalides";
}
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Connexion</title>
</head>
<body>
<h1>Connexion</h1>
<?php if (isset($error)) echo "<p>$error</p>"; ?>
<form method="post">
<input type="text" name="username" placeholder="Nom d'utilisateur"
required><br>
<input type="password" name="password" placeholder="Mot de passe"
required><br>
<label>
<input type="checkbox" name="remember"> Se souvenir de moi
</label><br>
<button type="submit">Se connecter</button>
</form>
</body>
</html>
<?php
// protected.php
require_once 'config.php';
checkRememberMe();
if (!isLoggedIn()) {
header('Location: login.php');
exit;
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Page protégée</title>
</head>
<body>
<h1>Page protégée</h1>
<p>Bienvenue, <?php echo htmlspecialchars($_SESSION['username']); ?>!</p>
<a href="logout.php">Se déconnecter</a>
</body>
</html>
<?php
// logout.php
require_once 'config.php';
$_SESSION = array();
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
session_destroy();
header('Location: login.php');
exit;
?>
Note : Ce code est une démonstration et ne doit pas être utilisé tel quel en production. Dans un
environnement réel, vous devriez utiliser une base de données pour stocker les informations
d'utilisateur et les tokens "Se souvenir de moi", ainsi que des mesures de sécurité
supplémentaires comme le hachage des mots de passe.
Exercice 17.2
Créez un système de panier d'achat simple utilisant les sessions. Le système doit permettre
d'ajouter des articles au panier, de les supprimer, et d'afficher le contenu du panier. Utilisez
également un cookie pour sauvegarder le contenu du panier pendant une semaine, même si
l'utilisateur ferme son navigateur.
Corrigé 17.2
<?php
// config.php
session_start();
// index.php
require_once 'config.php';
initCart();
$products = [
1 => ['name' => 'Produit A', 'price' => 10.99],
2 => ['name' => 'Produit B', 'price' => 24.99],
3 => ['name' => 'Produit C', 'price' => 5.99],
];
<h2>Votre panier</h2>
<?php if (empty($_SESSION['cart'])): ?>
<p>Votre panier est vide.</p>
<?php else: ?>
<ul>
<?php foreach ($_SESSION['cart'] as $id => $quantity): ?>
<li>
<?php echo htmlspecialchars($products[$id]['name']); ?> -
Quantité : <?php echo $quantity; ?> -
Prix : <?php echo number_format($products[$id]['price'] *
$quantity, 2); ?> €
<form method="post" style="display: inline;">
<input type="hidden" name="product_id" value="<?php
echo $id; ?>">
<button type="submit"
name="remove_from_cart">Supprimer</button>
</form>
</li>
<?php endforeach; ?>
</ul>
<p>
Total : <?php
$total = array_sum(array_map(function($id, $quantity) use
($products) {
return $products[$id]['price'] * $quantity;
}, array_keys($_SESSION['cart']), $_SESSION['cart']));
echo number_format($total, 2);
?> €
</p>
<?php endif; ?>
</body>
</html>
Ce code implémente un système de panier d'achat simple en utilisant les sessions PHP et les
cookies. Il comprend :
function initCart() {
if (!isset($_SESSION['cart'])) {
if (isset($_COOKIE['saved_cart'])) {
$_SESSION['cart'] = json_decode($_COOKIE['saved_cart'], true);
} else {
$_SESSION['cart'] = [];
}
}
}
Cette fonction vérifie d'abord si le panier existe déjà dans la session. Si ce n'est pas le cas, elle
cherche un panier sauvegardé dans les cookies. Si un cookie existe, son contenu est décodé et
utilisé pour initialiser le panier dans la session. Sinon, un panier vide est créé.
17.5.2 Sauvegarde du panier dans un cookie
La fonction saveCartToCookie() est appelée chaque fois que le panier est modifié :
function saveCartToCookie() {
setcookie('saved_cart', json_encode($_SESSION['cart']), time() + 7 * 24 *
60 * 60, '/', '', true, true);
}
Le script vérifie les actions POST pour ajouter ou supprimer des articles du panier :
Lors de l'ajout ou de la suppression d'un article, le panier est mis à jour dans la session, puis
sauvegardé dans le cookie.
Cette approche calcule le prix total pour chaque article (prix unitaire * quantité) puis fait la
somme de tous ces totaux.
Exercice 17.3
Corrigé 17.3
Voici les modifications à apporter au code précédent pour implémenter ces améliorations :
<?php
// config.php
session_start();
function initCart() {
if (!isset($_SESSION['cart'])) {
if (isset($_COOKIE['saved_cart'])) {
$_SESSION['cart'] = json_decode($_COOKIE['saved_cart'], true);
} else {
$_SESSION['cart'] = [];
}
}
}
function saveCartToCookie() {
setcookie('saved_cart', json_encode($_SESSION['cart']), time() + 7 * 24 *
60 * 60, '/', '', true, true);
}
function generateCSRFToken() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
function verifyCSRFToken($token) {
return isset($_SESSION['csrf_token']) &&
hash_equals($_SESSION['csrf_token'], $token);
}
// index.php
require_once 'config.php';
initCart();
$products = [
1 => ['name' => 'Produit A', 'price' => 10.99],
2 => ['name' => 'Produit B', 'price' => 24.99],
3 => ['name' => 'Produit C', 'price' => 5.99],
];
$csrf_token = generateCSRFToken();
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Boutique en ligne</title>
</head>
<body>
<h1>Produits disponibles</h1>
<ul>
<?php foreach ($products as $id => $product): ?>
<li>
<?php echo htmlspecialchars($product['name']); ?> -
<?php echo number_format($product['price'], 2); ?> €
<form method="post" style="display: inline;">
<input type="hidden" name="csrf_token" value="<?php echo
$csrf_token; ?>">
<input type="hidden" name="product_id" value="<?php echo
$id; ?>">
<button type="submit" name="add_to_cart">Ajouter au
panier</button>
</form>
</li>
<?php endforeach; ?>
</ul>
<h2>Votre panier</h2>
<?php if (empty($_SESSION['cart'])): ?>
<p>Votre panier est vide.</p>
<?php else: ?>
<ul>
<?php foreach ($_SESSION['cart'] as $id => $quantity): ?>
<li>
<?php echo htmlspecialchars($products[$id]['name']); ?> -
Quantité : <?php echo $quantity; ?> -
Prix : <?php echo number_format($products[$id]['price'] *
$quantity, 2); ?> €
<form method="post" style="display: inline;">
<input type="hidden" name="csrf_token" value="<?php
echo $csrf_token; ?>">
<input type="hidden" name="product_id" value="<?php
echo $id; ?>">
<button type="submit"
name="remove_from_cart">Supprimer</button>
</form>
</li>
<?php endforeach; ?>
</ul>
<p>
Total : <?php
$total = array_sum(array_map(function($id, $quantity) use
($products) {
return $products[$id]['price'] * $quantity;
}, array_keys($_SESSION['cart']), $_SESSION['cart']));
echo number_format($total, 2);
?> €
</p>
<a href="checkout.php">Procéder au paiement</a>
<?php endif; ?>
</body>
</html>
<?php
// checkout.php
require_once 'config.php';
initCart();
$products = [
1 => ['name' => 'Produit A', 'price' => 10.99],
2 => ['name' => 'Produit B', 'price' => 24.99],
3 => ['name' => 'Produit C', 'price' => 5.99],
];
$total = 0;
$csrf_token = generateCSRFToken();
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Paiement</title>
</head>
<body>
<h1>Résumé de la commande</h1>
<?php if ($payment_successful): ?>
<p>Paiement réussi ! Voici le résumé de votre commande :</p>
<?php else: ?>
<p>Veuillez confirmer votre commande :</p>
<?php endif; ?>
<ul>
<?php foreach ($order_summary as $id => $quantity): ?>
<li>
<?php echo htmlspecialchars($products[$id]['name']); ?> -
Quantité : <?php echo $quantity; ?> -
Prix : <?php echo number_format($products[$id]['price'] *
$quantity, 2); ?> €
</li>
<?php $total += $products[$id]['price'] * $quantity; ?>
<?php endforeach; ?>
</ul>
Ces améliorations rendent le système de panier plus sécurisé et plus robuste, tout en offrant
une meilleure expérience utilisateur.
<?php
// Afficher toutes les erreurs
error_reporting(E_ALL);
ini_set('display_errors', 1);
18.3.1 Try-Catch
<?php
try {
// Code susceptible de générer une exception
$result = 10 / 0;
} catch (Exception $e) {
echo "Une erreur s'est produite : " . $e->getMessage();
}
?>
18.3.2 Throw
Vous pouvez lancer vos propres exceptions :
<?php
function diviser($a, $b) {
if ($b == 0) {
throw new Exception("Division par zéro impossible");
}
return $a / $b;
}
try {
echo diviser(10, 0);
} catch (Exception $e) {
echo "Erreur : " . $e->getMessage();
}
?>
18.3.3 Finally
Le bloc finally s'exécute toujours, qu'une exception soit levée ou non :
<?php
try {
// Code susceptible de générer une exception
} catch (Exception $e) {
// Gestion de l'exception
} finally {
// Ce code s'exécute toujours
}
?>
<?php
class DatabaseException extends Exception {}
class FileNotFoundException extends Exception {}
try {
// Simulation d'une erreur de base de données
throw new DatabaseException("Erreur de connexion à la base de données");
} catch (DatabaseException $e) {
echo "Erreur de BDD : " . $e->getMessage();
} catch (FileNotFoundException $e) {
echo "Fichier non trouvé : " . $e->getMessage();
} catch (Exception $e) {
echo "Autre erreur : " . $e->getMessage();
}
?>
<?php
function customErrorHandler($errno, $errstr, $errfile, $errline) {
$message = date("Y-m-d H:i:s") . " - Erreur [$errno] : $errstr dans
$errfile à la ligne $errline\n";
error_log($message, 3, "error.log");
}
set_error_handler("customErrorHandler");
// Exemple d'utilisation
$undefined_variable;
?>
Exercice 18.1
Créez une classe BanqueCompte avec une méthode retirer qui lance une exception
personnalisée SoldeInsuffisantException si le retrait demandé dépasse le solde disponible.
Implémentez également une méthode deposer . Utilisez cette classe dans un script qui gère
les exceptions et affiche des messages appropriés.
Corrigé 18.1
<?php
class SoldeInsuffisantException extends Exception {}
class BanqueCompte {
private $solde;
private $numero;
// Utilisation
try {
$compte = new BanqueCompte("123456", 1000);
echo "Solde initial : " . $compte->getSolde() . " €\n";
$compte->deposer(500);
echo "Après dépôt : " . $compte->getSolde() . " €\n";
$compte->retirer(300);
echo "Après retrait : " . $compte->getSolde() . " €\n";
Exercice 18.2
Créez une fonction lireFichierConfig qui lit un fichier de configuration et retourne son
contenu sous forme de tableau associatif. Cette fonction doit gérer les exceptions pour les
erreurs courantes (fichier non trouvé, permissions insuffisantes, etc.) et utiliser un logger
personnalisé pour enregistrer ces erreurs.
Corrigé 18.2
<?php
class ConfigFileException extends Exception {}
function lireFichierConfig($fichier) {
try {
if (!file_exists($fichier)) {
throw new ConfigFileException("Le fichier de configuration
n'existe pas");
}
if (!is_readable($fichier)) {
throw new ConfigFileException("Le fichier de configuration n'est
pas lisible");
}
$contenu = file_get_contents($fichier);
if ($contenu === false) {
throw new ConfigFileException("Impossible de lire le contenu du
fichier de configuration");
}
// Utilisation
try {
$config = lireFichierConfig('config.ini');
print_r($config);
} catch (ConfigFileException $e) {
echo "Erreur de configuration : " . $e->getMessage() . "\n";
// Ici, on pourrait charger une configuration par défaut ou arrêter
l'application
} catch (Exception $e) {
echo "Erreur inattendue : " . $e->getMessage() . "\n";
}
?>
Ce code illustre :
Ces exercices permettent aux étudiants de pratiquer la gestion des erreurs et des exceptions
dans des scénarios réalistes, tout en appliquant les bonnes pratiques de logging et de gestion
des exceptions personnalisées.
<?php
$xml_string = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<livres>
<livre>
<titre>PHP pour les débutants</titre>
<auteur>John Doe</auteur>
<annee>2020</annee>
</livre>
<livre>
<titre>Maîtriser XML en PHP</titre>
<auteur>Jane Smith</auteur>
<annee>2019</annee>
</livre>
</livres>
XML;
$xml = simplexml_load_string($xml_string);
foreach ($xml->livre as $livre) {
echo $livre->titre . " par " . $livre->auteur . " (" . $livre->annee .
")\n";
}
?>
<?php
$xml = new SimpleXMLElement('<livres/>');
$livre1 = $xml->addChild('livre');
$livre1->addChild('titre', 'PHP avancé');
$livre1->addChild('auteur', 'Alice Johnson');
$livre1->addChild('annee', '2021');
$livre2 = $xml->addChild('livre');
$livre2->addChild('titre', 'Bases de données et PHP');
$livre2->addChild('auteur', 'Bob Williams');
$livre2->addChild('annee', '2022');
echo $xml->asXML();
?>
<?php
$reader = new XMLReader();
$reader->open('grand_fichier.xml');
while ($reader->read()) {
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'livre') {
$node = $reader->expand();
$titre = $node->getElementsByTagName('titre')->item(0)->nodeValue;
$auteur = $node->getElementsByTagName('auteur')->item(0)->nodeValue;
echo "Livre : $titre par $auteur\n";
}
}
$reader->close();
?>
<?php
$data = [
'nom' => 'Dupont',
'prenom' => 'Jean',
'age' => 30,
'hobbies' => ['lecture', 'sport', 'voyage']
];
<?php
$json_string = '{"nom":"Dupont","prenom":"Jean","age":30,"hobbies":
["lecture","sport","voyage"]}';
<?php
$json_invalide = '{"nom":"Dupont","prenom":}'; // JSON invalide
$data = json_decode($json_invalide);
if (json_last_error() !== JSON_ERROR_NONE) {
echo "Erreur JSON : " . json_last_error_msg();
}
?>
Exercice 19.1
Créez une classe GestionnaireLivres qui peut lire et écrire une liste de livres à la fois en
XML et en JSON. La classe doit avoir des méthodes pour ajouter un livre, supprimer un livre, et
afficher tous les livres. Implémentez également des méthodes pour sauvegarder la liste en XML
et en JSON, ainsi que pour charger la liste à partir de ces formats.
Corrigé 19.1
<?php
class Livre {
public $titre;
public $auteur;
public $annee;
// Utilisation
$gestionnaire = new GestionnaireLivres();
$gestionnaire->sauvegarderXML('livres.xml');
$gestionnaire->sauvegarderJSON('livres.json');
$gestionnaire->supprimerLivre(1);
$gestionnaire->chargerXML('livres.xml');
Cet exercice permet aux étudiants de pratiquer la manipulation de XML et JSON dans un
contexte concret de gestion de données. Il couvre la création, la lecture, la modification et la
sauvegarde de données dans ces deux formats, tout en appliquant des concepts de
programmation orientée objet.
Exercice 19.2
Créez un script PHP qui consomme une API RESTful publique (par exemple, l'API de GitHub
ou OpenWeatherMap) qui renvoie des données en JSON. Le script doit récupérer les données,
les parser, et afficher certaines informations de manière formatée. Gérez également les erreurs
potentielles lors de l'appel à l'API et du parsing des données.
Corrigé 19.2
<?php
function appelerAPI($url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'PHP Script');
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception(curl_error($ch));
}
curl_close($ch);
return $response;
}
function afficherMeteo($ville) {
$apiKey = 'VOTRE_CLE_API'; // Remplacez par votre clé API OpenWeatherMap
$url = "http://api.openweathermap.org/data/2.5/weather?
q=$ville&appid=$apiKey&units=metric&lang=fr";
try {
$response = appelerAPI($url);
$data = json_decode($response, true);
// Utilisation
$ville = 'Paris'; // Vous pouvez changer la ville ici
afficherMeteo($ville);
?>
Note : Pour utiliser ce script, les étudiants devront s'inscrire sur OpenWeatherMap et obtenir
une clé API gratuite. Cela leur donnera également une expérience pratique de l'utilisation des
clés API, un concept important dans le développement web moderne.
Ces exercices offrent une expérience pratique de la manipulation de données XML et JSON,
ainsi que de l'interaction avec des API web, des compétences essentielles pour les
développeurs PHP modernes.
20.1.2 Callbacks
Les callbacks sont des fonctions passées en argument à d'autres fonctions, qui seront
exécutées une fois une tâche asynchrone terminée.
function tacheAsynchrone($callback) {
// Simulation d'une tâche asynchrone
sleep(2);
$callback("Tâche terminée");
}
tacheAsynchrone(function($resultat) {
echo $resultat;
});
echo "Cette ligne s'affiche immédiatement";
<?php
require 'vendor/autoload.php';
$loop = React\EventLoop\Factory::create();
$loop->addTimer(2, function () {
echo "2 secondes se sont écoulées\n";
});
$loop->addPeriodicTimer(1, function () {
echo "Tic\n";
});
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Promise\Promise;
<?php
function generateur() {
yield "Première étape";
yield "Deuxième étape";
yield "Troisième étape";
}
$gen = generateur();
foreach ($gen as $etape) {
echo $etape . "\n";
}
<?php
// Notez que ceci est un exemple conceptuel et ne fonctionnera pas tel quel
dans PHP actuel
async function obtenirDonnees() {
$resultat = await appelerAPIAsynchrone();
return $resultat;
}
Exercice 20.1
Créez un script PHP utilisant ReactPHP pour simuler le traitement asynchrone de plusieurs
tâches. Le script doit :
Corrigé 20.1
<?php
require 'vendor/autoload.php';
use React\EventLoop\Factory;
$loop = Factory::create();
tacheAsynchrone("A", 2, $loop);
tacheAsynchrone("B", 4, $loop);
tacheAsynchrone("C", 3, $loop);
$loop->addPeriodicTimer(1, function () {
echo "En cours d'exécution...\n";
});
$loop->addTimer(5, function () use ($loop) {
$loop->stop();
});
Exercice 20.2
Créez une classe AsyncHttpClient qui utilise Guzzle Promises pour effectuer des requêtes
HTTP asynchrones. La classe doit avoir une méthode pour effectuer plusieurs requêtes GET en
parallèle et retourner les résultats. Testez la classe en effectuant des requêtes vers plusieurs
API publiques.
Corrigé 20.2
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Promise;
class AsyncHttpClient {
private $client;
return Promise\Utils::settle($promises)->wait();
}
}
// Utilisation
$client = new AsyncHttpClient();
$urls = [
'users' => 'https://jsonplaceholder.typicode.com/users',
'posts' => 'https://jsonplaceholder.typicode.com/posts',
'comments' => 'https://jsonplaceholder.typicode.com/comments'
];
$results = $client->getAsync($urls);
Cet exercice permet aux étudiants de créer un client HTTP asynchrone simple utilisant Guzzle
Promises. Il démontre comment effectuer plusieurs requêtes HTTP en parallèle et gérer leurs
résultats de manière asynchrone.
Rapide à exécuter
Indépendant des autres tests
Répétable (même résultat à chaque exécution)
Auto-vérifiable (pas d'intervention manuelle nécessaire)
Écrit au bon moment (idéalement avant ou pendant le développement de la fonctionnalité)
21.2 PHPUnit
PHPUnit est le framework de test unitaire le plus populaire pour PHP.
<?php
use PHPUnit\Framework\TestCase;
$this->assertTrue($condition);
$this->assertFalse($condition);
$this->assertEquals($attendu, $obtenu);
$this->assertNull($valeur);
$this->assertContains($aiguille, $meule);
$this->assertCount($nombreAttendu, $tableau);
// Tests...
}
/**
* @group slow
*/
public function testOperationLongue()
{
// Test long...
}
21.5 Tests de données
21.5.1 Tests paramétrés
/**
* @dataProvider additionProvider
*/
public function testAddition($a, $b, $resultatAttendu)
{
$this->assertEquals($resultatAttendu, $a + $b);
}
Exercice 21.1
Créez une classe Calculator avec des méthodes pour l'addition, la soustraction, la
multiplication et la division. Écrivez ensuite une classe de test CalculatorTest qui teste toutes
ces méthodes, y compris les cas limites (comme la division par zéro).
Corrigé 21.1
<?php
// Calculator.php
class Calculator
{
public function add($a, $b)
{
return $a + $b;
}
// CalculatorTest.php
use PHPUnit\Framework\TestCase;
./vendor/bin/phpunit CalculatorTest.php
Exercice 21.2
Créez une classe UserManager qui gère les utilisateurs (ajout, suppression, recherche) en
utilisant une base de données. Utilisez des mocks pour simuler la base de données dans vos
tests unitaires.
Corrigé 21.2
<?php
// UserManager.php
class UserManager
{
private $db;
// UserManagerTest.php
use PHPUnit\Framework\TestCase;
$this->assertEquals(1, $result);
}
$this->assertTrue($result);
}
$dbMock = $this->createMock(DatabaseInterface::class);
$dbMock->expects($this->once())
->method('selectOne')
->with('users', ['id' => 1])
->willReturn($expectedUser);
$this->assertEquals($expectedUser, $result);
}
}
Ces exercices permettent aux étudiants de pratiquer l'écriture de tests unitaires pour des
classes simples et plus complexes, en utilisant des assertions de base et des mocks pour
simuler des dépendances externes comme une base de données.
Développement local
Environnement de test (staging)
Production
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
Configuration Nginx :
location / {
try_files $uri $uri/ /index.php?$query_string;
}
22.2.2 PHP-FPM
Configuration de base de PHP-FPM :
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
Sécurisation de MySQL :
<?php
namespace Deployer;
require 'recipe/laravel.php';
host('project.com')
->set('deploy_path', '/var/www/project');
after('deploy:failed', 'deploy:unlock');
// webpack.config.js
module.exports = {
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new MiniCssExtractPlugin(),
],
// ...
};
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = new Logger('name');
$log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));
22.5.2 Monitoring
Mise en place d'outils de monitoring comme New Relic ou Datadog.
Exercice 22.1
Créez un script de déploiement bash simple qui effectue les étapes suivantes :
Corrigé 22.1
#!/bin/bash
# Configuration
APP_DIR="/var/www/monapp"
REPO_URL="https://github.com/username/monapp.git"
# Nettoyage du cache
log "Nettoyage du cache"
php artisan cache:clear
php artisan config:clear
php artisan view:clear
Exercice 22.2
Configurez un pipeline CI/CD simple avec GitHub Actions pour une application PHP. Le pipeline
doit :
Corrigé 22.2
Créez un fichier .github/workflows/ci-cd.yml dans votre projet avec le contenu suivant :
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v2
1. Configurer les secrets dans les paramètres de votre dépôt GitHub (HOST, USERNAME,
SSH_PRIVATE_KEY)
2. Avoir PHPUnit et PHP_CodeSniffer installés dans votre projet
3. Avoir un serveur de staging configuré avec un accès SSH
Ce pipeline exécutera les tests, vérifiera le style du code, et si tout est OK, déploiera
l'application sur le serveur de staging.
Ces exercices permettent aux étudiants de mettre en pratique les concepts de déploiement, à
la fois manuellement avec un script bash et de manière automatisée avec un pipeline CI/CD, ce
qui est essentiel dans le développement web moderne.
// Mauvais exemple
class User {
public function saveUser() { /* ... */ }
public function generateReport() { /* ... */ }
}
// Bon exemple
class User {
public function save() { /* ... */ }
}
class ReportGenerator {
public function generateUserReport(User $user) { /* ... */ }
}
Les objets d'une superclasse doivent pouvoir être remplacés par des objets de ses sous-
classes sans affecter la cohérence du programme.
interface Logger {
public function log($message);
}
class UserManager {
private $logger;
class Database {
private static $instance = null;
private $connection;
// Utilisation
$db = Database::getInstance();
$result = $db->query("SELECT * FROM users");
23.2.2 Factory
Définit une interface pour créer un objet, mais laisse les sous-classes décider quelle classe
instancier.
interface Animal {
public function makeSound();
}
class AnimalFactory {
public static function createAnimal($type) {
switch ($type) {
case 'dog':
return new Dog();
case 'cat':
return new Cat();
default:
throw new Exception("Animal type not supported");
}
}
}
// Utilisation
$dog = AnimalFactory::createAnimal('dog');
echo $dog->makeSound(); // Woof!
23.2.3 Observer
Définit une dépendance un-à-plusieurs entre objets, de sorte que lorsqu'un objet change d'état,
tous ses dépendants sont notifiés et mis à jour automatiquement.
interface Observer {
public function update($data);
}
class Subject {
private $observers = [];
private $state;
// Utilisation
$subject = new Subject();
$observer1 = new ConcreteObserver();
$observer2 = new ConcreteObserver();
$subject->attach($observer1);
$subject->attach($observer2);
$subject->setState("Nouvel état");
Garder le code aussi simple que possible. La simplicité rend le code plus facile à comprendre
et à maintenir.
function getUser($id) {
$user = // recherche de l'utilisateur
if (!$user) {
throw new UserNotFoundException("Utilisateur avec l'ID $id non
trouvé");
}
return $user;
}
try {
$user = getUser(123);
} catch (UserNotFoundException $e) {
// Gérer l'erreur
log($e->getMessage());
}
Exercice 23.1
Refactorez le code suivant en appliquant le principe de responsabilité unique (SRP) et le
pattern Factory :
class User {
public function createUser($data) {
// Logique de création d'utilisateur
}
// Utilisation
$user = new User();
$user->createUser($userData);
$user->sendEmail("[email protected]", "Bienvenue", "Contenu du message");
$user->generateReport();
Corrigé 23.1
class User {
private $id;
private $name;
private $email;
// Getters et setters...
}
class UserFactory {
public static function createUser($data) {
// Vérification et validation des données
return new User($data['id'], $data['name'], $data['email']);
}
}
class EmailService {
public function sendEmail($to, $subject, $body) {
// Logique d'envoi d'email
}
}
class ReportGenerator {
public function generateUserReport(User $user) {
// Logique de génération de rapport pour un utilisateur
}
}
// Utilisation
$userFactory = new UserFactory();
$user = $userFactory->createUser($userData);
Corrigé 23.2
interface Observer {
public function update($data);
}
class Newsletter {
private $observers = [];
private $content;
// Utilisation
$newsletter = new Newsletter();
$newsletter->attach($alice);
$newsletter->attach($bob);
$newsletter->attach($charlie);
$newsletter->detach($bob);
Ces exercices permettent aux étudiants de mettre en pratique les concepts de SOLID, les
patterns de conception, et les bonnes pratiques de développement dans des scénarios
réalistes.