web-dev-qa-db-fra.com

PHP instanceof pour les traits

Quelle est la bonne façon de vérifier si une classe utilise un certain trait?

24
xpedobearx

Bien que rien ne vous empêche d'utiliser instanceof avec des traits, l'approche recommandée consiste à associer des traits à des interfaces. Vous auriez donc:

class Foo implements MyInterface
{
    use MyTrait;
}

MyTrait est une implémentation de MyInterface. Ensuite, vous vérifiez l'interface au lieu de traits comme celui-ci:

if ($foo instanceof MyInterface) {
    ...
}

Et vous pouvez également taper un indice, que vous ne pouvez pas avec des traits:

function bar(MyInterface $foo) {
    ...
}

Dans le cas où vous avez absolument besoin de savoir si une classe utilise un trait ou une implémentation, vous pouvez simplement ajouter une autre méthode à l'interface, qui renvoie une valeur différente en fonction de l'implémentation.

32
Kuba Birecki

Vous pouvez utiliser class_uses fonction pour obtenir un tableau de tous les traits utilisés par une classe.

Ensuite, vous vérifiez si ce tableau a une clé avec le même nom que la caractéristique que vous testez.

si c'est le cas, alors votre classe utilise votre trait. Sinon, il ne l'utilise pas.

27
Rod Elias

Ce n'est pas vraiment propre et peut ne pas être la bonne solution pour votre cas. Mais une alternative est de vérifier si l'objet ou la classe implémente une méthode de Trait (comme d'habitude vous n'écrasez pas les méthodes existantes avec Trait)

if (method_exists($my_object, 'MyTraitSpecificMethod')){
    ...
}
8
thesailorbreton

Je viens de découvrir comment Laravel résout cela et pensais que je le partagerais ici. Il utilise class_uses en dessous mais passe par tous les parents pour trouver tous les traits récursivement.

Il définit une fonction d'aide appelée class_uses_recursive :

function class_uses_recursive($class)
{
    if (is_object($class)) {
        $class = get_class($class);
    }

    $results = [];

    foreach (array_reverse(class_parents($class)) + [$class => $class] as $class) {
        $results += trait_uses_recursive($class);
    }

    return array_unique($results);
}

function trait_uses_recursive($trait)
{
    $traits = class_uses($trait);

    foreach ($traits as $trait) {
        $traits += trait_uses_recursive($trait);
    }

    return $traits;
}

Et vous pouvez l'utiliser comme ceci:

in_array(MyTrait::class, class_uses_recursive($class));

Vous pouvez voir comment ils l'utilisent pour vérifier si un modèle implémente le trait SoftDeletes ici :

public function throughParentSoftDeletes()
{
    return in_array(SoftDeletes::class, class_uses_recursive($this->throughParent));
}
0
solarc