insteadof
La parola chiave "insteadof" in PHP specifica quale trait usare al posto di un altro per un dato metodo. In questo articolo esploriamo il suo utilizzo.
La parola chiave PHP insteadof
insteadof è una parola chiave PHP utilizzata all'interno di un blocco use per risolvere un conflitto di metodi tra due o più trait. Quando una classe utilizza più trait che definiscono ciascuno un metodo con lo stesso nome, PHP non può decidere quale sia il vincitore e genera un errore fatale. insteadof indica a PHP quale metodo del trait mantenere e quale scartare.
Questa pagina spiega il conflitto che risolve, la sua sintassi esatta, come si abbina alla parola chiave as per conservare il metodo "perdente" e i problemi noti da tenere a mente.
Il problema che insteadof risolve
Un trait è un blocco riutilizzabile di metodi che viene copiato in una classe in fase di compilazione. Se due trait definiscono un metodo con lo stesso nome e una classe li usa entrambi, PHP non ha modo di sceglierne uno. Il risultato è un errore fatale di collisione:
<?php
trait FileLogger {
function log() { echo "Writing to a file."; }
}
trait DatabaseLogger {
function log() { echo "Writing to the database."; }
}
class Service {
use FileLogger, DatabaseLogger; // No conflict resolution
}
// PHP Fatal error: Trait method DatabaseLogger::log has not been
// applied as Service::log, because of collision with FileLogger::logLa classe non viene nemmeno creata — si tratta di un errore in fase di compilazione, non di esecuzione. insteadof è il modo per indicare a PHP quale metodo applicare.
Sintassi
insteadof viene scritto all'interno del blocco tra parentesi graffe di un'istruzione use:
use TraitWeKeep, TraitWeDrop {
TraitWeKeep::methodName insteadof TraitWeDrop;
}Si legge come: "usa il methodName di TraitWeKeep al posto di quello di TraitWeDrop." Il trait indicato dopo insteadof ha la propria versione del metodo rimossa dalla classe.
<?php
trait FileLogger {
function log() { echo "Writing to a file."; }
}
trait DatabaseLogger {
function log() { echo "Writing to the database."; }
}
class Service {
use FileLogger, DatabaseLogger {
FileLogger::log insteadof DatabaseLogger;
}
}
$service = new Service();
$service->log();
// Output: Writing to a file.Risoluzione con più di due trait
Se tre o più trait entrano in conflitto, elenca tutti i trait da escludere dopo insteadof, separati da virgole:
<?php
trait Json { function format() { echo "JSON output"; } }
trait Xml { function format() { echo "XML output"; } }
trait Csv { function format() { echo "CSV output"; } }
class Report {
use Json, Xml, Csv {
Json::format insteadof Xml, Csv;
}
}
$report = new Report();
$report->format();
// Output: JSON outputMantenere l'altro metodo con as
insteadof scarta un metodo — ma spesso non vuoi perderlo, vuoi solo averlo con un nome diverso in modo che entrambi siano disponibili. È questo che fa la parola chiave as: assegna un alias al metodo escluso con un nuovo nome, così sopravvive alla risoluzione del conflitto.
<?php
trait FileLogger {
function log($msg) { return "[file] $msg"; }
}
trait DatabaseLogger {
function log($msg) { return "[db] $msg"; }
}
class Service {
use FileLogger, DatabaseLogger {
FileLogger::log insteadof DatabaseLogger; // FileLogger::log becomes log()
DatabaseLogger::log as logToDb; // DatabaseLogger::log survives as logToDb()
}
}
$service = new Service();
echo $service->log("started"), "\n"; // [file] started
echo $service->logToDb("started"), "\n"; // [db] startedinsteadof e as vengono quasi sempre usati insieme: insteadof sceglie il vincitore, as salva il perdente con un alias. La parola chiave as può anche modificare la visibilità di un metodo (ad esempio FileLogger::log as protected;).
Problemi noti
insteadofrisolve solo i conflitti di nomi. Se i due metodi non condividono lo stesso nome, non c'è alcun conflitto einsteadofnon è necessario.- Devi fare riferimento a un metodo che esiste effettivamente nel trait indicato, usando la sintassi
Trait::method. Un errore di digitazione produce un errore fatale. - Non unisce i comportamenti. Il metodo escluso semplicemente non viene applicato;
insteadofnon chiama mai entrambi i metodi. - Un metodo di una classe figlia ha comunque la precedenza su un trait. L'ordine di risoluzione dei metodi è: i metodi definiti nella classe stessa sovrascrivono i metodi dei trait, e i metodi dei trait sovrascrivono i metodi ereditati (dalla classe padre).
Quando usarlo
Ricorri a insteadof ogni volta che componi una classe da più trait e due di essi espongono lo stesso nome di metodo — situazione comune quando si combinano trait di terze parti che non puoi controllare. Combinato con as, ti permette di comporre comportamenti da trait indipendenti senza rinominare il loro codice sorgente, mantenendo le tue classi modulari e prive di conflitti.