PHP 5.4: why can classes override trait methods with a different signature?
Asked Answered
S

2

12

I'm wondering if there is any good reason why this behaviour is possible in the current PHP 5.4 implementation:

trait T {
    public function test(PDO $pdo) {}
}

class C {
    use T;
    public function test(DOMDocument $dom) {}
}

I thought that the fact that a class uses a trait, guaranteed that this class had a specific interface available. But here, if we inadvertently override the trait method for another purpose, we don't even receive a Strict Standards notice, as with classic inheritance.

Is this specifically allowed on purpose? What for?

Satanism answered 17/7, 2012 at 11:21 Comment(0)
T
29

This behavior is documented. From php.net (http://php.net/manual/en/language.oop5.traits.php):

An inherited member from a base class is overridden by a member inserted by a Trait. The precedence order is that members from the current class override Trait methods, which in return override inherited methods.

No reason for notices here.

Edit:

I took a look on some more serious literature to shed some light on this topic :) . Looks like that such behavior is a part of traits' definition. They are ment to work this way. This is from research "Traits: Composable Units of Behavior"(Proceedings of the European Conference on Object-Oriented Programming):

Another property of trait composition is that the composition order is irrelevant, and hence conflicting trait methods must be explicitly disambiguated (cf. section 3.5). Conflicts between methods defined in classes and methods defined by incorporated traits are resolved using the following two precedence rules.

– Class methods take precedence over trait methods.

– Trait methods take precedence over superclass methods. This follows from the flattening property, which states that trait methods behave as if they were defined in the class itself.

You can read more here: http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf

Thierry answered 17/7, 2012 at 11:36 Comment(5)
Thanks for your answer, it does indeed say that the class can override the trait's methods, but does not say why it can override it with a different signature!Satanism
As I know, traits in PHP override method the same way as inherited, that means no signature check, only method name.Taenia
@Eugene: I get your point, but that still does not explain why the signature of the method is ignored in case of class method precedence, IMO such a conflict (class method overriding trait method with an incompatible signature) should raise a notice - only, I insist, when the signatures are different; if they are equal, then the precedence rule applies. Is there something I'm still missing, or is there something missing in PHP?Satanism
@Timur: with inheritance, there is a Strict Standards notice, hence my question.Satanism
@Benjamin Heh... Didn't know it. If you are really want to know this, we can look at the sources of PHP to see whats happening there :)Taenia
M
0

Very late answer, but: @BenMorel it seems like you're expecting Traits to behave like Interfaces, but Traits and Interfaces aren't the same thing and are not designed for the same purpose.

An Interface is a contract to the outside world which ensures every implementing class can be used identically. Any class which deviates in any method signature breaks the Interface, and is rightly flagged by the PHP interpreter when it encounters the class definition — even before the class is instantiated.

The Interface contract is so important to the outside world that a class's interface list appears before the implementing class's opening braces:

class Foo implements FooInterface, BarInterface {
  // method definitions here...
}

Meanwhilst, a Trait is a convenience to allow code reuse inside a class. The Trait-based code which is reused may in fact be completely private and remain invisible to the user of the class.

Traits' relative unimportance to the outside world is reflected in the fact that Trait references appear between the referring class's curly braces — making them less visible than Interface references:

class Foo implements FooInterface, BarInterface {
  use FooTrait, BarTrait;
  // method definitions here...
}

The accepted answer is fully correct of course. I just felt some further distinction between Interfaces and Traits might be helpful.

Mckinley answered 28/8 at 23:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.