Module 2Programmer en PHP
Chapitre 6 sur 11
Utiliser l'orienté objet en PHP
Classes, encapsulation, héritage, interfaces, méthodes magiques, espaces de noms et autoload.
- ≈ 165 min
- 7 leçons
- 3 exercices
- TP
Module 2Programmer en PHP
Chapitre 6 sur 11
Classes, encapsulation, héritage, interfaces, méthodes magiques, espaces de noms et autoload.
Objectifs
Avec ce chapitre, tu sauras…
$this, new ;public, private, protected, accesseurs ;extends, parent::), final, readonly, static ;__construct, __destruct, __get, __set, __toString, __call…Leçon 1
| Programmation procédurale | Programmation orientée objet |
|---|---|
| le programme est découpé en fonctions | le programme est découpé en objets |
| les données sont séparées des fonctions, souvent globales | chaque objet regroupe ses données (propriétés) et ses fonctions (méthodes) |
| les opérations s'exécutent dans l'ordre du script | les objets collaborent en s'envoyant des messages |
| simple pour un petit script | indispensable pour une application qui grandit |
Leçon 2
| Notion | Écriture PHP |
|---|---|
| déclarer une classe | class Voiture { … } (nom en PascalCase) |
| créer un objet | $v = new Voiture('Dacia'); |
| accéder à un membre | $v->marque, $v->accelerer(50) |
| l'objet courant | $this (dans une méthode) |
| constructeur | public function __construct(…) |
Leçon 3
| Visibilité | Accessible depuis… |
|---|---|
public | partout |
protected | la classe et ses classes filles |
private | la classe seulement |
Leçon 4
| Mot-clé | Rôle |
|---|---|
extends | déclarer une classe fille (PHP n'accepte qu'une classe mère) |
parent::methode() | appeler la version de la classe mère |
final class / final public function | interdire l'héritage de la classe / la redéfinition de la méthode |
instanceof | tester si un objet est une instance d'une classe (ou d'une de ses filles) |
Leçon 5
| Classe abstraite | Interface | |
|---|---|---|
| mot-clé | abstract class | interface |
| contenu | méthodes abstraites et concrètes, propriétés | signatures de méthodes publiques, constantes |
| utilisation | extends (une seule) | implements (plusieurs possibles : implements A, B) |
Leçon 6
| Méthode | Appelée automatiquement… |
|---|---|
__construct(…) | à la création de l'objet (new) |
__destruct() | quand l'objet n'est plus utilisé (ou à la fin du script) |
__toString() | quand l'objet est utilisé comme une chaîne (echo $objet) |
__get($nom) / __set($nom, $valeur) | à la lecture / l'écriture d'une propriété inaccessible ou inexistante |
__isset($nom) / __unset($nom) |
Leçon 7
1Pour accéder à une propriété d'un objet en PHP :
2Dans une méthode, l'objet courant s'appelle…
3Une propriété protected est accessible…
4Pour appeler le constructeur de la classe mère :
5Une classe peut implémenter…
6echo $objet; appelle automatiquement…
7Pour une propriété partagée par toute la classe :
L'essentiel du chapitre
class, new, ->, $this, __construct (promotion des propriétés en PHP 8), propriétés typées.public, protected, private ; accesseurs ; readonly.extends (une mère), parent::, redéfinition, final, instanceof ; static et const avec self:: et ::.abstract class / abstract function ; interface + implements (plusieurs) ; traits.__construct, __destruct, __toString, __get, __set, __isset, __unset, __call, __invoke, __clone, __serialize.namespace, use, spl_autoload_register ; Composer en entreprise.Les traits
lors de isset() / unset() sur une propriété inaccessible |
__call($nom, $args) / __callStatic(…) | à l'appel d'une méthode inaccessible ou inexistante |
__invoke(…) | quand l'objet est appelé comme une fonction : $objet() |
__clone() | après une copie avec clone |
__serialize() / __unserialize() | lors de serialize() / unserialize() (remplacent __sleep / __wakeup) |
__set_state() | lors de var_export() |
Tu as appris les concepts de la POO dans le module M103 (classes, objets, héritage, encapsulation, polymorphisme, abstraction) avec Python. Ce chapitre les applique en PHP : les idées sont identiques, la syntaxe change.
<?php
class Voiture
{
// propriétés
public string $marque;
public string $couleur;
private int $vitesse = 0;
// constructeur : appelé automatiquement par new
public function __construct(string $marque, string $couleur = 'Blanche')
{
$this->marque = $marque;
$this->couleur = $couleur;
}
// méthodes
public function accelerer(int $kmh): void
{
$this->vitesse = min($this->vitesse + $kmh, 180);
}
public function freiner(): void
{
$this->vitesse = 0;
}
public function getVitesse(): int
{
return $this->vitesse;
}
}
$maVoiture = new Voiture('Dacia', 'Rouge'); // instanciation
$maVoiture->accelerer(50); // -> pour accéder aux membres
echo $maVoiture->marque . ' roule à ' . $maVoiture->getVitesse() . ' km/h'; // Dacia roule à 50 km/hLa promotion des propriétés (PHP 8)
<?php
class Stagiaire
{
public function __construct(
public string $nom,
public string $prenom,
private array $notes = [],
) {} // les propriétés sont déclarées ET affectées
public function ajouterNote(float $note): void
{
if ($note < 0 || $note > 20) {
throw new InvalidArgumentException("Note invalide : $note");
}
$this->notes[] = $note;
}
public function moyenne(): float
{
return $this->notes ? round(array_sum($this->notes) / count($this->notes), 2) : 0.0;
}
}
$s = new Stagiaire('Benali', 'Sara');
$s->ajouterNote(15);
$s->ajouterNote(17.5);
echo $s->moyenne(); // 16.25L'encapsulation masque l'état interne de l'objet : on déclare les propriétés private (ou protected) et on y accède par des accesseurs (getters) et des mutateurs (setters) qui contrôlent les valeurs. Dans la classe Stagiaire, impossible d'ajouter une note de 25 : ajouterNote() le refuse.
<?php
class Compte
{
public readonly string $numero; // readonly (PHP 8.1) : affectable une seule fois
private float $solde = 0;
public function __construct(string $numero)
{
$this->numero = $numero;
}
public function getSolde(): float
{
return $this->solde;
}
public function deposer(float $montant): void
{
if ($montant <= 0) {
throw new InvalidArgumentException('Montant invalide');
}
$this->solde += $montant;
}
}
$c = new Compte('MA001');
$c->deposer(500);
echo $c->getSolde(); // 500
// $c->solde = 1000000; // Error : Cannot access private property
// $c->numero = 'MA002'; // Error : Cannot modify readonly property<?php
class Personne
{
public function __construct(protected string $nom, protected string $prenom) {}
public function presenter(): string
{
return "{$this->prenom} {$this->nom}";
}
}
class Formateur extends Personne // Formateur HÉRITE de Personne
{
public function __construct(string $nom, string $prenom, private string $module)
{
parent::__construct($nom, $prenom); // appeler le constructeur parent
}
public function presenter(): string // redéfinition
{
return parent::presenter() . ", formateur de {$this->module}";
}
}
$f = new Formateur('Tazi', 'Youssef', 'M107');
echo $f->presenter(); // Youssef Tazi, formateur de M107
var_dump($f instanceof Personne); // bool(true)Les membres statiques
<?php
class Stagiaire2
{
public static int $nombre = 0; // partagé par toute la classe
public const ETABLISSEMENT = 'ISTA NTIC'; // constante de classe
public function __construct(public string $nom)
{
self::$nombre++; // self:: pour la classe elle-même
}
public static function compter(): int
{
return self::$nombre;
}
}
new Stagiaire2('Sara');
new Stagiaire2('Ali');
echo Stagiaire2::compter(); // 2 (:: opérateur de résolution de portée)
echo Stagiaire2::ETABLISSEMENT; // ISTA NTIC<?php
interface Imprimable
{
public function imprimer(): string;
}
abstract class Forme implements Imprimable // ne peut pas être instanciée
{
abstract public function surface(): float; // les filles DOIVENT l'implémenter
public function imprimer(): string
{
return static::class . ' : ' . round($this->surface(), 2);
}
}
class Cercle extends Forme
{
public function __construct(private float $rayon) {}
public function surface(): float { return M_PI * $this->rayon ** 2; }
}
class Rectangle extends Forme
{
public function __construct(private float $l, private float $h) {}
public function surface(): float { return $this->l * $this->h; }
}
$formes = [new Cercle(1), new Rectangle(4, 3)];
foreach ($formes as $f) {
echo $f->imprimer() . "\n"; // polymorphisme : Cercle : 3.14, puis Rectangle : 12
}Les méthodes dont le nom commence par deux tirets bas __ sont réservées à PHP : on les appelle méthodes magiques. PHP les appelle automatiquement dans certaines situations. Toutes, sauf __construct, __destruct et __clone, doivent être public.
Dans une vraie application, chaque classe est dans son propre fichier, et les classes sont rangées dans des espaces de noms (namespaces) pour éviter les conflits de noms.
<?php
namespace App\Modele;
class Stagiaire
{
public function __construct(public string $nom) {}
}<?php
spl_autoload_register(function (string $classe) {
// App\Modele\Stagiaire → src/Modele/Stagiaire.php
$chemin = __DIR__ . '/src/' . str_replace(['App\\', '\\'], ['', '/'], $classe) . '.php';
if (is_file($chemin)) {
require $chemin;
}
});
use App\Modele\Stagiaire;
$s = new Stagiaire('Benali'); // le fichier est chargé automatiquement au premier usageEn entreprise, l'autoload est généré par Composer, le gestionnaire de paquets de PHP : composer init, puis la section "autoload": {"psr-4": {"App\\": "src/"}} dans composer.json, et require 'vendor/autoload.php';. C'est aussi Composer qui installe les bibliothèques (composer require …).
Dans ton projet stagiaires, crée src/Modele/Stagiaire.php (espace de noms App\Modele) : propriétés privées id, nom, prénom, e-mail, filière ; constructeur avec promotion ; accesseurs ; un mutateur setEmail() qui valide l'adresse ; une méthode nomComplet() ; __toString(). Crée aussi une classe Promotion qui contient une liste de stagiaires, avec ajouter(), compter(), parFiliere(string $code). Charge-les avec un autoload dans index.php et affiche une promotion de 4 stagiaires.
<?php
class A {
public function __construct(protected int $x = 1) {}
public function valeur(): int { return $this->x; }
public function __toString(): string { return static::class . '(' . $this->valeur() . ')'; }
}
class B extends A {
public function valeur(): int { return parent::valeur() * 10; }
}
$objets = [new A(), new B(2), new A(5)];
foreach ($objets as $o) echo $o, ' ';
echo "\n", count(array_filter($objets, fn($o) => $o instanceof A));Écris une classe abstraite Compte (numéro en readonly, solde protected, méthodes deposer(), retirer() qui lance une exception si le retrait dépasse le solde disponible, méthode abstraite soldeDisponible(), et __toString()), puis CompteCourant (découvert autorisé de 1 000 DH) et CompteEpargne (sans découvert, méthode ajouterInterets(float $taux)). Teste-les.
PHP n'autorise qu'une seule classe mère. Pour partager du code entre classes sans lien d'héritage, on utilise un trait : trait Horodatable { public function maintenant() { … } } puis use Horodatable; dans la classe. Le code du trait est « copié » dans la classe.
<?php
class Produit
{
private array $donnees = [];
public function __construct(private string $nom, private float $prix) {}
public function __toString(): string
{
return sprintf('%s (%.2f DH)', $this->nom, $this->prix);
}
public function __get(string $cle): mixed
{
return $this->donnees[$cle] ?? null;
}
public function __set(string $cle, mixed $valeur): void
{
$this->donnees[$cle] = $valeur;
}
public function __call(string $methode, array $args): mixed
{
if (str_starts_with($methode, 'get')) { // getCouleur() → donnees['couleur']
return $this->donnees[strtolower(substr($methode, 3))] ?? null;
}
throw new BadMethodCallException("Méthode $methode inconnue");
}
public function __destruct()
{
// fermer une ressource, écrire un journal…
}
}
$p = new Produit('Clavier', 249.9);
echo $p; // Clavier (249.90 DH) ← __toString
$p->couleur = 'noir'; // __set
echo $p->couleur; // noir ← __get
echo $p->getCouleur(); // noir ← __call