Logo ESR

ESR - Ecole Supérieure de Rabat

en Management et Ingénierie

Programmation web orientée objet et introduction à Laravel

Dernière mise à jour : Octobre 2025

Parcours d'apprentissage

Partie 4 : Architecture d'une Application Robuste

Partie 6 : Synthèse et Transition vers Laravel

Modules à venir

  • Plongée dans l'écosystème Laravel (Eloquent, Blade...)
  • Authentification et sécurité dans Laravel
  • Tests automatisés et bonnes pratiques

Partie 1 : Du PHP Historique au PHP Moderne

Chapitre 1 : Accueil & Objectifs

Ce cours a été conçu pour vous, étudiants en 4ème année, afin de solidifier vos connaissances en PHP et de vous préparer efficacement à l'écosystème professionnel. Nous explorerons la syntaxe moderne, les architectures robustes et les bonnes pratiques qui sont les prérequis indispensables pour exceller avec des frameworks comme Laravel.

Objectif Principal

Aller au-delà de la syntaxe pour maîtriser les concepts : typage strict, programmation objet solide et standards de l'industrie (PSR). L'objectif est de vous apprendre à écrire un code qui soit non seulement fonctionnel, mais aussi lisible, maintenable et sécurisé.

Préparation à Laravel

Chaque module est une pierre angulaire de Laravel. En comprenant la POO, l'autoloading avec Composer, l'architecture MVC ou l'interaction sécurisée avec la base de données via PDO, vous ne ferez pas qu'utiliser Laravel : vous comprendrez sa philosophie et son fonctionnement interne.

Approche Pédagogique

Ce cours privilégie l'apprentissage actif. Des comparatifs de code interactifs, des schémas visuels et des exemples pratiques vous aideront à assimiler des concepts parfois abstraits.

Chapitre 2 : L'Évolution de PHP

Pour maîtriser le PHP moderne, il faut comprendre son passé. Son histoire, marquée par le pragmatisme, explique à la fois ses anciennes critiques et son incroyable résilience. L'acronyme lui-même a évolué : de **"Personal Home Page Tools" de Rasmus Lerdorf b>en 1994 **, il est devenu l'acronyme récursif **"PHP: Hypertext Preprocessor"**.

2.1. Analyse des Critiques Historiques

De nombreuses critiques ont jalonné l'histoire de PHP. Si certaines sont aujourd'hui obsolètes, elles trouvent leur origine dans les fondations mêmes du langage et son évolution progressive. Comprendre ces critiques est essentiel pour apprécier le chemin parcouru.

Critique : "PHP n'est pas un véritable langage de programmation"

Cette affirmation s'ancre dans les origines de PHP. Il n'a pas été conçu comme un langage de programmation formel, mais comme une suite d'outils pour dynamiser des pages web. Ses premières versions s'apparentaient davantage à un moteur de template, où la logique était intégrée dans des commentaires HTML.

<!--include /text/header.html--> <!--getenv HTTP_USER_AGENT--> <!--ifsubstr $exec_result Mozilla--> Hé, vous utilisez Netscape !<p> <!--endif--> <!--sql database select * from table where user='$username'--> <!--ifless $numentries 1--> Désolé, cette ligne n'existe pas<p> <!--endif exit-->

Il a fallu attendre **PHP 3** pour voir apparaître une syntaxe plus structurée, et surtout **PHP 5** pour l'introduction d'un modèle objet robuste (classes, visibilité `public`/`private`, interfaces), faisant de PHP un langage apte à la construction d'applications complexes.

L'ADN "Double Face" de PHP

Cet héritage confère à PHP une double nature unique. Il peut être utilisé de manière très simple et procédurale, en mêlant directement la logique à l'affichage, ce qui le rend très accessible pour des tâches rapides.

<?php if (strpos($_SERVER['HTTP_USER_AGENT'], "Firefox") !== false): ?> <p>Hé, vous utilisez Firefox !<p> <?php endif; ?> <?php $posts = mysqli_num_rows(mysqli_query($conn, 'SELECT * FROM wp_posts')); ?> <?php if ($posts === 0): ?> <p>Désolé, aucun article n'existe.<p> <?php else: ?> <p>Vous avez écrit <?= $posts; ?> articles !</p> <?php endif; ?>

Mais il permet également d'écrire un code hautement structuré, orienté objet, testable et maintenable, à l'image de ce que l'on trouve dans les frameworks professionnels modernes.

<?php namespace App\Basket\Table; use App\Auth\User; use App\Basket\Entity\Basket as BasketEntity; class BasketTable extends Table { public function findForUser(int $userId): ?BasketEntity { return $this->makeQuery()->where("user_id = $userId")->fetch() ?: null; } public function addRow(BasketEntity $basket, Product $product): void { // ... logique d'insertion ... } }

Critique : "PHP est un langage pour débutants"

Son accessibilité est une force, mais a aussi contribué à cette image. En permettant de produire des résultats visibles rapidement sans une compréhension profonde de la programmation, PHP a favorisé la prolifération de code de mauvaise qualité (mal organisé, failles de sécurité). Cependant, réduire PHP à cet usage est une erreur. L'existence de projets d'envergure mondiale (Wikipedia, Slack, Etsy) et d'un écosystème mature (Composer, frameworks robustes) prouve qu'il offre la profondeur nécessaire pour des applications professionnelles complexes.

Critique : "PHP est un langage inconsistant"

C'est l'une des critiques les plus fondées, découlant de son développement organique et de son attachement à la rétrocompatibilité. Les fonctions natives souffrent de nomenclatures et d'ordres de paramètres parfois illogiques.

// Nommage : underscore ou pas ? base64_encode() urlencode() // Nommage : 'to' ou '2' ? bin2hex() strtolower()
// Ordre des arguments inversé strpos($haystack, $needle); array_search($needle, $haystack);

De plus, son typage faible, s'il n'est pas géré avec rigueur (via `===` et les déclarations de types modernes), peut entraîner des comportements inattendus.

var_dump("6" == " 6"); // bool(true) var_dump("foo" == 0); // bool(true)

Aujourd'hui, ces problèmes sont largement atténués par l'utilisation de frameworks et de linters de code qui imposent une cohérence, et par les fonctionnalités de typage strict introduites dans les versions récentes.

Critique : "PHP est lent"

Historiquement, le modèle d'exécution de PHP (lire, interpréter, exécuter à chaque requête) était un goulot d'étranglement. Ce problème a été résolu en deux temps :

  • PHP 5.5 avec OPcache : Mise en cache du code pré-compilé (opcode) en mémoire, éliminant la relecture et réinterprétation coûteuse des fichiers à chaque requête.
  • PHP 7 et 8 : Une réécriture profonde du moteur a doublé les performances, et l'introduction d'un compilateur JIT (Just-In-Time) les a encore améliorées pour les tâches intensives. Cette critique n'est plus pertinente aujourd'hui.

2.2. La Renaissance (PHP 7 & 8)

La communauté a réagi de manière spectaculaire. **PHP 7 (2015)** fut un choc : **deux fois plus rapide** que PHP 5.6, il a pulvérisé la critique sur la lenteur et introduit le typage strict.

**PHP 8** a continué sur cette lancée en se concentrant sur l'ergonomie, rendant le code plus concis, lisible et robuste. C'est ce PHP moderne que les frameworks comme Laravel exigent.

Promotion des Propriétés du Constructeur (PHP 8)

Réduit le code répétitif lors de l'initialisation des objets.

Opérateur Nullsafe (PHP 8)

Met fin aux chaînes de vérifications `if ($objet !== null)`.

Expression `match` (PHP 8)

Un successeur plus sûr et plus concis que `switch`.

Propriétés Typées (Depuis PHP 7.4)

Garantit la cohérence des données au sein de vos objets.

Partie 2 : Du PHP Historique au PHP Moderne

Chapitre 3 : Maîtriser la Programmation Orientée Objet

La Programmation Orientée Objet (POO) est un paradigme qui structure le code en "objets" représentant des concepts du monde réel (un utilisateur, un produit, une facture). Plutôt que d'avoir des fonctions éparpillées, on regroupe les données (propriétés) et les comportements (méthodes) qui leur sont liés au sein d'une même entité.

Pourquoi utiliser la POO ? Pour écrire un code plus organisé, plus réutilisable et plus facile à maintenir. Elle est indispensable pour construire des applications complexes et constitue le fondement de tous les frameworks modernes comme Laravel ou Symfony.

3.1. Classes, Objets, Propriétés et Méthodes

Une **classe** est un plan de construction. Un **objet** est une instance de cette classe. Les **propriétés** sont les variables de la classe (ses données), et les **méthodes** sont ses fonctions (ses actions).

<?php // La classe 'User' est le plan class User { // Propriétés (les données de l'utilisateur) public string $username; public string $email; // Méthode (une action que l'utilisateur peut faire) public function sayHello(): string { return "Bonjour, je suis " . $this->username; } }

3.2. Instanciation et le Rôle du Constructeur

L'instanciation est l'acte de créer un objet concret (une instance) à partir d'une classe en utilisant le mot-clé new. Pour s'assurer qu'un objet est créé dans un état valide et prêt à l'emploi, PHP appelle automatiquement une méthode spéciale au moment de l'instanciation : le constructeur (__construct).

Le constructeur est idéal pour initialiser les propriétés d'un objet avec les valeurs nécessaires à son fonctionnement. Les arguments passés après le nom de la classe lors de l'appel `new` sont directement transmis au constructeur.

<?php class Product { private string $name; private float $price; // Le constructeur est appelé lors de `new Product(...)` public function __construct(string $name, float $price) { $this->name = $name; $this->price = $price; echo "Produit '" . $name . "' créé.\n"; } public function getDetails(): string { return $this->name . " - " . $this->price . " €"; } } // On instancie la classe Product en passant les arguments requis par le constructeur. $product1 = new Product('Livre PHP 8', 29.99); // Affiche "Produit 'Livre PHP 8' créé." echo $product1->getDetails(); // Affiche "Livre PHP 8 - 29.99 €"

3.3. Encapsulation (Visibilité)

L'encapsulation consiste à protéger l'état interne d'un objet. On utilise des mots-clés de visibilité pour contrôler l'accès aux propriétés et méthodes :

  • public : Accessible de partout.
  • protected : Accessible uniquement par la classe elle-même et ses classes enfants.
  • private : Accessible uniquement par la classe elle-même.
class BankAccount { // Le solde est privé pour empêcher une modification directe et non contrôlée private float $balance; public function __construct(float $initialAmount) { $this->balance = $initialAmount; } // On expose des méthodes publiques pour interagir avec le solde public function deposit(float $amount): void { if ($amount > 0) { $this->balance += $amount; } } public function getBalance(): float { return $this->balance; } } $account = new BankAccount(100); // $account->balance = 5000; // Erreur fatale ! Impossible d'accéder à une propriété privée. $account->deposit(50); // OK, on passe par la méthode publique.

3.4. Héritage

L'héritage permet à une classe (enfant) d'hériter des propriétés et méthodes d'une autre classe (parente). Cela favorise la réutilisation du code en créant des spécialisations.

// Classe parente class Animal { public function eat() { return "Je mange."; } } // La classe Dog hérite de Animal class Dog extends Animal { public function bark() { return "Wouaf !"; } } $dog = new Dog(); echo $dog->eat(); // Affiche "Je mange." (méthode héritée) echo $dog->bark(); // Affiche "Wouaf !" (méthode propre)

3.5. Polymorphisme : Un Comportement, Plusieurs Formes

Le polymorphisme (du grec "plusieurs formes") est une conséquence directe de l'héritage. Il permet de manipuler des objets de classes différentes comme s'ils étaient du même type (le type de la classe parente). Le programme détermine alors au moment de l'exécution quelle méthode spécifique appeler (celle du parent ou celle de l'enfant). C'est ce qui permet d'écrire du code générique et flexible, capable de traiter une famille d'objets sans connaître les détails de chaque classe.

<?php class Animal { public function makeSound() { echo "Son d'animal générique\n"; } } class Dog extends Animal { public function makeSound() { // Redéfinition (override) de la méthode echo "Wouaf !\n"; } } class Cat extends Animal { public function makeSound() { // Redéfinition de la méthode echo "Miaou !\n"; } } // Cette fonction accepte n'importe quel objet de type Animal. function animalSound(Animal $animal) { $animal->makeSound(); } $dog = new Dog(); $cat = new Cat(); // On appelle la MÊME fonction avec des objets de types différents. animalSound($dog); // Affiche "Wouaf !" animalSound($cat); // Affiche "Miaou !"

3.6. Surcharge de Méthode (Overloading) : L'Approche de PHP

Contrairement à des langages comme Java ou C#, PHP ne supporte pas la surcharge de méthode traditionnelle, qui consiste à définir plusieurs méthodes avec le même nom mais des signatures (nombre ou type de paramètres) différentes. Déclarer deux fois la même fonction dans une classe PHP générera une erreur fatale.

Cependant, PHP permet d'obtenir un comportement très similaire en créant une méthode unique et flexible qui réagit différemment en fonction des arguments qu'elle reçoit. On y parvient le plus souvent en utilisant des paramètres optionnels avec des valeurs par défaut.

<?php class Messager { // Une seule méthode pour gérer plusieurs cas de figure public function envoyer(string $message, string $destinataire = null) { if ($destinataire === null) { // Comportement 1 : si aucun destinataire n'est fourni return "Diffusion générale : " . $message; } else { // Comportement 2 : si un destinataire est spécifié return "Message pour " . $destinataire . " : " . $message; } } } $messager = new Messager(); // 1. Appel avec un seul argument obligatoire echo $messager->envoyer("Le serveur sera en maintenance ce soir."); // Affiche "Diffusion générale : Le serveur sera en maintenance ce soir." echo "\n"; // 2. Appel avec les deux arguments echo $messager->envoyer("Votre facture est disponible.", "Alice"); // Affiche "Message pour Alice : Votre facture est disponible."

3.7. Redéfinition de Méthode (Overriding) : Spécialiser le Comportement

Alors que la surcharge (simulée en PHP) concerne des méthodes de même nom au sein d'une même classe, la redéfinition (Overriding) est un concept fondamental de l'héritage. Elle permet à une classe enfant de fournir une implémentation spécifique pour une méthode qui est déjà définie dans sa classe parente. La signature de la méthode (nom, nombre et type de paramètres) doit rester compatible.

C'est un pilier du polymorphisme, car cela permet à un objet enfant de réagir différemment au même appel de méthode que son parent, adaptant ainsi son comportement.

<?php class Forme { public function calculerAire(): string { return "Le calcul de l'aire n'est pas défini pour une forme générique."; } } // La classe Carre hérite de Forme et REDÉFINIT sa méthode calculerAire. class Carre extends Forme { private int $cote; public function __construct(int $cote) { $this->cote = $cote; } // Implémentation spécifique pour le carré (override) public function calculerAire(): string { return "L'aire du carré est : " . ($this->cote * $this->cote); } } $forme = new Forme(); echo $forme->calculerAire(); // Affiche le message de la classe parente. echo "\n"; $carre = new Carre(5); echo $carre->calculerAire(); // Affiche "L'aire du carré est : 25". La méthode de l'enfant est appelée.

3.8. Membres Statiques : Propriétés et Méthodes de Classe

Jusqu'à présent, toutes les propriétés et méthodes que nous avons vues appartenaient à une instance d'objet (un objet créé avec `new`). Le mot-clé `static` permet de déclarer des membres qui appartiennent à la classe elle-même, et non à une de ses instances. Ils sont accessibles directement via le nom de la classe, sans avoir besoin de créer un objet.

  • Propriétés Statiques : Elles sont partagées par toutes les instances d'une classe. Si une instance modifie la valeur, la modification est visible par toutes les autres. Elles sont utiles pour des compteurs, des configurations, etc.
  • Méthodes Statiques : Elles peuvent être appelées sans créer d'objet. Elles sont souvent utilisées pour des fonctions utilitaires (ex: conversions, validateurs) qui n'ont pas besoin de l'état d'un objet pour fonctionner. Attention : une méthode statique ne peut pas utiliser la pseudo-variable $this, car elle n'est pas liée à une instance.
<?php
class CompteurUtilisateurs
{
    // Cette propriété appartient à la CLASSE, il n'y en a qu'une seule copie.
    public static int $nombreTotal = 0;

    public function __construct()
    {
        // On accède à la propriété statique avec self::
        self::$nombreTotal++;
    }

    // Cette méthode peut être appelée sans créer d'objet.
    public static function afficherTotal(): void
    {
        echo "Nombre total d'utilisateurs créés : " . self::$nombreTotal;
    }
}

// On peut appeler la méthode statique SANS instance.
CompteurUtilisateurs::afficherTotal(); // Affiche 0
echo "\n";

$user1 = new CompteurUtilisateurs();
$user2 = new CompteurUtilisateurs();

// La propriété statique a été modifiée par les instances.
CompteurUtilisateurs::afficherTotal(); // Affiche 2

3.9. Documenter son Code avec PHPDoc

Au-delà de rendre le code fonctionnel, il est crucial de le rendre compréhensible pour les autres développeurs (et pour soi-même dans le futur). PHPDoc est le standard de facto en PHP pour commenter le code. Ces commentaires, formatés d'une manière spécifique (/** ... */), ne sont pas de simples notes. Ils sont analysés par les éditeurs de code (comme VS Code) pour fournir de l'autocomplétion, de la validation de types et générer une documentation complète de l'application. C'est une pratique indispensable dans le développement professionnel.

<?php class CalculateurPrix { /** * Le taux de TVA par défaut. * @var float */ public const TAUX_DEFAUT = 0.20; /** * Calcule le prix toutes taxes comprises (TTC). * * @param float $prixHT Le prix hors taxes. Doit être positif. * @param float|null $tauxTVA Le taux de TVA (ex: 0.20 pour 20%). Si null, utilise TAUX_DEFAUT. * @return float Le prix TTC. * @throws \InvalidArgumentException Si le prix HT est négatif. */ public function calculerTTC(float $prixHT, ?float $tauxTVA = null): float { if ($prixHT < 0) { throw new \InvalidArgumentException("Le prix ne peut pas être négatif."); } $taux = $tauxTVA ?? self::TAUX_DEFAUT; return $prixHT * (1 + $taux); } }

Chapitre 4 : Concepts Avancés et Organisation du Code

Au-delà des bases, la POO offre des outils puissants pour organiser le code de manière logique, flexible et évolutive. Ces concepts sont au cœur du fonctionnement interne des frameworks.

4.1. Classes Abstraites et Interfaces

Ces deux concepts permettent de définir des "contrats" que d'autres classes doivent respecter.
• Une **classe abstraite** (`abstract`) est un modèle partiel de classe qui ne peut pas être instancié directement.


<?php
// On définit une classe abstraite avec le mot-clé 'abstract'.
// Elle sert de "modèle" ou de "plan" pour d'autres classes.
abstract class Vehicule
{
    protected string $marque;

    // Une classe abstraite peut avoir un constructeur et des méthodes déjà implémentées.
    public function __construct(string $marque)
    {
        $this->marque = $marque;
    }

    // Méthode concrète (avec du code) que toutes les classes enfants hériteront.
    public function getMarque(): string
    {
        return "Ce véhicule est de la marque " . $this->marque;
    }

    // On déclare une méthode abstraite. Elle n'a pas de corps (pas de { ... }).
    // Toute classe qui héritera de Vehicule SERA OBLIGÉE de définir cette méthode.
    abstract public function getNombreDeRoues(): int;
}

// La classe Voiture hérite de Vehicule.
class Voiture extends Vehicule
{
    // On DOIT implémenter la méthode abstraite du parent.
    public function getNombreDeRoues(): int
    {
        return 4;
    }
}

// La classe Moto hérite aussi de Vehicule.
class Moto extends Vehicule
{
    // Elle doit aussi implémenter la méthode avec sa propre logique.
    public function getNombreDeRoues(): int
    {
        return 2;
    }
}

// On ne peut pas instancier directement une classe abstraite.
// $vehicule = new Vehicule("Générique"); // ERREUR FATALE !

// On instancie les classes enfants, qui sont concrètes.
$maVoiture = new Voiture('Renault');
echo $maVoiture->getMarque() . " et a " . $maVoiture->getNombreDeRoues() . " roues.\n";

$maMoto = new Moto('Yamaha');
echo $maMoto->getMarque() . " et a " . $maMoto->getNombreDeRoues() . " roues.\n";

• Une **interface** (`interface`) est un contrat 100% abstrait : elle ne contient que des signatures de méthodes. Une classe peut `implement` plusieurs interfaces.

// L'interface définit un contrat : tout ce qui est "loggable" DOIT avoir une méthode formatMessage(). interface Loggable { public function formatMessage(): string; } // Une classe qui respecte ce contrat class LoginEvent implements Loggable { public function formatMessage(): string { return "Un utilisateur s'est connecté."; } } // Cette fonction peut maintenant accepter n'importe quel objet, du moment qu'il respecte le contrat Loggable. function writeToLog(Loggable $event) { $message = $event->formatMessage(); // ... écrire $message dans un fichier }

4.2. Les Traits : Réutiliser du Code Horizontalement

PHP ne supporte que l'héritage simple (une classe ne peut hériter que d'une seule autre classe). Pour contourner cette limite et partager du code entre des classes non liées, PHP propose les Traits. Un trait est un regroupement de méthodes (et de propriétés) que l'on peut "importer" dans une classe avec le mot-clé use.

C'est un mécanisme de réutilisation de code "horizontal", contrairement à l'héritage qui est "vertical". Par exemple, un trait `LoggerTrait` peut ajouter une capacité de logging à n'importe quelle classe, qu'il s'agisse d'un `UserService` ou d'un `OrderProcessor`.

<?php trait LoggerTrait { public function log(string $message) { // Concatène la date au message pour le logging echo '[' . date('Y-m-d H:i:s') . '] ' . get_class($this) . ' : ' . $message . "\n"; } } class UserService { // On importe le comportement du trait use LoggerTrait; public function createUser() { // On peut maintenant utiliser la méthode log() comme si elle était définie ici $this->log('Création d\'un utilisateur...'); } } $service = new UserService(); $service->createUser(); // Affiche "[DATE] UserService : Création d'un utilisateur..."

4.3. Le Singleton : Garantir une Instance Unique

Le pattern Singleton est un modèle de conception qui restreint l'instanciation d'une classe à un seul objet. Il est utile pour gérer des ressources partagées, comme une connexion à la base de données ou un objet de configuration, afin d'éviter la création d'instances multiples, coûteuses et potentiellement conflictuelles.

Son implémentation repose sur trois piliers : un constructeur privé pour empêcher l'instanciation externe, une propriété statique privée pour stocker l'unique instance, et une méthode statique publique (souvent `getInstance`) pour y accéder.

<?php class DatabaseConnection { // le type de $instance est soir PDO soit null d'ou ?PDO. private static ?PDO $instance = null; // Le constructeur est privé pour empêcher `new DatabaseConnection()`. private function __construct() {} public static function getInstance(): PDO { if (self::$instance === null) { $dsn = 'mysql:host=localhost;dbname=test'; self::$instance = new PDO($dsn, 'user', 'pass'); } return self::$instance; } } // On obtient toujours la MÊME instance, où que ce soit dans le code. $pdo1 = DatabaseConnection::getInstance(); $pdo2 = DatabaseConnection::getInstance(); var_dump($pdo1 === $pdo2); // bool(true)

4.4. La Factory : Centraliser la Création d'Objets

Le pattern Factory (ou "Fabrique") propose de déléguer la création d'objets à une classe ou une méthode dédiée. Son but est de **découpler** le code client (qui a besoin d'un objet) de la logique de création de cet objet. C'est extrêmement puissant : si la manière de créer un objet doit changer, on ne modifie que la factory, sans toucher au reste de l'application.

Ce pattern est souvent utilisé pour instancier des classes différentes en fonction d'un paramètre, comme nous le verrons dans l'exercice sur les produits.

<?php interface Logger { public function log(string $message); } class FileLogger implements Logger { /* ... */ } class DatabaseLogger implements Logger { /* ... */ } class LoggerFactory { public static function createLogger(string $type): Logger { return match ($type) { 'file' => new FileLogger(), 'database' => new DatabaseLogger(), default => throw new Exception('Type de logger non supporté'), }; } } // Le code client demande un logger sans savoir comment il est construit. $log_type = $_ENV['LOG_TYPE'] ?? 'file'; // 'file' ou 'database' $logger = LoggerFactory::createLogger($log_type); $logger->log("Ceci est un test.");

Passons à la Pratique : Le Code Source

La théorie est essentielle, mais la POO ne prend réellement vie qu'à travers le code. Pour accompagner ce cours, un dépôt GitHub a été préparé. Vous y trouverez des exemples concrets et commentés pour chaque concept que nous venons d'aborder.

Le dépôt est structuré en dossiers numérotés, suivant la progression du cours :

  • Des bases (Classe, Constructeur, Encapsulation) aux piliers de la POO (Héritage, Polymorphisme).
  • Jusqu'aux concepts avancés qui structurent les applications modernes (Classes Abstraites, Interfaces, Traits).
  • Et enfin, une introduction aux Design Patterns fondamentaux comme le Singleton et la Factory.

N'hésitez pas à télécharger le code, à l'exécuter et surtout, à le modifier pour expérimenter. C'est le meilleur moyen de consolider vos connaissances.

Exercice d'Application : Synthèse des Concepts de la POO

Objectif : Mettre en pratique l'ensemble des concepts avancés du chapitre 4 (Traits, Interfaces, Classes Abstraites, Héritage, Polymorphisme) en construisant un petit système de gestion de produits.

Contexte du Scénario

Vous devez modéliser un système de commande simple. Une commande peut contenir différents types de produits. Certains produits sont physiques (comme un livre) et ont un poids, d'autres sont numériques (comme un e-book) et ont une URL de téléchargement. Tous les produits et commandes doivent avoir une date de création. De plus, les commandes doivent pouvoir être exportées en format simplifié (CSV).

Instructions Détaillées

  1. Le Trait `Timestampable` :
    Créez un trait nommé `Timestampable`. Il doit contenir une propriété `protected \DateTime $createdAt` et un constructeur qui initialise cette propriété à la date et heure actuelles (`new \DateTime()`). Ce trait servira à horodater nos objets.
  2. L'Interface `Exportable` :
    Créez une interface nommée `Exportable`. Elle doit définir un contrat avec une seule méthode publique : `exporterLigneCSV(): string`. Toute classe implémentant cette interface devra savoir comment se représenter sur une seule ligne au format CSV.
  3. La Classe Abstraite `Produit` :
    Créez une classe `abstract class Produit`.
    • Elle doit utiliser le trait `Timestampable`.
    • Elle doit avoir des propriétés `protected` communes : `nom` et `prix`.
    • Elle doit avoir un constructeur pour initialiser `nom` et `prix`.
    • Elle doit déclarer une méthode `abstract public function getDetailsProduit(): string;` pour forcer les classes enfants à implémenter leur propre logique d'affichage de détails.
  4. Les Classes Enfants (Héritage et Polymorphisme) :
    - Créez une classe `class ProduitPhysique extends Produit`. Ajoutez une propriété `poids_kg`. Implémentez la méthode `getDetailsProduit()` pour qu'elle retourne une chaîne incluant le nom, le prix et le poids.
    - Créez une classe `class ProduitNumerique extends Produit`. Ajoutez une propriété `url_telechargement`. Implémentez la méthode `getDetailsProduit()` pour qu'elle retourne le nom, le prix et l'URL.
  5. La Classe `Commande` :
    Créez la classe `Commande`.
    • Elle doit utiliser le trait `Timestampable`.
    • Elle doit implémenter l'interface `Exportable`.
    • Elle doit avoir une propriété `numero` et un tableau `produits`.
    • La méthode `ajouterProduit(Produit $produit)` permettra d'ajouter n'importe quel type de produit (physique ou numérique) à la commande.
    • La méthode `exporterLigneCSV()` retournera une chaîne simple, par exemple : `"NumeroCommande;DateCreation;NombreProduits"`.
  6. Mise en Pratique (`Test.php`) :
    Créez un fichier pour tester votre logique. Il devrait ressembler à ceci :
    <?php // Inclure toutes vos classes ici... // 1. Créer différents types de produits $livre = new ProduitPhysique('Livre POO en PHP', 25.50, 0.8); $ebook = new ProduitNumerique('PDF du cours', 9.99, 'http://lien.telechargement/cours.pdf'); // 2. Créer une commande $commande = new Commande('CMD-2025-001'); $commande->ajouterProduit($livre); $commande->ajouterProduit($ebook); // 3. Démontrer le polymorphisme echo "Détails des produits dans la commande :\n"; foreach ($commande->produits as $produit) { // Appelle la bonne version de getDetailsProduit() selon l'objet echo "- " . $produit->getDetailsProduit() . "\n"; } // 4. Démontrer l'interface echo "\nExport CSV de la commande :\n"; echo $commande->exporterLigneCSV();


La solution existe dans le fichier /13_Exercice/solution.zip du Repository des exemple de la section précedente avec le mot de passe ousrah.

Pour l'extraire vous devez installez le logiciel 7zip sur le lien https://www.7-zip.org/

Cliquez avec le bouton droit sur le fichier solution.zip, cliquez sur Afficher autres options/7zip/Extraire vers, puis introduisez le mot de passe ousrah et décompressez le dossier

Partie 3 : L'Écosystème Professionnel

Chapitre 5 : L'Écosystème Professionnel

5.1. Composer : Le Chef d'Orchestre de l'Écosystème

Avant Composer, gérer des bibliothèques externes était un processus manuel, fastidieux et source d'erreurs ("l'enfer des dépendances"). Cet outil a tout changé.

**Composer** est un **gestionnaire de dépendances** pour PHP. Il permet aux développeurs de déclarer, d'installer et de gérer les bibliothèques (ou "paquets") externes dont un projet a besoin. Grâce à lui, il n'est plus nécessaire de réinventer la roue ; on peut s'appuyer sur des milliers de composants open-source robustes disponibles sur le dépôt public **Packagist.org**.

Installation

L'installation de Composer varie légèrement selon le système d'exploitation, mais le principe reste le même : le rendre accessible globalement via la ligne de commande.

Sur Linux / macOS (ou un serveur distant via SSH)

L'installation se fait en téléchargeant un script d'installation, en vérifiant son intégrité, puis en déplaçant l'exécutable dans un répertoire système.

# 1. Télécharger l'installeur php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" # 2. Vérifier la signature (SHA-384) pour la sécurité php -r "if (hash_file(...) === '...signature...') { ... }" # 3. Exécuter l'installation php composer-setup.php # 4. Rendre Composer accessible globalement (exemple) sudo mv composer.phar /usr/local/bin/composer

Sur Windows

L'approche est plus simple grâce à un installeur graphique (`Composer-Setup.exe`) disponible sur le site officiel. Il suffit de le télécharger et de suivre les étapes, en s'assurant de pointer vers l'exécutable `php.exe` de votre environnement local (comme XAMPP, Wamp, etc.). L'installeur se charge d'ajouter automatiquement `composer` à la variable d'environnement PATH.

Utilisation et Concepts Clés

L'utilisation de Composer s'articule autour de quelques fichiers et commandes fondamentaux.

1. Le manifeste `composer.json`

Ce fichier est le cœur de votre projet. Il déclare les métadonnées et, surtout, les dépendances requises. On le crée généralement avec la commande `composer init` ou en ajoutant des paquets avec `composer require`.

{ "name": "votre-nom/mon-projet", "require": { "php": "^8.1", "phpunit/php-timer": "^5.0" }, "autoload": { "psr-4": { "App\\": "src/" } } }

2. Le dossier `vendor` et l'autoloading

Lorsque vous exécutez `composer install`, Composer télécharge les paquets listés dans `composer.json` (et leurs propres dépendances) dans un dossier `vendor/`. Il génère également un fichier magique : `vendor/autoload.php`. En incluant ce seul fichier dans votre script, toutes les classes de toutes les bibliothèques installées deviennent disponibles instantanément.

<?php // Inclure cette ligne unique au début de votre application require __DIR__ . '/vendor/autoload.php'; // Vous pouvez maintenant utiliser les classes de vos dépendances Timer::start(); // ... votre code ... $time = Timer::stop();

3. Le fichier `composer.lock`

Ce fichier enregistre les **versions exactes** de chaque paquet installé. Il garantit que chaque développeur de l'équipe, ainsi que le serveur de production, utilise précisément la même version des dépendances, évitant ainsi les problèmes de compatibilité.

4. La mise à jour des dépendances

Pour mettre à jour les bibliothèques vers leurs dernières versions compatibles (selon les contraintes définies dans `composer.json`), on utilise la commande `composer update`.



Chapitre 6 : Les Standards (PSR) : Parler le même langage

Les **PHP Standard Recommendations (PSR)** sont des spécifications proposées par le groupe PHP-FIG (Framework Interoperability Group) dans le but d'harmoniser les pratiques de développement. Adopter ces standards n'est pas obligatoire, mais c'est une condition essentielle pour garantir l'interopérabilité entre les différents composants et frameworks de l'écosystème.

PSR-1 : Les standards de base

Cette norme établit les conventions minimales pour assurer une interopérabilité technique. Elle couvre des points fondamentaux comme l'encodage des fichiers, la déclaration des symboles et le nommage.

  • Les fichiers doivent utiliser uniquement les balises `<?php` et `<?=`.
  • L'encodage des fichiers PHP doit être **UTF-8 sans BOM - Unicode Transformation Format-8(Octet) withoutB yte Order Mark.
  • Les noms de classe doivent être en `PascalCase`.
  • Les constantes de classe doivent être en `UPPER_CASE_WITH_UNDERSCORES` ou 'SNAKE_CASE'.
  • Les noms de méthode doivent être en `camelCase`.
<?php namespace Vendor\Model; class Foo { // Constantes en majuscules avec underscores const VERSION = '1.0'; const DATE_APPROVED = '2012-06-01'; }

PSR-12 : Guide de style de code étendu

Remplaçant la PSR-2 (aujourd'hui dépréciée), la PSR-12 va plus loin en définissant un guide de style complet pour le formatage du code. Son objectif est de réduire la friction cognitive lors de la lecture de code provenant de différents auteurs.

  • L'indentation doit être de **4 espaces**, pas de tabulations.
  • La longueur des lignes est "soft-limitée" à 120 caractères.
  • L'accolade ouvrante d'une classe doit être sur sa propre ligne.
  • L'accolade ouvrante d'une méthode doit être sur la même ligne que sa déclaration.
  • Les mots-clés réservés de PHP (comme `public`, `function`, `use`) doivent être en minuscules.
<?php namespace Vendor\Package; use FooClass; use BarClass as Bar; use OtherVendor\OtherPackage\BazClass; // L'accolade de la classe est sur sa propre ligne class ClassName extends ParentClass implements \ArrayAccess, \Countable { // ... propriétés, constantes, méthodes ... }

PSR-4 : Le chargement automatique de classes

Cette spécification est le moteur de l'architecture moderne en PHP. Elle décrit comment faire correspondre un **espace de noms (`namespace`)** à un **chemin de fichier** sur le disque. En respectant cette convention, des outils comme Composer peuvent charger automatiquement n'importe quelle classe au moment où elle est utilisée, nous libérant de la gestion manuelle des `require`.

// Structure d'un nom de classe complet ("Fully Qualified Class Name") : \VendorName\PackageName\SubNamespace\ClassName // Exemple de mapping dans composer.json : "autoload": { "psr-4": { // Le namespace "Acme\Log" correspond au dossier "src/" "Acme\\Log\\": "src/" } } // Conséquence : // La classe Acme\Log\Writer\FileLog est automatiquement cherchée dans le fichier : // /chemin/du/projet/src/Writer/FileLog.php

Interfaces Communes (PSR-3, PSR-7, etc.)

Un grand nombre de PSR définissent des **interfaces communes** pour des fonctionnalités essentielles. L'idée est simple : si ma bibliothèque de logging implémente l'interface LoggerInterface de la PSR-3, et que votre framework attend un objet de ce type, alors ma bibliothèque est instantanément compatible avec votre framework. Cela favorise la réutilisation et le découplage du code.

Le débat de la PSR-7 : L'Immutabilité

Toutes les PSR n'ont pas fait l'unanimité. La **PSR-7**, qui standardise les objets de requête et de réponse HTTP, a suscité un vif débat. Elle impose que ces objets soient **immutables** (non modifiables après création), alors que des composants majeurs de l'écosystème, comme `HttpFoundation` de Symfony, sont fondamentalement **mutables**.

Ce conflit philosophique illustre la complexité de standardiser un écosystème aussi vaste que celui de PHP. Cela montre que les PSR sont des recommandations puissantes, mais qui doivent parfois composer avec l'existant.

Comment une recommandation devient-elle un standard ?

Chaque PSR suit un cycle de vie strict pour garantir sa pertinence et son adoption par la communauté. Ce processus assure que seules les idées mûrement réfléchies et testées deviennent des standards officiels.

  1. Avant-projet : La proposition est discutée pour évaluer l'intérêt général.
  2. Brouillon : Le concept est affiné et détaillé pour être soumis à un examen.
  3. Revue : La phase d'expérimentation. La communauté teste la proposition et fournit des retours.
  4. Acceptée : La proposition devient officiellement une PSR.
  5. Dépréciée ou Abandonnée : Si une PSR devient obsolète (comme la PSR-2 remplacée par la PSR-12) ou n'est pas adoptée, elle peut être officiellement retirée.

Partie 4 : Architecture d'une Application Robuste

Chapitre 7 : Le Pattern MVC, la Séparation des Préoccupations

Le design pattern **Modèle-Vue-Contrôleur (MVC)** est le pilier des frameworks web. Il organise le code en séparant les responsabilités : la logique métier (Modèle), l'affichage (Vue) et la gestion des requêtes (Contrôleur).

Cycle de Vie d'une Requête MVC

1. Contrôleur

Le chef d'orchestre. Reçoit la requête et coordonne.

2. Modèle

Le cerveau. Gère la logique métier et les données.

3. Vue

La vitrine. Génère le HTML final.

Survolez un composant pour découvrir son rôle détaillé.



Chapitre 8 : Le CRUD sécurisé avec PDO

Interagir avec une base de données est une source majeure de vulnérabilités. L'extension **PHP Data Objects (PDO)** offre une interface unifiée et sécurisée pour cette tâche. Son atout principal est la prise en charge native des **requêtes préparées**, votre meilleure défense contre les injections SQL. Mais avant tout, PDO nous oblige à gérer proprement les erreurs grâce au mécanisme des **exceptions**.

8.1. Le Principe Fondamental : La Gestion des Exceptions

Plutôt que de laisser une erreur provoquer un "fatal error" et arrêter brutalement le script, PHP propose un mécanisme élégant pour gérer les situations exceptionnelles : les exceptions. Une exception est un objet qui signale qu'une erreur s'est produite. Cela nous permet d'isoler le code "à risque" et de définir un plan B en cas de problème.

Ce système repose sur trois mots-clés :

  • try : On enveloppe le code susceptible de lancer une exception dans ce bloc.
  • catch : Si une exception est lancée dans le bloc `try`, l'exécution est stoppée et passe immédiatement à ce bloc, qui "attrape" l'exception pour la traiter proprement (afficher un message, logger l'erreur, etc.).
  • throw : Permet de lancer manuellement une exception.
<?php function diviser(int $a, int $b): float { if ($b === 0) { // On lance une exception car la situation est anormale et bloquante. throw new Exception("Division par zéro impossible !"); } return $a / $b; } try { // On "essaie" d'exécuter le code à risque. $resultat = diviser(10, 0); echo $resultat; // Cette ligne ne sera jamais atteinte. } catch (Exception $e) { // Si une exception est attrapée, on exécute ce bloc. echo "Une erreur est survenue : " . $e->getMessage(); } // Affiche "Une erreur est survenue : Division par zéro impossible !"

8.2. Connexion Robuste à la Base de Données

Armés de cette connaissance, nous pouvons maintenant comprendre la connexion PDO. Nous la plaçons dans un bloc `try` car l'instanciation de PDO peut échouer. En configurant le mode d'erreur sur `PDO::ERRMODE_EXCEPTION`, nous demandons à PDO de lancer une `PDOException` pour toute erreur SQL, que nous pouvons ensuite `catch`.

$dsn = 'mysql:host=127.0.0.1;dbname=ma_bdd;charset=utf8mb4'; $user = 'root'; $password = 'votre_mot_de_passe'; try { $pdo = new PDO($dsn, $user, $password, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // Ligne cruciale PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC // Pratique ]); } catch (PDOException $e) { // En production : logguer l'erreur et afficher un message générique. die("Erreur de connexion à la base de données."); }

8.3. Create : Insertion avec Requête Préparée

Pour insérer des données provenant d'une source externe (formulaire, API), utilisez des marqueurs (:email). Les données sont ensuite fournies dans la méthode execute(). PDO s'assure que les données sont traitées comme des valeurs et non comme du code SQL, bloquant ainsi les injections.

$sql = "INSERT INTO users (name, email) VALUES (:name, :email)"; $statement = $pdo->prepare($sql); $statement->execute([ 'name' => $_POST['name'], 'email' => $_POST['email'] ]);

8.4. Read : Lecture Sécurisée avec Paramètres

Même pour lire des données, si la requête dépend d'une entrée utilisateur (ex: un ID dans l'URL), la préparation est obligatoire pour protéger la clause WHERE.

$userId = (int) $_GET['id'] ?? 0; $sql = "SELECT id, name, email FROM users WHERE id = ?"; $statement = $pdo->prepare($sql); $statement->execute([$userId]); $user = $statement->fetch(); // Récupère une seule ligne

8.5. Update : Mise à Jour Blindée

La mise à jour combine les risques : les données à modifier (clause SET) et le critère de sélection (clause WHERE) doivent tous deux être passés via des paramètres liés.

$sql = "UPDATE users SET name = :name WHERE id = :id"; $statement = $pdo->prepare($sql); $statement->execute([ 'name' => $_POST['newName'], 'id' => $_POST['userId'] ]);

8.6. Delete : Suppression Contrôlée

Même une opération simple comme la suppression doit être sécurisée. Ne concaténez jamais un ID provenant de $_POST ou $_GET directement dans votre chaîne SQL.

$userIdToDelete = $_POST['id']; $sql = "DELETE FROM users WHERE id = ?"; $statement = $pdo->prepare($sql); $statement->execute([$userIdToDelete]);

Partie 5 : De la pratique

Chapitre 9 : Gestion des clients et des fournisseurs

Partie Pratique : Notre Projet Fil Rouge

Pour mettre en application tous les concepts théoriques vus précédemment, nous allons construire ensemble une application complète de gestion. Ce projet, baptisé "ClientManager", évoluera au fil du cours, partant d'une base très simple pour aboutir à une application robuste et moderne.

Environnement de Travail Requis

Pour suivre la partie pratique dans de bonnes conditions, il est essentiel de configurer un environnement de développement local. Voici les outils que nous utiliserons :

Logo Laragon Logo XAMPP Logo Wamp

Serveur Local

Fournit une pile complète (Apache, MySQL, PHP). Laragon est recommandé pour sa simplicité, mais XAMPP ou WAMP sont d'excellentes alternatives.

Logo PHP

PHP 8.1+

Le projet est développé avec la syntaxe moderne de PHP. Assurez-vous que la version en ligne de commande est 8.1 ou supérieure (`php -v`).

Logo Composer

Composer

Le gestionnaire de dépendances indispensable. À installer globalement sur votre système.

Logo MySQL Logo MariaDB

SGBD

Notre base de données (MySQL/MariaDB), incluse avec Laragon/XAMPP. Un outil comme HeidiSQL est recommandé.

Logo Git

Git

Essentiel pour la gestion de versions et pour télécharger le code source du projet depuis GitHub. Assurez-vous qu'il est bien installé sur votre machine.

Logo Git

VS Code

Editeur de texte multifonctions pour l'ecriture du code/

Objectif et Versions du Projet

Le projet est disponible sur GitHub en plusieurs versions, correspondant à différentes étapes de notre apprentissage :

  • Version Zero : Ancienne methode de programmatin php avec la version 5.6
  • Version de Base : Se concentre sur la structure (Contrôleur, Repository, Entité), l'autoloader et une gestion simple des clients sans POO avancée.
  • Version Intermédiaire : Amélioration du routage.
  • Version Finale :Ajout des fonctionnalités d'héritage , de polymorphisme et d'encapsulation. Implémente une interface dynamique avec une layout avancée et une recherche en AJAX pour toutes les opérations CRUD, offrant une expérience utilisateur professionnelle.

Cette approche vous permettra de voir concrètement comment et pourquoi chaque concept (POO, Composer, MVC...) contribue à améliorer la qualité et la maintenabilité du code.

Chapitre 10 : Exercice: Gestion des produtis

Exercice Pratique : Gestion des Produits

Il est temps de mettre vos compétences à l'épreuve ! Votre mission est d'étendre l'application finale en y ajoutant la gestion des **Produits**. L'objectif principal de cet exercice est de mettre en pratique l'**héritage**.

Contexte

Un produit peut être de deux types : un **vêtement** ou un **appareil électroménager**. Chaque type de produit partage des caractéristiques communes (nom, description) mais possède aussi des attributs spécifiques.

1. Structure de la Table `produits`

Commencez par créer la table `produits` dans votre base de données. La colonne `type` est cruciale, elle nous servira à différencier les types de produits (c'est un *discriminator column*).

CREATE TABLE produits ( id INT AUTO_INCREMENT PRIMARY KEY, nom VARCHAR(255) NOT NULL, description TEXT, prix DECIMAL(10, 2) NOT NULL, -- Colonne pour différencier les types de produits -- type VARCHAR(50) NOT NULL, -- 'vetement' ou 'electromenager' -- Attributs spécifiques aux vêtements -- taille VARCHAR(10) NULL, couleur VARCHAR(50) NULL, -- Attributs spécifiques à l'électroménager -- marque VARCHAR(100) NULL, garantie_mois INT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );

2. Pistes à Suivre (Étapes Recommandées)

  1. Création des Entités (Héritage) :
    - Créez une classe `abstract class Produit` avec les propriétés communes (`id`, `nom`, `description`, `prix`, `type`).
    - Créez une classe `class Vetement extends Produit` avec ses propriétés spécifiques (`taille`, `couleur`).
    - Créez une classe `class Electromenager extends Produit` avec ses propriétés (`marque`, `garantie_mois`).
  2. Création du Repository :
    - Créez un `ProduitRepository`. Dans la méthode `findAll()`, vous devrez lire la colonne `type` de chaque ligne pour décider quelle classe instancier (`new Vetement(...)` ou `new Electromenager(...)`).
  3. Création du Contrôleur :
    - Créez un `ProduitController.php` sur le modèle de `ClientController.php`, avec les méthodes `index`, `store`, `update`, `destroy` et `showForm`.
  4. Création des Vues :
    - Créez la vue `views/produits/list.php`.
    - Créez la vue partielle `views/partials/_produit_form.php`. Ce formulaire devra afficher dynamiquement les champs spécifiques en fonction du type de produit.
  5. Mise à jour des Routes :
    - Ajoutez les nouvelles routes pour les produits dans `routes/web.php`.

3. Mise à Jour de la Navigation

Pour finir, ajoutez un lien vers votre nouvelle page de gestion des produits dans la barre de navigation latérale. Modifiez le fichier `views/layout.php` :

<nav> <ul> <li class="mb-2"><a href="<?= $_ENV['APP_BASEPATH'] ?>/" class="...">Polymorphisme</a></li> <li class="mb-2"><a href="<?= $_ENV['APP_BASEPATH'] ?>/clients" class="...">Clients</a></li> <li class="mb-2"><a href="<?= $_ENV['APP_BASEPATH'] ?>/fournisseurs" class="...">Fournisseurs</a></li> <!-- AJOUTEZ CETTE LIGNE --> <li class="mb-2"><a href="<?= $_ENV['APP_BASEPATH'] ?>/produits" class="block p-2 rounded hover:bg-gray-700">Produits</a></li> </ul> </nav>

Conclusion & Vers Laravel

Félicitations ! Vous avez exploré les piliers du développement PHP moderne.

Laravel n'est plus une "boîte noire". C'est l'application élégante et puissante des concepts que nous avons vus ensemble : l'évolution vers un **langage mature**, la puissance de la **Programmation Orientée Objet**, la cohérence des **PSR**, la gestion des dépendances avec **Composer**, la structure du **MVC** et la sécurité de **PDO**. Ces fondations solides vous permettront d'être productif avec Laravel et d'écrire un code de qualité professionnelle.

Explorer la Documentation Laravel