php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #63911 Ignore conflicting trait methods originationg from identical sub traits
Submitted: 2013-01-05 18:27 UTC Modified: 2017-11-22 17:11 UTC
Votes:47
Avg. Score:4.6 ± 0.7
Reproduced:47 of 47 (100.0%)
Same Version:10 (21.3%)
Same OS:13 (27.7%)
From: bitluni at bitluni dot net Assigned: pmmaga (profile)
Status: Closed Package: Class/Object related
PHP Version: 5.6.9 OS: *
Private report: No CVE-ID: None
 [2013-01-05 18:27 UTC] bitluni at bitluni dot net
Description:
------------
When extracting traits I would like to extract some general functions into sub traits and 'use' them in the traits where needed.
But when I compose a class out of multiple traits with sub traits I have to do obsolete statements regardless if the colliding methods originate from same sub trait.

Just add a check if a method collides with itself

Test script:
---------------
trait A
{
    public function a(){}
}
trait B
{
    use A;
}
trait C
{
    use A;
}
class D
{
    use B, C;
}

Actual result:
--------------
Fatal error: Trait method a has not been applied, because there are collisions with other trait methods on D in

Patches

Add a Patch

Pull Requests

Pull requests:

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2013-01-05 18:39 UTC] bitluni at bitluni dot net
same issue for properties
 [2013-01-06 02:36 UTC] necktrox at gmail dot com
# from http://php.net/manual/de/language.oop5.traits.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;
    }
}
 [2013-01-06 10:31 UTC] bitluni at bitluni dot net
@necktrox

I know there is 'insteadof' but see my example, there is just one function implemented yet still I get collision error. The traits are just usable very flat at the moment. If you have an hirachical stucture of traits like I do, you have to do potentally exponetial count of 'insteadof's in the top level class even there's not one dublicate function implemented.
 [2013-05-03 10:26 UTC] simon at simon dot geek dot nz
This is still occurring with PHP 5.5, commit 
cbe2870b72c4cfdb5c295e83c88d7cff5c39e396 (Fri May 3 12:25:49 2013 +0400).

Not being able to use multiple traits that have the same trait somewhere in their 
composition trees is a rather major limitation of the trait system.
 [2013-09-15 17:23 UTC] heruan at aldu dot net
insteadOf in this case is just a workaround, since there is nothing to 'use 
instead of' something else because here there is a property/method conflicting 
with its very self!
And BTW insteadOf cannot be used as a workaround with properties.
 [2013-09-19 09:17 UTC] gron@php.net
-Status: Open +Status: Verified -Type: Feature/Change Request +Type: Bug
 [2013-09-19 09:17 UTC] gron@php.net
This is indeed a bug. Unfortunately, this case slipped and did not get tested. It 
is a perfectly valid usecase, and should not require the use of insteadof at all.

Best regards
Stefan
 [2013-12-07 08:31 UTC] gaetaneislockedoutof at gmail dot com
I have this issue. It makes me very sad :'(

Windows 7 Home Premium Edition Service Pack 1, 64bit
PHP 5.5.6 / Apache/2.4.7 (Win32)
 [2014-10-29 16:08 UTC] jpauli@php.net
Can someone explain me what you expect as a result ?

For me, this is the right behavior, the engine has no way of differenciating between B::a() and C::A() , you have to help it using 'insteadof'
 [2014-10-29 17:15 UTC] gaetane dot le dot grange at gmail dot com
I went into some detail here: http://stackoverflow.com/questions/20382202/php-trait-method-conflicts-trait-inheritance-and-trait-hierarchies
 [2014-10-29 19:30 UTC] keithdavis at solidtechservice dot com
I think the point here is that if I use the same trait twice because I use a trait that uses an already used trait, it's the SAME method included twice.
 [2014-12-22 21:10 UTC] mw dot wanrooij at vodafonevast dot nl
In my humble opinion, a solution/improvement would be that precedence order should not only be appliccable to classes, but also to traits.

To expand on the given example at the start of this topic, when:
trait A
{
    public function a(){}
    public function b(){}
    public function c(){}
    public function z(){}
}
trait B
{
    use A;
}
trait C
{
    use A;
}
class D
{
    use B, C;
}
The expected result here would be that methods in trait C override methods in trait B, unless explicitly specified using the insteadof operator.

Shortened example using the insteadof operator:
class D
{
    use B;
    use C {
        B::a insteadof C;
    }
}
Here, the expected results would be that methods in trait C override methods in trait B, except for method B::a() which is explicitly defined to be used instead of C::a()

If one would change which methods would override other methods, one would simply change the precedence other, as with the example:
class D
{
    use C;
    use B {
        C::a insteadof B;
    }
}
Here, the expected results would be that methods in trait B override methods in trait C, except for method C::a() which is explicitly defined to be used instead of B::a()

Am I making sense?
 [2014-12-24 05:15 UTC] keithdavis at solidtechservice dot com
I suppose that would work, but that does still divert from the actual issue here - that there is nothing to "override", as it's the same trait being used twice.
 [2015-03-17 15:02 UTC] paul at edunation dot co dot uk
Fixing this issue could make traits a far more useful composition tool! (and avoid countless insteadof statements)

If tampering with the "use" statement is problematic, could an explicit "use_once" statement not add a simple, per-class usage check for a given trait?
 [2015-06-09 14:12 UTC] cmb@php.net
-Operating System: debian +Operating System: * -PHP Version: 5.4.10 +PHP Version: 5.6.9 -Assigned To: +Assigned To: gron
 [2015-06-09 14:12 UTC] cmb@php.net
I would assume that a use statement simply injects the members of
the trait into the lexically current class/trait. So B and C in
the test script of the OP have separate a() methods, which leads
to a conflict when both traits are used in D.

The section "The Flattening Property" in the respective RFC[1]
supports this assumption:

| Traits are only entities of the literal code written in your
| source files. There is no notion about Traits at runtime. They
| are used to group methods and reuse code and are totally
| flattened into the classes composed from them. It is almost like
| a language supported and failsafe copy'n'paste mechanism to
| build classes.

The section "Traits Composed from Traits"[2] furthermore states:

> Since Traits are fully flattened away at compile time it is
> possible to use Traits to compose Traits without any additional
> impact on the semantics.

According to that information I would say the behavior is to be
expected, and as such is not a bug, but rather needs to be
documented in the PHP manual.

As gron stated otherwise, I'm assigning to him. Stefan, can you
please have a look at this issue?

See also <http://3v4l.org/RkYH5>.

[1] <https://wiki.php.net/rfc/horizontalreuse#the_flattening_property>
[2] <https://wiki.php.net/rfc/horizontalreuse#traits_composed_from_traits>
 [2015-12-12 15:44 UTC] serovov at gmail dot com
I would like to add another test case to this issue. It should handle same methods not only from sub-traits but also from current class.

Thanks!

<?php

trait helper {
    public function helper() {}
}


trait util_useful {
    use helper;
    public function do_something_useful() {$this->helper(); return 'useful';}
}

trait util_useless {
    use helper;
    public function do_something_useless() {$this->helper();  return 'useless';}
}

class utils {
    use helper;
    use util_useful;
    use util_useless;

    public function use_helper() { $this->helper();}
}

$ob = new utils();

echo $ob->do_something_useful(), "\n";
echo $ob->do_something_useless(), "\n";
echo $ob->use_helper(), "\n";

?>
 [2017-09-04 11:04 UTC] nicolas dot giraud dot dev at gmail dot com
I experiment the same issue here and it is very unpleasant.
https://3v4l.org/nnvOK

Plus, the error message is frustrating as it tells there are collisions while there are not! :(
 [2017-10-24 05:22 UTC] kalle@php.net
-Status: Verified +Status: Assigned
 [2017-10-24 07:05 UTC] kalle@php.net
-Status: Assigned +Status: Open -Assigned To: gron +Assigned To:
 [2017-11-22 17:11 UTC] pmmaga@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: pmmaga
 [2017-11-22 17:11 UTC] pmmaga@php.net
The fix for this bug has been committed.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.

 For Windows:

http://windows.php.net/snapshots/
 
Thank you for the report, and for helping us make PHP better.

Fixed with commit: http://git.php.net/?p=php-src.git;a=commit;h=179ed6e43d9703fe6bdaa286aec79f7e131a8ab0
 [2017-11-24 18:10 UTC] keithdavis at solidtechservice dot com
I could kiss you!
 
PHP Copyright © 2001-2018 The PHP Group
All rights reserved.
Last updated: Wed Dec 12 06:01:26 2018 UTC