implements
Scopri come la parola chiave "implements" di PHP applica i contratti delle interfacce. Sintassi, interfacce multiple, costanti e type hinting.
La parola chiave implements in PHP
La parola chiave implements indica a PHP che una classe si impegna a rispettare il contratto definito da un'interfaccia. Un'interfaccia elenca le firme dei metodi (e facoltativamente le costanti) ma nessun corpo di metodo; una classe che la implementa deve fornire un corpo concreto per ogni metodo dichiarato dall'interfaccia. In caso contrario, PHP lancia un errore fatale prima dell'esecuzione del programma.
Questa pagina tratta la sintassi, l'implementazione di più interfacce contemporaneamente, le costanti delle interfacce, il type hinting rispetto alle interfacce, gli errori comuni e le differenze tra implements ed extends. Se le interfacce sono nuove per te, inizia da Interfacce PHP e Cos'è l'OOP.
Sintassi
interface MyInterface {
public function doSomething(); // signature only — no body
}
class MyClass implements MyInterface {
public function doSomething() {
// the concrete implementation lives here
}
}MyClass implements MyInterface è una promessa: "questa classe fornisce una versione funzionante di ogni metodo richiesto da MyInterface." L'interfaccia è il cosa; la classe è il come.
Un esempio di base
<?php
interface Animal {
public function makeSound();
}
class Dog implements Animal {
public function makeSound() {
echo "Woof!";
}
}
class Cat implements Animal {
public function makeSound() {
echo "Meow!";
}
}
$animals = [new Dog(), new Cat()];
foreach ($animals as $animal) {
$animal->makeSound(); // Output: Woof!Meow!
}Poiché sia Dog che Cat implementano Animal, qualsiasi codice che si aspetta un Animal può lavorare con entrambi senza conoscere la classe concreta. Questo è il fondamento del polimorfismo in PHP.
Implementare più interfacce
A differenza di extends (una classe può estendere un solo genitore), una classe può implementare qualsiasi numero di interfacce — basta separarle con le virgole. La classe deve soddisfare ogni metodo di ogni interfaccia che elenca.
<?php
interface Logger {
public function log(string $message): void;
}
interface Notifier {
public function notify(string $message): void;
}
class AlertService implements Logger, Notifier {
public function log(string $message): void {
echo "LOG: $message\n";
}
public function notify(string $message): void {
echo "NOTIFY: $message\n";
}
}
$service = new AlertService();
$service->log("Disk space low"); // Output: LOG: Disk space low
$service->notify("Disk space low"); // Output: NOTIFY: Disk space lowQuesto è il modo in cui PHP ottiene i vantaggi dell'ereditarietà multipla (condivisione di più contratti) senza i suoi svantaggi (implementazioni ereditate ambigue).
Costanti delle interfacce e type hinting
Un'interfaccia può dichiarare costanti, e una classe che la implementa vi accede come fossero proprie. Il vero punto di forza, però, è il type hinting: quando si indica un parametro con il tipo dell'interfaccia, viene accettata qualsiasi classe che la implementa — così è possibile sostituire le implementazioni liberamente.
<?php
interface PaymentGateway {
const CURRENCY = "USD";
public function charge(float $amount): bool;
}
class StripeGateway implements PaymentGateway {
public function charge(float $amount): bool {
echo "Charging " . self::CURRENCY . " $amount\n";
return true;
}
}
// Accepts ANY PaymentGateway, not just StripeGateway
function processPayment(PaymentGateway $gateway, float $amount): void {
$gateway->charge($amount);
}
processPayment(new StripeGateway(), 49.99); // Output: Charging USD 49.99È anche possibile verificare un oggetto rispetto a un'interfaccia in fase di esecuzione con instanceof:
var_dump($gateway instanceof PaymentGateway); // bool(true)Errori comuni e insidie
- Omettere un metodo è fatale. Se una classe salta anche un solo metodo dell'interfaccia, PHP lancia
Fatal error: Class X contains 1 abstract method and must therefore be declared abstract or implement the remaining methods. Il controllo avviene in fase di compilazione, prima dell'esecuzione di qualsiasi codice. - Le firme devono essere compatibili. La tua implementazione deve mantenere i tipi di parametro e di ritorno dichiarati dall'interfaccia (è possibile allargare i tipi di parametro e restringere i tipi di ritorno secondo le regole di varianza di PHP, ma le incompatibilità sono fatali).
- I metodi delle interfacce sono implicitamente
public. Non è possibile implementare un metodo di un'interfaccia comeprotectedoprivate. - Le interfacce possono estendere altre interfacce. Usa
interface B extends Aper costruire su un'altra interfaccia; una classe che implementaBdeve soddisfare i metodi di entrambe. Nota che si usaextends, nonimplements.
implements vs extends
Queste due parole chiave si confondono facilmente:
extends | implements | |
|---|---|---|
| Usato con | una classe genitore (o interfaccia→interfaccia) | una o più interfacce |
| Eredita codice? | sì — proprietà e corpi di metodi | no — solo il contratto (le firme) |
| Quanti? | una classe estende una classe | una classe implementa molte interfacce |
Una classe può fare entrambe le cose contemporaneamente: class Circle extends Shape implements JsonSerializable { ... }. Per i dettagli sull'ereditarietà, consulta PHP extends e, quando vuoi implementazioni parziali, classi astratte PHP.
Quando usare implements
Opta per un'interfaccia e implements quando:
- Diverse classi non correlate devono essere intercambiabili (ad es. più gateway di pagamento, logger o driver di cache).
- Vuoi eseguire il type hint su una capacità anziché su una classe concreta, mantenendo il codice liberamente accoppiato e facile da testare con i mock.
- Stai definendo un contratto API pubblico che altre classi — incluse quelle scritte in seguito o da altri team — devono rispettare.
Se invece hai bisogno di condividere codice di implementazione effettivo tra classi correlate, usa l'ereditarietà di classe (extends) oppure una classe astratta.