50

Notice that a trait may use other traits, so the class may not be using that trait directly. And also the class may be inherited from a parent class who is the one uses the trait.

Is this a question that can be solved within several lines or I would have to do some loops?

bijiDango
  • 1,385
  • 3
  • 14
  • 28
  • 1
    There are some functions that will return all traits used by a class (and its parents/etc) in the comments on this page: http://php.net/manual/en/function.class-uses.php – iainn Sep 14 '17 at 11:34
  • @iainn. I think ulf's shall do the work! – bijiDango Sep 14 '17 at 11:38

10 Answers10

45

If anyone has this problem while using the Laravel framework, that has a helper function called class_uses_recursive().

https://laravel.com/docs/8.x/helpers#method-class-uses-recursive

So you can do something like this:

in_array(SomeTrait::class, class_uses_recursive(SomeClass::class))
Ken
  • 2,859
  • 4
  • 24
  • 26
25

The class_uses() function will return an array containing the names of all traits used by that class, and will work by passing it a class name or an instance.... however, you'd need to "recurse" through the inheritance tree to get all traits used, and through each trait as well

EDIT

Note that stealz at op dot pl has provided an example function showing how to do this recursion in the comments section of the linked PHP Docs page

Mark Baker
  • 209,507
  • 32
  • 346
  • 385
20

Check your trait in a trait list:

$usingTrait = in_array(
    MyTrait::class, 
    array_keys((new \ReflectionClass(MyClass::class))->getTraits())
);

This return true or false if MyClass uses MyTrait

Cave Johnson
  • 6,499
  • 5
  • 38
  • 57
Davi Menezes
  • 507
  • 1
  • 5
  • 10
15

Another way to approach this is to use interfaces that define what is expected of your traits. Then you are using "instanceof SomeInterface" instead of doing reflection or duck typing.

MeatPopsicle
  • 832
  • 13
  • 28
15

If you want to check the class only you can use class_uses it will return an array of all traits used by the class only

in_array(SoftDeletes::class, class_uses(Model::class), true) //return boolean

If you want to check if the class or its parents use the trait use class_uses_recursive

in_array(SoftDeletes::class, class_uses_recursive(Model::class), true) //return boolean
Hisham Elsayad
  • 418
  • 5
  • 12
5

Often, checking if the part of API you intend to use exists is a good enough substitute.
method_exists ( mixed $object , string $method_name ) : bool

Also, as @MeatPopsicle mentions, traits are often used in combination with (marker-)interfaces.

4

The below function is from http://php.net/manual/en/function.class-uses.php, ulf's comment. Works perfect.

function class_uses_deep($class, $autoload = true)
{
    $traits = [];

    // Get traits of all parent classes
    do {
        $traits = array_merge(class_uses($class, $autoload), $traits);
    } while ($class = get_parent_class($class));

    // Get traits of all parent traits
    $traitsToSearch = $traits;
    while (!empty($traitsToSearch)) {
        $newTraits = class_uses(array_pop($traitsToSearch), $autoload);
        $traits = array_merge($newTraits, $traits);
        $traitsToSearch = array_merge($newTraits, $traitsToSearch);
    };

    foreach ($traits as $trait => $same) {
        $traits = array_merge(class_uses($trait, $autoload), $traits);
    }

    return array_unique($traits);
}
bijiDango
  • 1,385
  • 3
  • 14
  • 28
1

I had some problems with this topic and I wrote a solution that I want to share with every one. By using a new ReflectionClass($objectOrClass), you can use the instance method getTraits(). The problem is it will only returns an array of traits for the $objectOrClass, and not the inherited traits from the traits or from the extended class.

Here is the solution :

/** @return ReflectionClass[] */
function get_reflection_class_traits( ReflectionClass $reflection, array $traits = [] ): array {
    if ( $reflection->getParentClass() ) {
        $traits = get_reflection_class_traits( $reflection->getParentClass(), $traits );
    }

    if ( ! empty( $reflection->getTraits() ) ) {
        foreach ( $reflection->getTraits() as $trait_key => $trait ) {
            $traits[$trait_key] = $trait;
            $traits = get_reflection_class_traits( $trait, $traits );
        }
    }

    return $traits;
}

// string|object $objectOrClass Either a string containing the name of the class to reflect, or an object.
$objectOrClass = //...
$reflection = new ReflectionClass( $objectOrClass );
$all_traits = $this->get_reflection_class_traits( reflection );
0

This is what I have in my Tools class

static function 
isTrait( $object, $traitName, $autoloader = true )
{
    $ret = class_uses( $object, $autoloader ) ;
    if( is_array( $ret ) )
    {
        $ret = array_search( $traitName, $ret ) !== false ;
    }
    return $ret ;
}
dBags
  • 447
  • 4
  • 4
0

Using ReflactionClass :

array_keys((new \ReflectionClass($object))->getTraits()
Waseem Alhabash
  • 498
  • 4
  • 12