La famille des véhicules
Un camion et un triporteur sont tous deux des véhicules de livraison : ils ont une immatriculation et peuvent démarrer (hérité), mais chacun calcule sa charge maximale à sa façon (redéfini).
Réutiliser une classe avec extends, appeler le parent (parent::), redéfinir une méthode, imposer un contrat avec abstract et interface, traiter des objets différents de la même façon, et charger les classes automatiquement.
Souk Atlas propose plusieurs modes de livraison : standard, express, retrait au dépôt. Ils partagent beaucoup (une ville, un délai, un résumé) mais calculent leurs frais différemment. L'héritage évite de copier le code commun, et le polymorphisme permet d'écrire une seule boucle pour tous les modes.
Livraison est abstraite : elle fournit resume() mais laisse frais() et delai() à ses enfants. On ne peut pas faire new Livraison.
class Produit
{
public function __construct(protected string $nom, protected float $prix) {}
public function etiquette(): string
{
return "{$this->nom} : {$this->prix} DH";
}
}
class ProduitSolde extends Produit
{
public function __construct(string $nom, float $prix, private int $remise)
{
parent::__construct($nom, $prix);
}
public function etiquette(): string // redéfinition
{
$nouveau = $this->prix * (100 - $this->remise) / 100;
return parent::etiquette() . " → $nouveau DH (-{$this->remise} %)";
}
}
echo (new ProduitSolde("Babouches", 150, 20))->etiquette();
Dans la simulation, Livraison impose frais() et delai(), mais écrit une fois pour toutes resume(), qui les utilise. Chaque mode n’a plus qu’à donner ses deux règles.
interface Exportable
{
public function versCsv(): string;
}
class Commande implements Exportable, JsonSerializable
{
public function __construct(private int $numero, private float $total) {}
public function versCsv(): string { return "{$this->numero};{$this->total}"; }
public function jsonSerialize(): array { return ['numero' => $this->numero, 'total' => $this->total]; }
}
JsonSerializable est une interface fournie par PHP : json_encode($commande) appelle automatiquement jsonSerialize(). On s’en servira pour les API (leçon D.2).
$modes = [new Standard("Fès", 320), new Express("Tanger", 320), new Retrait("Safi", 320)];
foreach ($modes as $m) {
echo $m->resume(), "<br>"; // chaque objet utilise SES frais() et delai()
}
$m instanceof Express permet de tester la classe d’un objet si besoin, mais le polymorphisme sert justement à l’éviter : pour ajouter un mode « Relais », on écrit une nouvelle classe sans toucher à la boucle.
spl_autoload_register(function (string $classe) {
require __DIR__ . "/classes/$classe.php";
});
$p = new Produit("Savon", 30); // classes/Produit.php est inclus à cet instant
Un camion et un triporteur sont tous deux des véhicules de livraison : ils ont une immatriculation et peuvent démarrer (hérité), mais chacun calcule sa charge maximale à sa façon (redéfini).
Une interface est comme une prise normalisée : peu importe l'appareil, s'il a la bonne fiche, il se branche. Toute classe qui implémente Payable doit avoir une méthode payer().
La page s'affichera ici.
Le navigateur demandera /index.php au serveur, qui exécutera le PHP et renverra du HTML.
Consignes 0 / 3
Déclarez une interface Payable avec une méthode montant(): float, et deux classes Commande et Abonnement qui l'implémentent. Écrivez une fonction totalAPayer(array $elements) qui additionne les montants.
<?php interface Payable { public function montant(): float; } class Commande implements Payable { public function __construct(private float $total) {} public function montant(): float { return $this->total; } } class Abonnement implements Payable { public function __construct(private int $mois, private float $prixMois) {} public function montant(): float { return $this->mois * $this->prixMois; } } function totalAPayer(array $elements): float { $t = 0; foreach ($elements as $e) { $t += $e->montant(); // peu importe la classe : elle a montant() } return $t; } echo totalAPayer([new Commande(330), new Abonnement(3, 49)]); // 477
ProduitArtisanal hérite de Produit(nom, prix) et ajoute la propriété artisan. Écrivez son constructeur.
<?php class ProduitArtisanal extends Produit { public function __construct(string $nom, float $prix, public string $artisan) { parent::__construct($nom, $prix); // on laisse Produit initialiser ses propriétés } }
Dans une liste de livraisons, affichez « (gratuit) » seulement pour les objets de type Retrait.
<?php foreach ($modes as $m) { echo $m->resume(); if ($m instanceof Retrait) { echo " (gratuit)"; } echo "<br>"; }