It seems to me, there's a couple of things related to traits that were
missed in this implementation.

Take the following example:

<?php

header('Content-type: text/plain');

class Cart
{
  public static $instance;

# public function addItem(CartBehavior $item, $amount=1) // => script
terminates
  public function addItem($item, $amount=1)
  {
    echo "Adding {$amount} {$item->getName()} to shopping cart - price =
".($amount * $item->getPrice())."\n\n";
  }
}

Cart::$instance = new Cart;

trait CartBehavior
{
  public function addToCart($amount=1)
  {
    Cart::$instance->addItem($this, $amount);
  }
}

class Product
{
  use CartBehavior;

  private $_name;
  private $_price;

  public function __construct($name, $price)
  {
    $this->_name = $name;
    $this->_price = $price;
  }

  public function getPrice()
  {
    return $this->_price;
  }

  public function getName()
  {
    return $this->_name;
  }
}

$test = new Product('Fish', 100);
$test->addToCart();

var_dump($test instanceof CartBehavior); // => false

var_dump((new ReflectionClass('Product'))->getTraits()); // CartBehavior is
a ReflectionClass?


The first problem is that type-hinting isn't supported... so you have this
horizontal extensibility now - traits are kind of like interfaces with a
built-in implementation. They're more like interfaces than classes, at
least, in the sense that they can't have state, and you can't create an
instance of a trait.

So the first issue is, you can't use a trait as a type-hint - the addItem()
method can't be type-hinted, so we can't define explicitly what to expect
here. This is one of the most important reasons for having interfaces,
perhaps the second-most important after the ability to enforce conformity
on class implementations. Traits don't work in this case. And sure, you
could add an interface too, and redeclare all your accessors again. But
why? An abstract base-class with some abstract methods seems like a much
stronger tool, except that you can't reuse that horizontally...

Secondly, the instanceof operator doesn't complain when you test to see if
an object carries a trait - if this isn't going to work, it should at least
throw an exception. Although as pointed out, it seems natural to expect
that this would work.

Lastly, when you reflect on a trait, it comes back as an instance of
ReflectionClass. As pointed out, traits are probably more similar to
interfaces than they are to classes, and they definitely don't have
properties, as are exposed by the ReflectionClass type.

I would also point out that the examples in the documentation (which it
seems were just copied from the RFC?) do not demonstrate any real purpose
of this feature. Is this the most anybody has attempted to do with this
feature? Trying to come up with a real example, I didn't get very far
before running into these stumbling blocks.

If this feature is useful in it's current form, I don't see how - and the
examples in the documentation definitely do not demonstrate any real
practical use for this.

Here's another quick example demonstrating these problems:

interface MyInterface
{
  public function myMethod();
}

trait MyTrait # implements MyInterface // fails (B)
{
  public function myMethod()
  {
    echo "foo";
  }
}

class MyClass implements MyInterface
{
  use MyTrait;
}

$test = new MyClass;

var_dump($test instanceof MyInterface); // true
var_dump($test instanceof MyTrait); // false (A)


If I can't use instanceof to check for a trait (A), then I would at least
expect to be able to write a trait that implements an interface (B) - does
that not seem reasonable or logical?

Having to use both the interface and the trait as a pair, and having to
explicitly apply them both to the class, feels like a work-around.

I apologize if I'm somehow missing the big picture here, or maybe I set my
expectations too high - but my first impression of this feature is that
it's crippled and somewhat half-baked... If there was a deliberate and
logical reason for not supporting these features, I would like to
understand why. If not - great work so far, but perhaps this feature is not
quite mature enough for release just yet?

- Rasmus

Reply via email to