In PHP una classe eredita da un'altra con extends: class Dog extends Animal dà a Dog ogni proprietà e metodo public e protected di Animal. La figlia può aggiungere nuovi metodi, sovrascrivere quelli ereditati dichiarando un metodo con lo stesso nome e chiamare l'originale con parent::method().
Dog non ha un costruttore, quindi usa quello di Animal, ed è per questo che new Dog('Rex') funziona. Una classe figlia può estendere un solo padre; PHP non ha l'ereditarietà multipla delle classi.
Chiamare il metodo del padre con parent::
L'override sostituisce completamente il metodo del padre. Per estenderlo invece di sostituirlo, chiama parent::method() dalla figlia e costruisci sul suo risultato:
Lo stesso vale per i costruttori con parent::__construct(...), trattato nella pagina sul costruttore, compreso cosa succede quando lo dimentichi.
protected vs private nelle classi figlie
Un membro protected è nascosto al codice esterno ma visibile a ogni classe della famiglia. Un membro private è visibile solo nella classe che lo dichiara, quindi una classe figlia non può vederlo affatto:
isset($this->pin) è falso nella figlia perché la figlia non ha accesso al $pin privato del padre, mentre il metodo del padre hasPin() lo legge ancora. Usa protected per ciò su cui le sottoclassi devono costruire, e private per i dettagli che vuoi restare libero di cambiare.
Polimorfismo e instanceof
Il codice scritto per il tipo padre funziona con ogni figlia. Ogni oggetto esegue la propria versione di un metodo sovrascritto, quindi un solo ciclo li gestisce tutti. instanceof verifica se un oggetto appartiene a una classe o a una delle sue figlie:
Quando il padre non deve mai essere usato da solo (un semplice Shape con area 0 ha poco senso), rendilo una classe astratta e dichiara area() abstract.
Regole dell'override: le firme devono restare compatibili
Un metodo che sovrascrive deve accettare almeno ciò che accettava il padre e restituire qualcosa che i chiamanti del padre possano usare. Puoi aggiungere un parametro facoltativo, ampliare il tipo di un parametro o restringere il tipo restituito; non puoi aggiungere un parametro obbligatorio né rendere privato un metodo pubblico.
Cambia string $sep = ',' in un string $sep obbligatorio e PHP si rifiuta di compilare la classe con un errore fatale ("Declaration of CsvExporter::export(array sep): string must be compatible with Exporter::export(array $rows): string").
Classi e metodi final
final ferma l'ereditarietà in quel punto. Un metodo final non può essere sovrascritto, e una final class non può essere estesa. Entrambi vengono verificati quando il file viene compilato, quindi lo script non parte affatto:
<?php
class Payment
{
final public function charge(int $cents): string
{
return "charged $cents";
}
}
class FreePayment extends Payment
{
public function charge(int $cents): string
{
return 'free';
}
}
PHP Fatal error: Cannot override final method Payment::charge() in /home/index.php on line 12
Per una classe final il messaggio è Class B cannot extend final class A. Rendi final un metodo quando il padre conta sul fatto che si comporti esattamente in un modo, per esempio un metodo che verifica i permessi prima di fare qualsiasi altra cosa.
La trappola dei metodi privati
Un metodo privato non viene ereditato, quindi un metodo della figlia con lo stesso nome non lo sovrascrive. Il codice del padre continua a chiamare la propria versione privata, il che sorprende chi si aspetta che venga eseguito il metodo della figlia:
greet() stampa ancora Hello: il $this->word() del padre si risolve nel metodo privato del padre. Cambia private in protected in Greeter ed eseguilo di nuovo; ora il metodo della figlia è un vero override e greet() dice Bonjour.
Domande frequenti
Come funziona extends in PHP?
class Dog extends Animal rende Dog figlia di Animal. Un oggetto Dog ha ogni proprietà e metodo public e protected di Animal, può aggiungerne di propri e può sostituire (override) i metodi ereditati dichiarando un metodo con lo stesso nome.
Come chiamo il metodo della classe padre in PHP?
Usa parent::methodName() dentro il metodo della figlia, per esempio return parent::describe() . ' and more';. Per il costruttore è parent::__construct(...).
PHP supporta l'ereditarietà multipla?
No. Una classe può estendere una sola classe padre. Per condividere comportamenti da più fonti, implementa più interfacce (class A implements B, C) e usa i trait (use Loggable, Timestamps;).
Cosa significa final in PHP?
Un metodo final non può essere sovrascritto da una classe figlia, e una final class non può essere estesa affatto. Violare una delle due regole è un errore fatale quando la classe viene compilata, per esempio Cannot override final method A::f().
Una classe figlia può accedere a una proprietà privata del padre in PHP?
No. Un membro private appartiene solo alla classe che lo dichiara, quindi nella figlia $this->pin è una proprietà non definita (un warning, poi null) e un metodo della figlia con lo stesso nome non sovrascrive un metodo privato. Dichiara il membro protected quando servono alle classi figlie.