MODULE 06
Programmation Orientée Objet
Classes, héritage, interfaces, traits et les fondements de la POO en PHP 8.
1 Classes & Objets
<?php
class Produit {
// Propriétés typées (PHP 7.4+)
public string $nom;
public float $prix;
private int $stock = 0;
protected string $categorie;
// Constructeur avec promotion de propriétés (PHP 8.0+)
public function __construct(
public readonly string $sku, // readonly = lecture seule après init
string $nom,
float $prix,
string $categorie = 'général'
) {
$this->nom = $nom;
$this->prix = $prix;
$this->categorie = $categorie;
}
// Getter / Setter
public function getStock(): int { return $this->stock; }
public function setStock(int $n): void {
if ($n < 0) throw new InvalidArgumentException("Stock négatif interdit");
$this->stock = $n;
}
// __toString : appelé automatiquement par echo
public function __toString(): string {
return "{$this->nom} ({$this->sku}) — {$this->prix} €";
}
// Méthode chaînable (fluent interface)
public function appliquerRemise(float $pct): static {
$this->prix *= (1 - $pct / 100);
return $this; // retourne $this pour chaîner
}
}
$p = new Produit('SKU-001', 'Clavier', 49.90, 'informatique');
$p->setStock(100);
echo $p; // Clavier (SKU-001) — 49.9 €
$p->appliquerRemise(10)->appliquerRemise(5); // chaînage
echo $p->prix; // 42.41
2 Héritage
<?php
class Animal {
public function __construct(
protected string $nom,
protected int $age
) {}
// Peut être surchargée
public function parler(): string {
return "{$this->nom} fait un bruit.";
}
// final empêche la surcharge dans les sous-classes
final public function description(): string {
return "{$this->nom}, {$this->age} an(s)";
}
}
class Chien extends Animal {
public function __construct(string $nom, int $age, private string $race) {
parent::__construct($nom, $age); // appel obligatoire si parent a un constructeur
}
// override (PHP 8.3+ : attribut #[Override] optionnel pour validation)
public function parler(): string {
return "{$this->nom} aboie ! Race : {$this->race}";
}
}
class Chat extends Animal {
public function parler(): string { return "{$this->nom} miaule."; }
}
$animaux = [new Chien('Rex', 3, 'Labrador'), new Chat('Mimi', 5)];
foreach ($animaux as $a) {
echo $a->parler() . "\n"; // polymorphisme
}
// instanceof vérifie le type
var_dump($animaux[0] instanceof Animal); // true — Chien hérite d'Animal
3 Interfaces
<?php
// Interface : contrat sans implémentation
interface Payable {
public function calculerMontant(): float;
public function getDevise(): string;
}
interface Exportable {
public function toArray(): array;
public function toJson(): string;
}
// Une classe peut implémenter plusieurs interfaces
class Facture implements Payable, Exportable {
public function __construct(
private float $montantHT,
private float $tauxTVA = 20.0
) {}
public function calculerMontant(): float {
return $this->montantHT * (1 + $this->tauxTVA / 100);
}
public function getDevise(): string { return 'EUR'; }
public function toArray(): array {
return [
'ht' => $this->montantHT,
'tva' => $this->tauxTVA,
'ttc' => $this->calculerMontant(),
'devise'=> $this->getDevise(),
];
}
public function toJson(): string {
return json_encode($this->toArray(), JSON_PRETTY_PRINT);
}
}
$f = new Facture(100.0);
echo $f->calculerMontant(); // 120.0
echo $f->toJson();
4 Classes Abstraites
<?php
// Classe abstraite : ne peut pas être instanciée directement
// Mixe méthodes concrètes ET méthodes abstraites (à implémenter)
abstract class Forme {
public function __construct(protected string $couleur = 'noir') {}
// Méthode abstraite : les enfants DOIVENT l'implémenter
abstract public function aire(): float;
abstract public function perimetre(): float;
// Méthode concrète partagée par tous les enfants
public function description(): string {
return sprintf(
"%s %s — aire: %.2f, périmètre: %.2f",
$this->couleur,
static::class, // late static binding : nom de la classe réelle
$this->aire(),
$this->perimetre()
);
}
}
class Cercle extends Forme {
public function __construct(private float $rayon, string $couleur = 'rouge') {
parent::__construct($couleur);
}
public function aire(): float { return M_PI * $this->rayon ** 2; }
public function perimetre(): float { return 2 * M_PI * $this->rayon; }
}
class Rectangle extends Forme {
public function __construct(private float $l, private float $h, string $couleur = 'bleu') {
parent::__construct($couleur);
}
public function aire(): float { return $this->l * $this->h; }
public function perimetre(): float { return 2 * ($this->l + $this->h); }
}
$formes = [new Cercle(5), new Rectangle(4, 6)];
foreach ($formes as $f) echo $f->description() . "\n";
5 Traits
<?php
// Trait : réutilisation de code sans héritage (composition)
trait Horodatable {
private ?DateTime $createdAt = null;
private ?DateTime $updatedAt = null;
public function initTimestamps(): void {
$this->createdAt = new DateTime();
$this->updatedAt = new DateTime();
}
public function touch(): void {
$this->updatedAt = new DateTime();
}
public function getCreatedAt(): string {
return $this->createdAt?->format('Y-m-d H:i:s') ?? 'non défini';
}
}
trait Validatable {
private array $errors = [];
public function addError(string $field, string $msg): void {
$this->errors[$field] = $msg;
}
public function isValid(): bool { return empty($this->errors); }
public function getErrors(): array { return $this->errors; }
}
class Article {
use Horodatable, Validatable; // composition de plusieurs traits
public function __construct(public string $titre, public string $contenu) {
$this->initTimestamps();
$this->validate();
}
private function validate(): void {
if (strlen($this->titre) < 3)
$this->addError('titre', "Titre trop court");
if (strlen($this->contenu) < 10)
$this->addError('contenu', "Contenu trop court");
}
}
$a = new Article('Hi', 'court');
var_dump($a->isValid()); // false
var_dump($a->getErrors()); // ['titre' => '...', 'contenu' => '...']
6 Méthodes & Propriétés Statiques
<?php
class Compteur {
private static int $instances = 0;
public function __construct(private string $nom) {
self::$instances++; // self:: = cette classe exacte
}
public static function getInstances(): int { return self::$instances; }
}
// Pattern Singleton
class Config {
private static ?Config $instance = null;
private array $data = [];
private function __construct() {} // constructeur privé
public static function getInstance(): static {
if (static::$instance === null) {
static::$instance = new static(); // static:: = classe appelante (LSB)
}
return static::$instance;
}
public function set(string $key, mixed $val): void { $this->data[$key] = $val; }
public function get(string $key, mixed $default = null): mixed {
return $this->data[$key] ?? $default;
}
}
$cfg = Config::getInstance();
$cfg->set('debug', true);
$cfg2 = Config::getInstance(); // même objet
var_dump($cfg === $cfg2); // true