Semaine 4 - Jour 2 : POO Avancée

Aujourd'hui, nous explorons les concepts avancés de la Programmation Orientée Objet en PHP : l'héritage, la visibilité, les méthodes statiques, les constantes, les interfaces et les traits.

1. Héritage avec extends

Créer une hiérarchie de classes

<?php // Classe parente (classe de base) class Personne { protected $nom; protected $prenom; protected $age; public function __construct($nom, $prenom, $age) { $this->nom = $nom; $this->prenom = $prenom; $this->age = $age; } public function sePresenter() { return "Je m'appelle $this->prenom $this->nom et j'ai $this->age ans."; } } // Classe enfant (hérite de Personne) class Etudiant extends Personne { private $ecole; private $niveau; public function __construct($nom, $prenom, $age, $ecole, $niveau) { // Appeler le constructeur parent parent::__construct($nom, $prenom, $age); $this->ecole = $ecole; $this->niveau = $niveau; } // Méthode spécifique à l'étudiant public function etudier() { return "$this->prenom étudie en $this->niveau à $this->ecole."; } // Redéfinir (override) une méthode du parent public function sePresenter() { return parent::sePresenter() . " Je suis étudiant en $this->niveau."; } } // Utilisation $personne = new Personne("Dupont", "Jean", 45); $etudiant = new Etudiant("Martin", "Marie", 20, "Sorbonne", "Master 2"); echo $personne->sePresenter() . "<br>"; echo $etudiant->sePresenter() . "<br>"; echo $etudiant->etudier(); ?>

Résultat :

Je m'appelle Jean Dupont et j'ai 45 ans.
Je m'appelle Marie Martin et j'ai 20 ans. Je suis étudiant en Master 2.
Marie étudie en Master 2 à Sorbonne.

Concepts clés de l'héritage :

  • extends : Hériter d'une classe parente
  • parent:: : Appeler une méthode de la classe parente
  • override : Redéfinir une méthode du parent dans l'enfant
  • La classe enfant hérite de TOUTES les propriétés et méthodes publiques/protected du parent

2. Visibilité : public, private, protected

Contrôler l'accès aux propriétés et méthodes

Visibilité Accessible depuis la classe Accessible depuis une classe enfant Accessible depuis l'extérieur
public Oui Oui Oui
protected Oui Oui Non
private Oui Non Non

Exemple pratique

<?php class CompteBancaire { public $titulaire; // Accessible partout protected $numero; // Accessible dans cette classe et ses enfants private $solde; // Accessible uniquement dans cette classe public function __construct($titulaire, $numero, $soldeInitial) { $this->titulaire = $titulaire; $this->numero = $numero; $this->solde = $soldeInitial; } // Méthode publique pour accéder au solde privé public function getSolde() { return $this->solde; } // Méthode publique pour modifier le solde de manière contrôlée public function deposer($montant) { if ($montant > 0) { $this->solde += $montant; return "Dépôt de $montant€ effectué. Nouveau solde : " . $this->solde . "€"; } return "Montant invalide."; } public function retirer($montant) { if ($montant > 0 && $montant <= $this->solde) { $this->solde -= $montant; return "Retrait de $montant€ effectué. Nouveau solde : " . $this->solde . "€"; } return "Solde insuffisant ou montant invalide."; } // Méthode privée (utilisable uniquement dans cette classe) private function verifierSolde() { return $this->solde >= 0; } } $compte = new CompteBancaire("Jean Dupont", "FR123456", 1000); echo $compte->titulaire . "<br>"; // ✅ OK : public echo $compte->getSolde() . "<br>"; // ✅ OK : via méthode publique echo $compte->deposer(500) . "<br>"; // ✅ OK // ❌ ERREUR : $compte->solde (private, pas accessible depuis l'extérieur) // ❌ ERREUR : $compte->numero (protected, pas accessible depuis l'extérieur) // ❌ ERREUR : $compte->verifierSolde() (private, pas accessible) ?>

Pourquoi utiliser private/protected ?

  • Encapsulation : Protéger les données sensibles (solde bancaire)
  • Contrôle : Valider les données avant modification
  • Maintenance : Changer l'implémentation interne sans casser le code externe
  • Sécurité : Empêcher la modification directe de propriétés critiques

3. Méthodes et propriétés static

Membres partagés entre toutes les instances

<?php class Utilisateur { public $nom; private static $compteur = 0; // Partagé par TOUS les objets public function __construct($nom) { $this->nom = $nom; self::$compteur++; // self:: pour accéder aux membres static } // Méthode static (appelable sans créer d'objet) public static function getNombreUtilisateurs() { return self::$compteur; } // Méthode static utilitaire public static function validerEmail($email) { return filter_var($email, FILTER_VALIDATE_EMAIL) !== false; } } // Appeler une méthode static SANS créer d'objet echo "Utilisateurs au départ : " . Utilisateur::getNombreUtilisateurs() . "<br>"; // Créer des objets $user1 = new Utilisateur("Jean"); $user2 = new Utilisateur("Marie"); $user3 = new Utilisateur("Paul"); echo "Utilisateurs créés : " . Utilisateur::getNombreUtilisateurs() . "<br>"; // Utiliser une méthode static utilitaire if (Utilisateur::validerEmail("test@example.com")) { echo "Email valide !"; } ?>

Résultat :

Utilisateurs au départ : 0
Utilisateurs créés : 3
Email valide !

Quand utiliser static ?

  • Compteurs : Compter les instances créées
  • Utilitaires : Fonctions qui ne dépendent pas d'un objet (validation, formatage)
  • Configuration : Stocker des valeurs partagées
  • Factory methods : Créer des objets de manière spécialisée

4. Constantes de classe

Valeurs immuables au niveau de la classe

<?php class Configuration { // Constantes (toujours en MAJUSCULES) const VERSION = "1.0.0"; const NOM_APP = "MonSuperSite"; const MAX_UPLOAD_SIZE = 5242880; // 5 Mo en octets public static function afficherInfo() { echo "Application : " . self::NOM_APP . "<br>"; echo "Version : " . self::VERSION . "<br>"; echo "Taille max upload : " . (self::MAX_UPLOAD_SIZE / 1024 / 1024) . " Mo"; } } // Accéder aux constantes depuis l'extérieur echo Configuration::VERSION . "<br>"; echo Configuration::NOM_APP . "<br>"; Configuration::afficherInfo(); ?>

Exemple : Statuts d'utilisateur

<?php class Utilisateur { // Constantes pour les statuts const STATUT_ACTIF = 1; const STATUT_INACTIF = 0; const STATUT_SUSPENDU = 2; // Constantes pour les rôles const ROLE_USER = "user"; const ROLE_ADMIN = "admin"; const ROLE_MODERATEUR = "moderateur"; private $nom; private $statut; private $role; public function __construct($nom, $role = self::ROLE_USER) { $this->nom = $nom; $this->statut = self::STATUT_ACTIF; $this->role = $role; } public function estActif() { return $this->statut === self::STATUT_ACTIF; } public function estAdmin() { return $this->role === self::ROLE_ADMIN; } } // Utilisation claire et lisible $user = new Utilisateur("Jean", Utilisateur::ROLE_ADMIN); if ($user->estAdmin()) { echo "Accès administrateur accordé."; } ?>

5. Interfaces

Définir un contrat

<?php // Interface = Contrat que les classes doivent respecter interface Affichable { public function afficher(); public function afficherResume(); } // Classe qui implémente l'interface class Article implements Affichable { private $titre; private $contenu; public function __construct($titre, $contenu) { $this->titre = $titre; $this->contenu = $contenu; } // DOIT implémenter toutes les méthodes de l'interface public function afficher() { return "<h2>$this->titre</h2><p>$this->contenu</p>"; } public function afficherResume() { return "$this->titre : " . substr($this->contenu, 0, 50) . "..."; } } class Produit implements Affichable { private $nom; private $prix; public function __construct($nom, $prix) { $this->nom = $nom; $this->prix = $prix; } public function afficher() { return "<h3>$this->nom</h3><p>Prix : $this->prix €</p>"; } public function afficherResume() { return "$this->nom ($this->prix €)"; } } // Fonction qui accepte n'importe quel objet Affichable function afficherElement(Affichable $element) { echo $element->afficher(); } $article = new Article("PHP POO", "Apprendre la programmation orientée objet..."); $produit = new Produit("Laptop", 999.99); afficherElement($article); afficherElement($produit); ?>

Interfaces vs Classes abstraites :

  • Interface : Définit QUOI faire (aucune implémentation)
  • Classe abstraite : Définit QUOI et peut définir COMMENT (peut avoir des méthodes implémentées)
  • Une classe peut implémenter PLUSIEURS interfaces, mais hériter d'UNE SEULE classe

6. Traits

Réutiliser du code entre classes

<?php // Trait = Morceau de code réutilisable trait Horodatage { private $dateCreation; private $dateModification; public function setDateCreation() { $this->dateCreation = date("Y-m-d H:i:s"); } public function setDateModification() { $this->dateModification = date("Y-m-d H:i:s"); } public function getDateCreation() { return $this->dateCreation; } } trait Identifiable { private $id; public function setId($id) { $this->id = $id; } public function getId() { return $this->id; } } // Utiliser plusieurs traits class Article { use Horodatage, Identifiable; private $titre; private $contenu; public function __construct($id, $titre, $contenu) { $this->setId($id); $this->titre = $titre; $this->contenu = $contenu; $this->setDateCreation(); } } class Utilisateur { use Horodatage, Identifiable; private $nom; public function __construct($id, $nom) { $this->setId($id); $this->nom = $nom; $this->setDateCreation(); } } $article = new Article(1, "Mon article", "Contenu..."); echo "Article ID : " . $article->getId() . "<br>"; echo "Créé le : " . $article->getDateCreation(); ?>

Quand utiliser les traits ?

  • Partager du code entre classes sans héritage
  • Éviter la duplication de code
  • Ajouter des fonctionnalités transversales (logs, timestamps...)
  • Alternative à l'héritage multiple (PHP ne supporte pas l'héritage multiple)
Froggiesplaining :


Objectifs de ce cours :
✅ Maîtriser l'héritage avec extends et parent::
✅ Comprendre les modificateurs de visibilité (public, protected, private)
✅ Utiliser les membres static et les constantes
✅ Implémenter des interfaces pour définir des contrats
✅ Utiliser les traits pour partager du code entre classes

Points clés à retenir :
Héritage = Etudiant extends Personne (grenouille savante qui hérite des capacités de base)
public = Accessible partout (ma couleur verte visible par tous)
protected = Accessible dans la classe et ses enfants (mon ADN familial)
private = Accessible uniquement dans la classe (mes pensées secrètes)
static = Partagé par toutes les instances (compteur de grenouilles dans la mare)
const = Valeurs immuables (COULEUR_DEFAUT = "vert")
Interface = Contrat définissant QUOI faire (Nageable impose nager())
Trait = Code réutilisable sans héritage (lunettes portables par tous)

Exercice pratique :
1. Créer une classe CompteBancaire avec propriétés private (solde, titulaire)
2. Ajouter des méthodes public pour accéder/modifier le solde de façon contrôlée
3. Créer une classe CompteEpargne qui extends CompteBancaire
4. Ajouter une constante TAUX_INTERET et une méthode static calculerInterets()
5. Créer une interface Transferable avec méthode transferer()
6. Implémenter l'interface dans CompteBancaire
7. Conseil de Froggie: Utilise private pour protéger les données sensibles comme mon solde bancaire de mouches !

Froggie explain

GitHub - eCrea