W3docs

Programmazione Orientata agli Oggetti con le Interfacce PHP

Le interfacce PHP sono fondamentali nella OOP: definiscono contratti di metodi, favoriscono il disaccoppiamento e supportano l'implementazione multipla.

Un'interfaccia è un contratto che elenca i metodi che una classe deve fornire, senza specificare come tali metodi funzionano. Consente a classi non correlate di concordare un insieme condiviso di comportamenti, in modo che il resto del codice possa fare affidamento sul contratto piuttosto che su una classe specifica. Questo capitolo spiega come dichiarare le interfacce, implementarle, usare le costanti, estenderle e combinarle, e quali errori comuni evitare.

Se sei nuovo agli oggetti, leggi prima Classi e Oggetti PHP e Cos'è la OOP in PHP.

Cos'è un'Interfaccia in PHP?

Un'interfaccia definisce un insieme di firme di metodi pubblici — nomi, parametri e (facoltativamente) tipi di ritorno — ma nessun corpo di metodo. Qualsiasi classe che implementa l'interfaccia si impegna a fornire un corpo concreto per ognuno di quei metodi. In caso contrario, PHP genera un errore fatale.

Poiché l'interfaccia specifica solo cosa una classe può fare e mai come, è possibile sostituire un'implementazione con un'altra senza modificare il codice che le utilizza. Questo disaccoppiamento è lo scopo principale.

<?php
interface Logger {
  public function log(string $message): void;
}

Regole fondamentali per i membri dell'interfaccia:

  • Tutti i metodi sono implicitamente pubblici e astratti — non è possibile assegnare loro un corpo, né contrassegnarli come private o protected.
  • Un'interfaccia può dichiarare costanti, ma non proprietà.
  • Una classe segnala che soddisfa il contratto con la parola chiave implements.

Perché Usare le Interfacce?

  • Contratti. Garantiscono che qualsiasi classe che le implementa esponga un'API concordata, così i chiamanti sanno esattamente quali metodi sono disponibili.
  • Disaccoppiamento. Il codice può essere scritto contro l'interfaccia (Logger) anziché contro una classe concreta (FileLogger), consentendo di cambiare implementazioni liberamente — un file logger reale in produzione, uno fittizio nei test.
  • Tipi multipli. A differenza dell'ereditarietà di classe, una classe può implementare molte interfacce, consentendole di svolgere più ruoli contemporaneamente.
  • Polimorfismo. Oggetti diversi che condividono un'interfaccia possono essere usati in modo intercambiabile ovunque tale interfaccia sia attesa.

Implementare un'Interfaccia

Usa la parola chiave implements seguita dal nome dell'interfaccia, poi fornisci un corpo per ogni metodo dichiarato nell'interfaccia.

<?php
interface Logger {
  public function log(string $message): void;
}

class ConsoleLogger implements Logger {
  public function log(string $message): void {
    echo "[LOG] {$message}\n";
  }
}

$logger = new ConsoleLogger();
$logger->log('App started');
// Output: [LOG] App started

ConsoleLogger rispetta il contratto Logger, quindi ovunque il codice si aspetti un Logger è possibile passare un ConsoleLogger.

Type Hinting con un'Interfaccia

Il vantaggio maggiore si ottiene quando si esegue il type-hint dell'interfaccia anziché di una classe concreta. La funzione seguente funziona con qualsiasi Logger, quindi è possibile cambiare implementazioni senza riscriverla.

<?php
interface Logger {
  public function log(string $message): void;
}

class ConsoleLogger implements Logger {
  public function log(string $message): void {
    echo "[LOG] {$message}\n";
  }
}

class SilentLogger implements Logger {
  public function log(string $message): void {
    // intentionally does nothing
  }
}

function runJob(Logger $logger): void {
  $logger->log('Job finished');
}

runJob(new ConsoleLogger()); // Output: [LOG] Job finished
runJob(new SilentLogger());  // Output: (nothing)

Implementare Più Interfacce

Una classe può implementare più interfacce contemporaneamente separando i loro nomi con le virgole. Questo è il modo in cui PHP aggira la mancanza di ereditarietà multipla di classe.

<?php
interface Drawable {
  public function draw(): string;
}

interface Serializable {
  public function toArray(): array;
}

class Circle implements Drawable, Serializable {
  public function __construct(private float $radius) {}

  public function draw(): string {
    return "Circle(r={$this->radius})";
  }

  public function toArray(): array {
    return ['type' => 'circle', 'radius' => $this->radius];
  }
}

$c = new Circle(2.5);
echo $c->draw() . "\n";          // Output: Circle(r=2.5)
echo json_encode($c->toArray()); // Output: {"type":"circle","radius":2.5}

Costanti nelle Interfacce

Le interfacce possono contenere costanti, che ogni classe che le implementa eredita. Sono utili per valori condivisi e fissi che appartengono al contratto.

<?php
interface HttpStatus {
  const OK = 200;
  const NOT_FOUND = 404;
}

class Response implements HttpStatus {
  public function notFound(): int {
    return self::NOT_FOUND;
  }
}

$r = new Response();
echo $r->notFound();        // Output: 404
echo HttpStatus::OK;        // Output: 200

Estendere le Interfacce

Un'interfaccia può costruire su un'altra con extends e, a differenza delle classi, un'interfaccia può estendere più interfacce contemporaneamente. Una classe che implementa l'interfaccia figlia deve soddisfare tutti i metodi ereditati.

<?php
interface Readable {
  public function read(): string;
}

interface Writable {
  public function write(string $data): void;
}

interface ReadWrite extends Readable, Writable {}

class Memory implements ReadWrite {
  private string $buffer = '';

  public function read(): string {
    return $this->buffer;
  }

  public function write(string $data): void {
    $this->buffer .= $data;
  }
}

$m = new Memory();
$m->write('hello ');
$m->write('world');
echo $m->read(); // Output: hello world

Verificare un'Interfaccia

Usa l'operatore instanceof per confermare che un oggetto rispetti un determinato contratto prima di chiamare i suoi metodi.

<?php
interface Logger {
  public function log(string $message): void;
}

class ConsoleLogger implements Logger {
  public function log(string $message): void {
    echo $message . "\n";
  }
}

$obj = new ConsoleLogger();
var_dump($obj instanceof Logger); // Output: bool(true)

Interfaccia vs. Classe Astratta

Si sovrappongono ma risolvono problemi diversi:

InterfacciaClasse astratta
Corpi dei metodiNo (solo firme)Sì (può mescolare concreti + astratti)
ProprietàNo (solo costanti)
Quante per classeMolte (implements A, B)Una (extends A)
Usala quandoClassi non correlate condividono una capacitàClassi correlate condividono codice e stato

Scegli un'interfaccia per descrivere una capacità; scegli una classe astratta per condividere un'implementazione parziale tra classi correlate. Se hai bisogno di condividere corpi di metodi tra classi non correlate, consulta i Trait PHP.

Errori Comuni

  • Metodo mancante. Dimenticare di implementare anche un solo metodo dell'interfaccia genera un errore fatale alla dichiarazione della classe.
  • Visibilità ridotta. I metodi delle interfacce sono pubblici; non è possibile implementarli come protected o private.
  • Modifica della firma. I parametri e il tipo di ritorno del metodo implementato devono rimanere compatibili con l'interfaccia, altrimenti PHP genera un errore.
  • Aspettarsi proprietà. Le interfacce dichiarano comportamenti, non dati — non possono contenere proprietà, solo costanti.

Conclusione

Le interfacce ti permettono di programmare verso un contratto anziché verso una classe concreta: definiscono cosa una classe deve fare, supportano implementazioni multiple e rendono il polimorfismo, i test e il refactoring molto più semplici. Combinale con l'ereditarietà e i trait per progettare applicazioni PHP flessibili e manutenibili.

Esercizio

Pratica
Qual è l'uso principale delle interfacce PHP secondo la pagina di W3Docs?
Qual è l'uso principale delle interfacce PHP secondo la pagina di W3Docs?
Was this page helpful?