25

Is there any function in PHP (5.4) to get used traits as array or similar:

class myClass extends movingThings {
  use bikes, tanks;

  __construct() {
    echo 'I\'m using the two traits:' . ????; // bikes, tanks
  }
}
hakre
  • 193,403
  • 52
  • 435
  • 836
Teson
  • 6,644
  • 8
  • 46
  • 69
  • 1
    I tried to make your question clearer. I think you're wanting to get bikes and tanks (your traits) from within the object or class. Similar to `get_class_methods()` except `get_class_traits()` which doesn't exist. – Mike B Nov 29 '12 at 19:43
  • 1
    @Nik.... , why did you close it? You should not be using "close everything" approach when using `/review`. – tereško Dec 01 '12 at 18:51

7 Answers7

36

To easily get the used traits you can call class_uses()

$usedTraits = class_uses(MyClass);
// or
$usedTraits = class_uses($myObject);

General recommendations

When checking for available functionality, I would generally recommend to use interfaces. To add default functionality to an interface you would use traits. This way you can also benefit from type hinting.

Force an object having functionality by implementing an interface and then use a trait to implement default code for that interface.

class MyClass 
    implements SomeInterface 
{
    use SomeTrait;
}

Then you can check the interface by;

$myObject = new MyClass();
if ($myObject instanceof SomeInterface) {
    //...
}

And still use type hinting;

function someFunction( SomeInterface $object )
{ 
    //...
}
Maurice
  • 4,829
  • 7
  • 41
  • 50
12

You can write it yourself, by using the ReflectionClass

$rc = new ReflectionClass('myClass');
$count = count($rc->getTraits());
Cerbrus
  • 70,800
  • 18
  • 132
  • 147
Ziumin
  • 4,800
  • 1
  • 27
  • 34
8

Just my 2 cents =)

in_array( "CLASS_NAME", class_uses($model) ) 

Or with PHP > 5.6

in_array( CLASS_NAME::class, class_uses($model) )
Mark Mooibroek
  • 7,636
  • 3
  • 32
  • 53
2

class-uses(<class>) will get the immediate traits of <class> however it won't get all inherited traits by way of parent classes or traits of traits etc...

If you need to get absolutely all inherited traits on a class I would recommend reading the comments in the official docs here:

http://php.net/manual/en/function.class-uses.php

Henry
  • 7,721
  • 2
  • 38
  • 38
2

class_uses and ReflectionClass->getTraits will not work if you want to get traits from child class.

Example.

trait A {}

class Mother {
    use A;
}

class Child extends Mother {}

var_dump(class_uses('Child'));  // empty array
var_dump(class_uses('Mother')); // has A

I have this problem so I write simple code for get all traits from classes.

function all_class_uses($model)
{
    $class = new ReflectionClass($model);
    $traits = $class->getTraits();
    while($parent = $class->getParentClass()) {
        $traits += $class->getTraits();
        $class = $parent;
    }
    return array_combine(array_keys($traits), array_keys($traits));
}
EThaiZone
  • 311
  • 1
  • 8
1

I wish that will be useful (thanks to Maurice for interface usage):

    interface IHaveHasTrait
    {
        public function has_trait($name);
    };

    trait THaveHasTrait
    {
        public function has_trait($name)
        {
            $classes = class_parents( $this );
            $classes[] = get_class( $this );
            foreach( $classes as $class ) {
                foreach( class_uses( $class ) as $t ) {
                    if( $t === $name ) {
                        return true;
                    }
                }
            }
            return false;
        }
    };

    trait TLinkedListItem
    {
        use THaveHasTrait;
        public $next;
        public $prev;
    };

    class A implements IHaveHasTrait
    {
        use TLinkedListItem;
        public $text;

        public function __construct( $_text )
        {
            $this->text = $_text;
        }
    };

    class B extends A{};

    class C extends B{};

    class LinkedList
    {
        public function insertItem( &$item, $position=0, $relative=false )
        {
            if( is_a( $item, 'IHaveHasTrait' ) ) {
                echo $item->has_trait( 'TLinkedListItem' ) ? 'has' : 'not';
            }
        }
    };

    $a = new C('a');
    $l = new LinkedList;
    $l->insertItem($a);
    ?>
-2

Short answer: you shouldn't. Traits are almost exactly copy and paste code. You don't NEED to know which traits are used, only on what the traits generate.

Answer I don't want to give: use ReflectionClass::getTraits. I'm not going to elaborate on this one.

Levi Morrison
  • 19,116
  • 7
  • 65
  • 85
  • Point of clarification: Are you saying you shouldn't use traits at all or shouldn't develop functionality that depends on what traits are implemented? – Mike B Nov 29 '12 at 19:47
  • Traits are not just copy&paste. Why not use an information about traits like some kind of Metadata? – Ziumin Nov 29 '12 at 19:49
  • Imho traits should almost always be avoided, because it is just another way of tight coupling code. – PeeHaa Nov 29 '12 at 19:49
  • @Ziumin In PHP they are almost copy and paste. I chose my wording carefully. – Levi Morrison Nov 29 '12 at 19:53
  • @Levi Morrison I didn't say you that your answer is wrong. What about my second question? – Ziumin Nov 29 '12 at 19:57
  • @LeviMorrison Traits can be used as the oposite of "copy and paste", and to avoid redundant code. – Teson Nov 29 '12 at 20:03
  • @user247245 I mean that under the hood it is copy/paste. It has no special meaning, which is why you can't type-hint on it and that's why there isn't a `get_class_traits` function. – Levi Morrison Nov 30 '12 at 20:03
  • @Ziumin If you like programming with metadata you are either doing something cool or foolish. Since he isn't already familiar with Reflection, it's probably the latter. – Levi Morrison Nov 30 '12 at 20:03
  • @LeviMorrison, I'm doing something cool and it has nothing to do with reflection. – Teson Sep 18 '14 at 05:27