Leçon 4/6 10 min

Interfaces & polymorphisme

Le contrat en PHP : interface + implements. Coder contre une interface plutôt qu'une implémentation, et le polymorphisme en action. Code exécutable.

FR EN

Une interface, c'est un contrat

Une interface liste des méthodes sans les implémenter : c'est une promesse. Une classe qui implements une interface s'engage à fournir toutes ces méthodes. Le reste du code peut alors lui faire confiance sans connaître son type exact — c'est le polymorphisme vu dans le cours POO, en PHP.

interface MoyenPaiement {
    public function payer(float $montant): string;   // signature, pas de corps
}

class CarteBancaire implements MoyenPaiement {
    public function payer(float $montant): string {
        return "Payé $montant € par carte";
    }
}
class Paypal implements MoyenPaiement {
    public function payer(float $montant): string {
        return "Payé $montant € via PayPal";
    }
}

Une classe peut implémenter plusieurs interfaces (séparées par des virgules) — contrairement à extends qui n'autorise qu'un seul parent.

Coder contre l'interface, pas l'implémentation

La règle d'or : une fonction doit dépendre du contrat (l'interface), pas d'une classe précise. Ainsi elle accepte n'importe quel moyen de paiement, présent ou futur.

// On type avec l'INTERFACE : accepte carte, PayPal, ou tout futur moyen
function encaisser(MoyenPaiement $moyen, float $montant): void {
    echo $moyen->payer($montant) . "\n";
}

encaisser(new CarteBancaire(), 50);
encaisser(new Paypal(), 30);
// Demain : une classe ApplePay implements MoyenPaiement → marche sans rien changer ici

C'est le secret du code extensible : encaisser() ne sera jamais à modifier quand on ajoute un moyen de paiement. On branche une nouvelle classe qui respecte le contrat, point.

Polymorphisme : exécute

Une seule boucle, plusieurs types : chaque objet répond à sa façon au même appel payer().

<?php
interface MoyenPaiement {
    public function payer(float $montant): string;
}
class CarteBancaire implements MoyenPaiement {
    public function payer(float $m): string { return "💳 $m € par carte"; }
}
class Paypal implements MoyenPaiement {
    public function payer(float $m): string { return "🅿  $m € via PayPal"; }
}
class Especes implements MoyenPaiement {
    public function payer(float $m): string { return "💶 $m € en espèces"; }
}

$moyens = [new CarteBancaire(), new Paypal(), new Especes()];
foreach ($moyens as $moyen) {            // ignore le type exact
    echo $moyen->payer(20) . "\n";
}
?>
Rappel libre

Sans remonter dans la leçon : à quoi sert le mot-clé implements, et pourquoi typer un paramètre avec MoyenPaiement plutôt qu'avec CarteBancaire ?

implements engage une classe à fournir toutes les méthodes promises par l'interface : c'est un contrat vérifié par PHP (sinon erreur). Typer avec MoyenPaiement au lieu de CarteBancaire permet à la fonction d'accepter n'importe quelle classe respectant le contrat (PayPal, espèces, un futur ApplePay) sans jamais la modifier : c'est coder contre l'interface, pas l'implémentation.
Accepter ou rejeter le code de l'IA

Tu demandes à l'IA un moyen de paiement « chèque ». Elle te rend ceci. Ton rôle de relecteur : l'accepter tel quel ou le rejeter, et dire pourquoi.

class Cheque extends CarteBancaire {
    public function payer(float $m): string {
        return "🧾 $m € par chèque";
    }
}
Rejeter : héritage abusif. Cheque hérite de CarteBancaire alors qu'un chèque n'est pas une carte bancaire ; il ne réutilise rien d'elle et risque d'hériter d'un comportement qui n'a aucun sens (frais de carte, etc.). Le bon geste : implémenter directement le contrat avec class Cheque implements MoyenPaiement. On hérite pour un vrai lien « est-un », on implements pour partager un contrat entre classes sans parenté.
Que garantit « class X implements Contrat » ?
Pourquoi typer un paramètre avec une interface plutôt qu'une classe concrète ?
Prochaine étape

PHP n'autorise qu'un seul parent (extends). Alors comment partager un même bout de code entre des classes sans lien de parenté ? Avec les traits.

Leçon 5 : les traits →

An interface is a contract

An interface lists methods without implementing them: it's a promise. A class that implements an interface commits to providing all those methods. The rest of the code can then trust it without knowing its exact type — that's the polymorphism from the OOP course, in PHP.

interface MoyenPaiement {
    public function payer(float $montant): string;   // signature, no body
}

class CarteBancaire implements MoyenPaiement {
    public function payer(float $montant): string {
        return "Paid $montant € by card";
    }
}
class Paypal implements MoyenPaiement {
    public function payer(float $montant): string {
        return "Paid $montant € via PayPal";
    }
}

A class can implement several interfaces (comma-separated) — unlike extends which allows only one parent.

Code against the interface, not the implementation

The golden rule: a function should depend on the contract (the interface), not a specific class. That way it accepts any payment method, present or future.

// We type with the INTERFACE: accepts card, PayPal, or any future method
function encaisser(MoyenPaiement $moyen, float $montant): void {
    echo $moyen->payer($montant) . "\n";
}

encaisser(new CarteBancaire(), 50);
encaisser(new Paypal(), 30);
// Tomorrow: a class ApplePay implements MoyenPaiement → works with no change here

That's the secret of extensible code: encaisser() will never need changing when you add a payment method. You plug in a new class that honors the contract, done.

Polymorphism: run it

One loop, several types: each object answers the same payer() call in its own way.

<?php
interface MoyenPaiement {
    public function payer(float $montant): string;
}
class CarteBancaire implements MoyenPaiement {
    public function payer(float $m): string { return "💳 $m € by card"; }
}
class Paypal implements MoyenPaiement {
    public function payer(float $m): string { return "🅿  $m € via PayPal"; }
}
class Especes implements MoyenPaiement {
    public function payer(float $m): string { return "💶 $m € in cash"; }
}

$moyens = [new CarteBancaire(), new Paypal(), new Especes()];
foreach ($moyens as $moyen) {            // ignores the exact type
    echo $moyen->payer(20) . "\n";
}
?>
Next step

PHP allows only one parent (extends). So how do you share the same bit of code between unrelated classes? With traits.

Lesson 5: traits →
Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement