Un trait en PHP est un regroupement de une ou plusieurs méthodes au sein d'un même conteneur qui peut être utilisé par plusieurs classes.

Attention, un trait n'a rien à voir avec une interface, qui ne doit obligatoirement définir que la signature de une ou plusieurs méthodes, leurs définitions étant à la charge des classes implémentant l'interface.

En effet, un trait doit contenir la définition des méthodes, et non uniquement leurs signatures.

Il peut cependant contenir des méthodes abstraites, et dans ce cas, les classes qui l'utilisent devront définir ces dernières.

Par ailleurs, un trait est indépendant de la structure interne des classes qui seront amenées à l'utiliser.

En conséquence, il n'est pas possible d'utiliser directement des propriétés de classe dans un trait, d'ou l'utilisation de méthodes abstraites pour définir l'interface qui lui permettra de manipuler les données de l'objet concerné.

Le plus simple pour comprendre le concept est de se baser sur un exemple concret.

Supposons que vous souhaitez représenter au format JSON des instances de deux classes différentes.

Le code pourrait être le suivant, avec les dernières versions de PHP :

<?php

interface jsonizable
{
public function toJSON();
}

class A implements jsonizable
{
protected $title = '';
protected $paragraphes = '';

...
public function toJSON()
{
return json_encode(array('title' => $this->title, 'paragraphes' => $this->paragraphes));
}
...
}

class B implements jsonizable
{
protected $firstName = '';
protected $lastName = '';

...
public function toJSON()
{
return json_encode(array('firstName' => $this->firstName, 'lastName' => $this->lastName));
}
...
}

$a = new A(uniqid(), array(uniqid(), uniqid()));
$aJSON = $a->toJSON();

$b = new B(uniqid(), uniqid());
$bJSON = $b->toJSON();


?>

La prochaine version de PHP permettra l'utilisation de l'interface JsonSerializable pour obtenir le même résultat.

Le code sera alors le suivant :

<?php

class A implements JsonSerializable
{
protected $title = '';
protected $paragraphes = '';

...
public function jsonSerialize()
{
return array('title' => $this->title, 'paragraphes' => $this->paragraphes);
}
...
}

class B implements JsonSerializable
{
protected $firstName = '';
protected $lastName = '';

...
public function jsonSerialize()
{
return array('firstName' => $this->firstName, 'lastName' => $this->lastName);
}
...
}

$a = new A(uniqid(), array(uniqid(), uniqid()));
$aJSON = json_encode($a);

$b = new A(uniqid(), uniqid());
$bJSON = json_encode($b);

?>

Et la même problématique aurait pu également être résolue à l'aide d'un trait, de la manière suivante :

<?php

trait jsonSerializable
{
function toJSON()
{
return json_encode($this->getJSONProperties());
}

public abstract function getJSONProperties();
}

class A
{
use jsonSerializable;

protected $title = '';
protected $paragraphes = '';

...
public function getJSONProperties()
{
return array('title' => $this->title, 'paragraphes' => $this->paragraphes);
}
...
}

class B
{
use jsonSerializable;

protected $firstName = '';
protected $lastName = '';

...
public function getJSONProperties()
{
return array('firstName' => $this->firstName, 'lastName' => $this->lastName);
}
...
}

$a = new A(uniqid(), array(uniqid(), uniqid()));
$aJSON = $a->toJSON();

$b = new A(uniqid(), uniqid());
$bJSON = $b->toJSON();

?>

Je précise cependant que cette solution ne présente aucun intérêt, du fait de l'existence de l'interface JsonSerializable, et ne me permet dans le cas présent que d'illustrer le fonctionnement des traits.

Petite précision, il est tout à fait possible d'aggréger des traits au sein d'un trait et en conséquence, les possibilités qu'ils offrent sont très intéressantes.

Et moi, j'ai réussi à expliquer deux futures fonctionnalités de PHP dans un même billet, que metagoto en soit remercié !

Mise à jour : metagoto a écrit un billet complémentaire sur le sujet, à lire absolument si vous voulez savoir comment tout fonctionne sous le capot.