Dalla versione 5.4 PHP offre un'ulteriore possibilità per aggiungere funzionalità a più di una classe mantenendo un'unica fonte di codice, i traits. In termini tecnici i traits permettono di ottenere un effetto simile all'ereditarietà multipla senza la complessità di quest'ultima ma con dei limiti.
In termini pratici un trait è uno snippet di codice che viene incluso all'interno di una classe per aggiungere delle funzionalità in maniera analoga a quanto avviene all'interno di uno script richiamando un file esterno con include
o require
. Di fatto non c'è alcuna differenza pratica tra richiamare un trait all'interno di una classe e copiare/incollare lo stesso codice. L'unica differenza è che il codice che abbiamo scritto è presente in un unico punto che possiamo modificare portando la stessa modifica in tutte le classi che utilizzano il trait.
Concettualmente quindi un trait serve per aggiungere un behavior (alla lettera "comportamento") ad una serie di classi. Ad esempio possono essere usati per aggiungere funzioni alle classi controller in un modello MVC in maniera modulare.
La struttura del trait
Un trait viene definito con la parola chiave trait
seguita dal nome assegnato e da parentesi graffe che contengono lo snippet vero e proprio di codice, in maniera analoga alle classi. A differenza di queste però un trait non può essere istanziato.
<?php
trait MyTrait
{
// corpo del trait
}
Nel corpo del trait è possibile definire tutto quello che verrebbe definito in una classe quindi sia metodi che proprietà che risulteranno accessibili dalle classi che useranno il trait. I metodi possono essere definiti come public
, protected
e private
. Possono essere definiti come static
e perfino come abstract
. Chiaramente i metodi astratti dovranno essere ridefiniti all'interno delle classi. Anche per quanto riguarda le proprietà è possibile decidere la visibilità .
<?php
trait MyTrait
{
/*
* Proprietà
*/
public $var1;
protected $var2;
private $var3;
/*
* Metodi
*/
public function function1()
{
echo 'function 1';
}
protected function function2()
{
echo 'function 2';
}
private function function3()
{
echo 'function 3';
}
public static function function4()
{
echo 'function 4';
}
abstract function function5();
}
Da notare che è possibile ridefinire nella classe utilizzatrice un metodo precedentemente definito all'interno del trait mentre non è possibile fare la stessa cosa con le proprietà.
Richiamare un trait
L'utilizzo del trait è molto semplice, basta usare la parola chiave use
all'interno del corpo di una classe seguita dal nome del trait stesso. Se la classe usa più di un trait è possibile indicarli nella stessa riga con un unico use
seguito dall'elenco dei nomi dei trait separati da una virgola.
All'interno della classe utilizzatrice possono essere richiamati i metodi e le proprietà definite nel trait come se fossero definiti nella stessa classe, quindi utilizzando la variabile $this
.
<php
class MyClass
{
use MyTrait;
public function function5()
{
echo 'function 5 ', $this->var1;
}
}
$myClass = new MyClass();
$myClass->function3(); // output => 'function 3'
echo $myClass->var1; // output => 'A'
echo $myClass->var3; // errore
Utilizzo avanzato dei trait
Due trait utilizzati nella stessa classe potrebbero definire metodi o proprietà con lo stesso nome: in questo caso l'interprete PHP restituirebbe un errore. Per evitarlo è possibile modificare la modalità di importazione con un costrutto più complesso di use
. Infatti alla lista di trait usati è possibile far seguire un blocco racchiuso tra parentesi graffe per specificare la sorgente di importazione di un metodo o per rinominarlo.
<?php
trait MyTraitEng
{
public function hello()
{
echo 'Hello!';
}
}
trait MyTraitIta
{
public function hello()
{
echo 'Ciao!';
}
}
class MyClass
{
use MyTraitEng, MyTraitIta {
MyTraitEng::hello insteadof MyTraitIta;
MyTraitIta::hello as ciao;
}
}
$myClass = new MyClass();
$myClass->hello(); // output => 'Hello!'
$myClass->ciao(); // output => 'Ciao!'
Infine è possibile cambiare la proprietà di visibilità di un metodo o una proprietà definita in un trait usando la parola chiave as
, come specificato nell'esempio precedente, seguita dalla nuova visibilità e dall'eventuale nuovo nome da attribuire.