11

I have two PHP traits that each inherit from the same 3rd trait:

trait C {
    public function smallTalk() {
        echo 'c';
    }
}

trait A {
    use C;
    public function ac() {
        echo 'a'.smallTalk();
    }
}

trait B {
    use C;
    public function bc() {
        echo 'b'.smallTalk();
    }
}

And I want to use them both in a class:

class D {
    use A, B;
    public function acbc() {
        echo ac().bc();
    }
}

but I keep getting the error

Fatal error: Trait method smallTalk has not been applied, because there are collisions with other trait methods on D

I know use_once is not a thing, but I am looking for the same functionality that require_once or include_once provides, but for traits. This example is simplified. My real C has lots of methods and is inherited by many more than 2 traits, so I don't really want to have to repeat a long string of insteadof every time I use more than 1 of these traits.

wogsland
  • 9,106
  • 19
  • 57
  • 93
  • short version: No, manually handling collisions is the price for using traits. – VolkerK Mar 28 '16 at 20:26
  • I suspect you are abusing traits. I only use traits as a last resort. This answer has a good explantion on the alternatives (in C++, but they mostly apply to any OOP language) http://stackoverflow.com/questions/406081/why-should-i-avoid-multiple-inheritance-in-c#answer-407928 – Pevara May 12 '16 at 19:18

2 Answers2

3

You need read: Conflict Resolution

If two Traits insert a method with the same name, a fatal error is produced, if the conflict is not explicitly resolved.

To resolve naming conflicts between Traits used in the same class, the insteadof operator needs to be used to choose exactly one of the conflicting methods.

Since this only allows one to exclude methods, the as operator can be used to allow the inclusion of one of the conflicting methods under another name.

Example:

<?php
trait A {
    public function smallTalk() {
        echo 'a';
    }
    public function bigTalk() {
        echo 'A';
    }
}

trait B {
    public function smallTalk() {
        echo 'b';
    }
    public function bigTalk() {
        echo 'B';
    }
}

class Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
    }
}

class Aliased_Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
        B::bigTalk as talk;
    }
}
Maxim Tkach
  • 1,607
  • 12
  • 23
  • Did you read the question? "My real C has lots of methods and is inherited by many more than 2 traits, so I don't really want to have to repeat a long string of insteadof every time I use more than 1 of these traits." I know I can do what you're saying, but I was asking if there was a _better_ solution. – wogsland May 12 '16 at 19:24
  • 1
    You will not be able to resolve the conflict by other means. Use the trait + extended. If you want to do everything "right" - do not try to do everything with the help of trait. You can create a class C. D extended from C. Without a real example - hard to say which architecture will be true. I recommend to get acquainted with the SOLID. It can assist in selecting the architecture. – Maxim Tkach May 12 '16 at 19:38
  • 1
    Upvoted because this is the correct answer, even if OP doesn't like that it is the correct answer. – mopsyd Dec 31 '17 at 19:38
  • 1
    It's not *really* the correct answer because it ignores the spirit and intent of the original question. The answer is, in php we haven't yet a good solution for handling this. The only solution is per-method aliasing or renaming. There are still short comings in php's trait use; particularly it will attempt to load the same trait in multiple places (use Trait1 in Trait2; then try to use both of those in your class; it should resolve that Trait1 has been included already, but rather, it will fatally conflict). – Rob_vH Jun 14 '18 at 17:15
0

Here is one way to avoid writing a lot of code. Basically is to make use of the anonymous class and call the public function that essentially come out from trait C.

It may still be depend on how your code is really structured (e.g. are you using $this):

trait C
{
    public function smallTalk()
    {
        return 'c';
    }
}

trait A
{
    public function ac()
    {
        // anonymous class to avoid leaking all Trait C functions to
        // the classes that didn't sign up for that. Can't see other bad
        // side effects except for being inefficient
        $local_class = new class {
            use C;
        };
        return 'a' . (new $local_class())->smallTalk();
    }
}

trait B
{
    public function bc()
    {
        $local_class = new class {
            use C;
        };
        return 'b' . (new $local_class())->smallTalk();
    }
}

So you can now you can include both traits:

class D
{
    use A, B;
    public function acbc()
    {
        echo $this->ac() . $this->bc();
    }
}
Douglas
  • 1
  • 1