# Traits behavior still up in the air in 5.4

- Thread: https://externals.io/message/61901
- Original archive: https://news-web.php.net/php.internals/61901
- Started by Stan Vass on 2012-07-31T23:23:57+00:00
- Messages: 11
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #61901 — Stan Vass — 2012-07-31T23:23:57+00:00

URL: https://externals.io/message/61901#61901

I'd like to point out some puzzling behaviors in Traits as they exist in the production releases of PHP 5.4.

----------------------------------------------------------------------------
1. Name collisions between a trait method and a class method using the trait go unreported, the class silently shadowing the trait method:
----------------------------------------------------------------------------

trait T {
    function foo() { $this->bar; }
    function bar() { echo 'trait'; }
}

class C {
    use T;
    function bar() { echo 'class'; }
}

$c = new C;
$c->foo(); // "class"

Proposed behavior: Fatal error on collision, unless the method is imported with a unique name using the { ... as ... } syntax.

----------------------------------------------------------------------------
2. Using "as" syntax when importing a trait does NOT rename a method, but creates an alias CLONE, the original method still callable.
----------------------------------------------------------------------------

trait T {
    function bar() { echo 'trait'; }
}

class C {
    use T { bar as foo; }
}

$c = new C;
$c->bar(); // "trait"

Proposed behavior: the original name should be only accessible within the trait and its methods, not from the class methods or by calling the class instance's methods from outside.

----------------------------------------------------------------------------
3. Properties silently collide in traits and classes.
----------------------------------------------------------------------------

trait T1 {
    private $foo;

trait T2 {
    private $foo;
}

class C { use T1, T2; } // No error.

Proposed behavior: An error is produced only when the properties differ in visibility or a default value, which is clearly insufficient to determine they're used for the same purpose, and hold the same data. Instead they should use the same logic as method conflicts: fatal error on name collision. Alternatively, each trait property whould be accessible within the trait that defines it, not from other traits used in the same class, or the class itself.

----------------------------------------------------------------------------
4. The documentation says static propeties can't be defined by traits. Yet they can.
----------------------------------------------------------------------------

I don't know what's the bug here: a doc bug, or a code bug. For consistency, static properties should work, if instance properties work. Nothing is gained supporting it half-way.

Not many use 5.4 yet, so the sooner all this is clarified, the better, before it becomes completely unfixable due to BC. Feedback?

Stan

---

## #61903 — Stefan Marr — 2012-08-01T06:10:40+00:00

URL: https://externals.io/message/61901#61903 · In reply to: #61901

Dear Stan:



> ----------------------------------------------------------------------------
> 1. Name collisions between a trait method and a class method using the trait go unreported, the class silently shadowing the trait method:
> ----------------------------------------------------------------------------
> 
> trait T {
>    function foo() { $this->bar; }
>    function bar() { echo 'trait'; }
> }
> 
> class C {
>    use T;
>    function bar() { echo 'class'; }
> }
> 
> $c = new C;
> $c->foo(); // "class"
> 
> Proposed behavior: Fatal error on collision, unless the method is imported with a unique name using the { ... as ... } syntax.

This happens to be 'by design'.

The methods in the class have *always* higher precedence.
This is the same 'overriding' behavior used for inheritance.
From my personal perspective, changing this would lead to a major inconsistency with how subclassing works.

If you disagree, please open an explicit thread for this problem and report it in the bug tracker.



> ----------------------------------------------------------------------------
> 2. Using "as" syntax when importing a trait does NOT rename a method, but creates an alias CLONE, the original method still callable.
> ----------------------------------------------------------------------------
> 
> trait T {
>    function bar() { echo 'trait'; }
> }
> 
> class C {
>    use T { bar as foo; }
> }
> 
> $c = new C;
> $c->bar(); // "trait"
> 
> Proposed behavior: the original name should be only accessible within the trait and its methods, not from the class methods or by calling the class instance's methods from outside.

Again, this happens to be 'by design'.
PHP is a rather dynamic language, and we happen to have things like $c->$foo();
where $foo is a string. Renaming is technically not reasonable, and would also lead to major confusion when metaprogramming is used.

Bye the way, this is documented: https://wiki.php.net/rfc/horizontalreuse#renaming



> ----------------------------------------------------------------------------
> 3. Properties silently collide in traits and classes.
> ----------------------------------------------------------------------------
> 
> trait T1 {
>    private $foo;
> 
> trait T2 {
>    private $foo;
> }
> 
> class C { use T1, T2; } // No error.
> 
> Proposed behavior: An error is produced only when the properties differ in visibility or a default value, which is clearly insufficient to determine they're used for the same purpose, and hold the same data. Instead they should use the same logic as method conflicts: fatal error on name collision. Alternatively, each trait property whould be accessible within the trait that defines it, not from other traits used in the same class, or the class itself.

Please read https://wiki.php.net/rfc/horizontalreuse#handling_of_propertiesstate carefully.

Again, this is intended behavior.
If you want to be notified of such collisions, please use E_STRICT.



> ----------------------------------------------------------------------------
> 4. The documentation says static propeties can't be defined by traits. Yet they can.
> ----------------------------------------------------------------------------
> 
> I don't know what's the bug here: a doc bug, or a code bug. For consistency, static properties should work, if instance properties work. Nothing is gained supporting it half-way.

Could you please point me *exactly* to the paragraph where we have something written about static properties? I do not see that I wrote anything special about static properties in the RFC. And I do not find it in the docs either.

static properties should work like normal properties.


Best regards
Stefan


-- 
Stefan Marr
Software Languages Lab
Vrije Universiteit Brussel
Pleinlaan 2 / B-1050 Brussels / Belgium
http://soft.vub.ac.be/~smarr
Phone: +32 2 629 2974
Fax:   +32 2 629 3525

---

## #61914 — Stan Vass — 2012-08-01T11:54:05+00:00

URL: https://externals.io/message/61901#61914 · In reply to: #61903

> The methods in the class have *always* higher precedence.
> This is the same 'overriding' behavior used for inheritance.
> From my personal perspective, changing this would lead to a major 
> inconsistency with how subclassing works.

Hi, I've subsequently read your RFC carefully and saw some of those items 
are by design. However while they're by design, the end result seems rather 
confusing I should say.

While I'm glad the RFC was shooting for consistency in general, traits are 
not subclassing. Therefore aligning the behavior of features for vertical 
reuse with features for horizontal reuse seems somewhat arbtirary to me. The 
use cases for overriding methods like this seem sketchy at best. What is the 
use case, and how are those use cases more important than the confusion 
caused by allowig this?

> > […]

> Again, this happens to be 'by design'.
> PHP is a rather dynamic language, and we happen to have things like 
> $c->$foo();
where $foo is a string. Renaming is technically not reasonable, and would 
also lead to major confusion when metaprogramming is used.

Can you please explain how are function names by variable at all related to 
this? $object->foo() and $object->$foo() aren't even related in any way 
semantically. If a conflict exists while grafting a trait, the original 
method will be overshadowed silently and the method will be only accessible 
by the alias. The entire idea of aliasing is to avoid a conflict with an 
existing method of the same name. While if there's no collision, it's 
accessible by both. So in practice it works by chance. How is "working by 
chance" in any way aiding meta programming?

> > […]
> Please read 
> https://wiki.php.net/rfc/horizontalreuse#handling_of_propertiesstate 
> carefully.
>
> Again, this is intended behavior.
> If you want to be notified of such collisions, please use E_STRICT.

I've read them carefully now, but there's no solid reason given for this 
behavior, or why the proper behavior only exists in E_STRICT.
I can't really see "state is a complex topic" as a reason why trait property 
collisions are handles inconsistently with method collisions.

> > […]

>Could you please point me *exactly* to the paragraph where we have 
>something written about static properties? I do not see that I wrote 
>anything special about static properties in the >RFC. And I do not find it 
>in the docs either. static properties should work like normal properties.

Sure. From the manual page for traits: "Static variables can be referred to 
in trait methods, but cannot be defined by the trait." This paragraph is 
then following by a code example using function-static variables as what 
seems like an example for a replacement for static trait properties, 
strongly suggesting the former aren't supported. Yet they are.

The RFC seems to also give examples of method-level static variables, and I 
honestly can't see how this is related to traits or trait/class-level 
members at all.

Stan

---

## #61916 — Stan Vass — 2012-08-01T12:00:03+00:00

URL: https://externals.io/message/61901#61916 · In reply to: #61903

> Could you please point me *exactly* to the paragraph where we have 
> something written about static properties? I do not see that I wrote 
> anything special about static properties in the RFC. And I do not find it 
> in the docs either. static properties should work like normal properties.

I'd like to add something to my answer to this question: while I've added a 
doc bug for this quote I gave, people handling the documentation *don't want 
to fix it*, because they think static members *should notwork* in traits, 
this is how they read the RFC for some reson. Thus a clear sign people are 
confused about this behavior. It has to be clarified.

Stan.

---

## #61917 — Stefan Marr — 2012-08-01T12:26:36+00:00

URL: https://externals.io/message/61901#61917 · In reply to: #61914

Hi Stan:



> > […]
> 
> Hi, I've subsequently read your RFC carefully and saw some of those items are by design. However while they're by design, the end result seems rather confusing I should say.
> 
> While I'm glad the RFC was shooting for consistency in general, traits are not subclassing. Therefore aligning the behavior of features for vertical reuse with features for horizontal reuse seems somewhat arbtirary to me.

Horizontal reuse is 'fit into' the hierarchy of things:

  1. super class
  2. horizontal reuse
  3. actual class

From that follows that traits override the super class's methods.
And, conflict resolution is only done between traits, not between a class and its traits.
The class body is a definite thing. Why would you purposefully add something to a class body that conflicts with a trait?
The class needs to know exactly what the traits are doing. There is no black-box reuse.


> The use cases for overriding methods like this seem sketchy at best. What is the use case, and how are those use cases more important than the confusion caused by allowig this?

I don't see your problem. Can you please elaborate with an example (other than your initial one?

The use case for overriding is typically that you got a conflict that you need to resolve by making a call to both conflicting methods.
Consider two traits that happen to provide a `log()` function, and in the resulting class you need to call both in all places `log()` has been called before.

Since the user of these traits is the only place where such semantic is sensibly defined, the method in the class body is overriding the traits methods, and can access aliases you introduced for the traits' methods.


> > […]
> 
> > […]

> Can you please explain how are function names by variable at all related to this? $object->foo() and $object->$foo() aren't even related in any way semantically. If a conflict exists while grafting a trait, the original method will be overshadowed silently and the method will be only accessible by the alias. The entire idea of aliasing is to avoid a conflict with an existing method of the same name. While if there's no collision, it's accessible by both. So in practice it works by chance. How is "working by chance" in any way aiding meta programming?

Let's use your example and make something more complex out of it:

trait T {
   function bar(){ $this->print();
                    $str = 'print';
                    $this->$str(); }
   function print() { echo 'trait'; }
}

class C {
   use T { print as foo; }
}

What is supposed to happen in this example if we assume renaming?
Would you expect the first function call to print() in bar() to work after the renaming?
Would you expect the second call, using metaprogramming, to work as well?


If we use aliasing instead of renaming, we have predictable behavior for all options.
foo just happens to be another alias for print.
If print is conflicting with something else, the conflict can be resolved, but print() is still going to be called reliably. If you do renaming, it will be partial, and you will get foo() called one time, and print() the other time.



> 
> > […]
> 
> I've read them carefully now, but there's no solid reason given for this behavior, or why the proper behavior only exists in E_STRICT.
> I can't really see "state is a complex topic" as a reason why trait property collisions are handles inconsistently with method collisions.

Please do me the favor and consult the mailing list archives.
It is all in the various discussions.

There is no proper way to handle state, state requires splitting, merging, and what not. None of the research prototypes provides a simple solution.
The solution we have is a 'best effort' solution.
If you can do better, please make a proposal.


> > […]
> 
> > […]
> 
> Sure. From the manual page for traits: "Static variables can be referred to in trait methods, but cannot be defined by the trait."
> This paragraph is then following by a code example using function-static variables as what seems like an example for a replacement for static trait properties, strongly suggesting the former aren't supported. Yet they are.
> 
> The RFC seems to also give examples of method-level static variables, and I honestly can't see how this is related to traits or trait/class-level members at all.

I assume we are talking about: http://php.net/manual/en/language.oop5.traits.php

Static variables and members/properties are not related at all. The writer of the documentation didn't get the wording correct. Please feel free to submit a patch.

The example code given in the manual is correct. (Example #9 Static Variables)
The section heading is misleading. It is not about static members, and members and variables are not synonym. Members are synonym with properties, and properties can be static.

Static variables are local to a class, not to a trait.
Traits do not exist at runtime, they are flattened into classes.

With regard to your other mail.
I didn't get a bug report with the keyword trait in it.

If you want me to clarify the confusion, please give me a link.


Best regards
Stefan


> 
> Stan 
> 
> -- 
> 
> 
> 

-- 
Stefan Marr
Software Languages Lab
Vrije Universiteit Brussel
Pleinlaan 2 / B-1050 Brussels / Belgium
http://soft.vub.ac.be/~smarr
Phone: +32 2 629 2974
Fax:   +32 2 629 3525

---

## #61919 — Stan Vass — 2012-08-01T12:56:59+00:00

URL: https://externals.io/message/61901#61919 · In reply to: #61917

> From that follows that traits override the super class's methods.
> And, conflict resolution is only done between traits, not between a class 
> and its traits.
> The class body is a definite thing. Why would you purposefully add 
> something to a class body that conflicts with a trait?
> The class needs to know exactly what the traits are doing. There is no 
> black-box reuse.

But that's precisely my point. No class would *purposefully* add something 
to conflict with a trait. Then by your own description the only case is 
*accidentally* adding something to conflict with it. In such a case why on 
Earth is it allowed without a fatal error?

>The use case for overriding is typically that you got a conflict that you 
>need to resolve by making a call to both conflicting methods.
> Consider two traits that happen to provide a `log()` function, and in the 
> resulting class you need to call both in all places `log()` has been called 
> before.
> Since the user of these traits is the only place where such semantic is 
> sensibly defined, the method in the class body is overriding the traits 
> methods, and can access aliases you
> introduced for the traits' methods.

The way it works right now, on conflict, a class may access the trait method 
via the alias, or its own overriden copy by the conflicting name.
What is a trait to do? It can't access its own method at all as it doesn't 
"know" the alias.

The way this is implemented makes no sense, I'm afraid.

> Let's use your example and make something more complex out of it:
>
> trait T {
>    function bar(){ $this->print();
>                     $str = 'print';
>                     $this->$str(); }
>    function print() { echo 'trait'; }
> }
>
> > […]
>    use T { print as foo; }
> }
>
> What is supposed to happen in this example if we assume renaming?
> Would you expect the first function call to print() in bar() to work after 
> the renaming?
> Would you expect the second call, using metaprogramming, to work as well?

It's very simple:

1) *only* the original name should work for methods defined in a trait 
(including metacalls).
2) *only* the new name should work from methods defined in the using class 
(including metacalls).

Yes, I know: this is a lot more complicated to implement. *But* if we'll 
take shortcuts in user-facing behavior just because it's simpler to 
implement, then aliasing should've have never been added at all for 5.4, 
until it can be implemented reasonably.

> If we use aliasing instead of renaming, we have predictable behavior for 
> all options.

It's not predictable at all. If the class defines "function print" in your 
example above, the trait suddenly can't call it's own method anymore! 
Metacall or not.

> Please do me the favor and consult the mailing list archives.
> It is all in the various discussions.

If behavior specification and reasoning worked by consulting the archives 
for internals, we wouldn't have RFCs. I see no good reasoning in the RFC for 
this behavior, and going though thousands of emails in the archive is not a 
reasonable way to find out the reasoning.

> There is no proper way to handle state, state requires splitting, merging, 
> and what not. None of the research prototypes provides a simple solution.

State does not require splitting, merging. What does state "splitting" even 
mean? Here's a very simple design principle here: if you can't guarantee 
correctness for a behavior, provide a limited behavior, which guarantees 
correctness. This is how language design works. If there's a conflict, die 
with a fatal error, make people rename their properties so there's no 
conflict.

Assuming *silently* that the user is intentionally colliding state variables 
between traits because he/she wants to merge/split state is insanity IMO.

> The solution we have is a 'best effort' solution.
> If you can do better, please make a proposal.

I did in my original email. No property collisions. Fatal errors. It's very 
simple.

> I assume we are talking about: 
> http://php.net/manual/en/language.oop5.traits.php
>
> Static variables and members/properties are not related at all. The writer 
> of the documentation didn't get the wording correct. Please feel free to 
> submit a patch.

I've submitted a doc bug. No one want to fix it since they think it's PHP 
that's wrong, not the manual. I can't do anything more about it.
Here's the link: https://bugs.php.net/bug.php?id=62156

Stan

---

## #61921 — Stefan Marr — 2012-08-01T13:42:27+00:00

URL: https://externals.io/message/61901#61921 · In reply to: #61919

Hi Stan:



> > […]
> 
> But that's precisely my point. No class would *purposefully* add something to conflict with a trait. Then by your own description the only case is *accidentally* adding something to conflict with it. In such a case why on Earth is it allowed without a fatal error?

Because it is not a matter of horizontal reuse.
Why don't you get a warning when you override an inherited method?
Because that is precisely the way things are supposed to work.

The body of a class is not a trait. These methods are not 'equals'.
I still think that design decision is a sensible one with respect to the rest of PHP.



> 
> > […]
> 
> The way it works right now, on conflict, a class may access the trait method via the alias, or its own overriden copy by the conflicting name.
> What is a trait to do? It can't access its own method at all as it doesn't "know" the alias.
> 
> The way this is implemented makes no sense, I'm afraid.

Traits are not classes, traits are supposed to enable composition.
There is no static binding between trait methods. And, yes, this is by design.
It makes sense when you use traits for things you couldn't use classes for.

If you need strong consistency guarantees then you want to use classes.


> 
> > […]
> 
> It's very simple:
> 
> 1) *only* the original name should work for methods defined in a trait (including metacalls).
> 2) *only* the new name should work from methods defined in the using class (including metacalls).
> 
> Yes, I know: this is a lot more complicated to implement. *But* if we'll take shortcuts in user-facing behavior just because it's simpler to implement, then aliasing should've have never been added at all for 5.4, until it can be implemented reasonably.

So, what is your proposal?


> 
> > […]
> 
> It's not predictable at all. If the class defines "function print" in your example above, the trait suddenly can't call it's own method anymore! Metacall or not.

It is calling another method instead, indeed. That's composition, and that's what traits are designed for.


> > […]
> 
> State does not require splitting, merging. What does state "splitting" even mean? Here's a very simple design principle here: if you can't guarantee correctness for a behavior, provide a limited behavior, which guarantees correctness. This is how language design works. If there's a conflict, die with a fatal error, make people rename their properties so there's no conflict.

http://scg.unibe.ch/archive/papers/Berg07aStatefulTraits.pdf


> Assuming *silently* that the user is intentionally colliding state variables between traits because he/she wants to merge/split state is insanity IMO.

Instead of calling my insane, I would prefer if you make constructive proposals.
I guess, since you are an expert in language design (and an active user of PHP), you are aware of this behavior:

class T1 {
  public function foo() {
    $this->prop = 1;
  }
}

class T2 extends T1 {
  public function bar() {
    $this->prop = 2;
  }
}

$o = new T2;
$o->foo();
$o->bar();

var_dump($o);


What is your point again?

Traits offer already a much stronger safety-net than most other constructs in the language. And, thereby are way to strict for some people.

> > […]
> 
> I did in my original email. No property collisions. Fatal errors. It's very simple.

There is nothing simple in PHP...
People don't like fatal errors, especially not for things they intend to just work.
From my example above, the kind of guarantees you expect are just not something that is part of what PHP is.


> > […]
> 
> I've submitted a doc bug. No one want to fix it since they think it's PHP that's wrong, not the manual. I can't do anything more about it.
> Here's the link: https://bugs.php.net/bug.php?id=62156

I don't know who 'they' are, but I am the insane (thanks for that attribute) person who wrote the RFCs and the implementation. So, either you accept my answer that static properties just work, and you fix the manual by providing a greatly appreciate patch,
or, you open a new thread and propose a change, start an RFC, and all that jazz.


Best regards
Stefan


-- 
Stefan Marr
Software Languages Lab
Vrije Universiteit Brussel
Pleinlaan 2 / B-1050 Brussels / Belgium
http://soft.vub.ac.be/~smarr
Phone: +32 2 629 2974
Fax:   +32 2 629 3525

---

## #61923 — Stan Vass — 2012-08-01T14:31:40+00:00

URL: https://externals.io/message/61901#61923 · In reply to: #61921

> Because it is not a matter of horizontal reuse.
> Why don't you get a warning when you override an inherited method?
> Because that is precisely the way things are supposed to work.
>
> The body of a class is not a trait. These methods are not 'equals'.
> I still think that design decision is a sensible one with respect to the 
> rest of PHP.

If traits act like superclasses, why can the class call a private trait 
method, by that logic? It shouldn't be able to. You've mixed two paradigms 
here and the result is messy.
Traits are for horizontal reuse. If you're justifying this by giving 
examples about superclasses, this is the wrong mental model for traits.

Ask anybody "what are traits". The most common answer is "a glorified 
copy-and-paste". Do it, you'll see. "Glorified copy-and-paste."

Now ask yourself, if you have class Foo { public $bar; public $bar; } would 
that be fine? No. So it's not a copy-and-paste.

But if you copy pasted a private method would you be able to call it? Yes. 
So it's copy-and-paste.

So what is it? It's neither and both. I expect a mighty confusion and abuse 
of traits because they can't decide what they are in 5.4. Traits should pick 
one model and stick to it.

> > […]

>So, what is your proposal?

My proposal is what I said above. Either aliases should be *dropped* as a 
feature from traits and the feature has to be rethought, or they have to 
work sensibly as outlined in 1) and 2) above.

IMO they do more harm than help in their current form. Sometimes not 
implementing a compromise is a feature itself.

>
> > […]
>
> It's not predictable at all. If the class defines "function print" in your 
> example above, the trait suddenly can't call it's own method anymore! 
> Metacall or not.

It is calling another method instead, indeed. That's composition, and that's 
what traits are designed for.

> http://scg.unibe.ch/archive/papers/Berg07aStatefulTraits.pdf

Let me quote that PDF you've linked me to:

"By default, variables are private to the trait that deﬁnes them. Because 
variables are private, CONFLICTS BETWEEN VARIABLES CANNOT OCCUR when traits 
are composed. If, for example, traits T1 and T2 each deﬁne a variable x, 
then the composition of T1 + T2 does not yield a variable conﬂict. Variables 
are only visible to the trait that deﬁnes them"

"By default, instance variables are private to their trait. If the scope of 
variables is not broadened at composition time using the variable access 
operator, CONFLICTS DO NOT OCCUR AND THE TRAITS DO NOT SHARE STATE."

Emphasis mine.

This is absolutely not what is implemented in 5.4, and this is in part why 
we're having this thread now.

Here's the proof:

trait T {
   private $foo = 0;
   public function set($x) { $this->foo = $x; }
   public function get() { echo $this->foo . "<br>"; }
}

trait T2 {
   private $foo = 0;
   public function set2($x) { $this->foo = $x; }
   public function get2() { echo $this->foo . "<br>"; }
}

class C {
   use T, T2;
   public function classGet () { echo $this->foo . "<br>"; }
}

$x = new C;

$x->set(321);
$x->get(); // Correctly produces "321"

$x->set2(123);
$x->get2(); // Correctly produces "123"

$x->get(); // Incorrect, the paper says it should still be "321", instead 
the var is shared, so it's "123"

$x->classGet(); // Incorrect, the paper says this shouldn't work at all, 
instead it's "123"


> > […]
>
> Instead of calling my insane, I would prefer if you make constructive 
> proposals.

I'm not calling you insane, I'm calling PHP's behavior on trait conflicts 
insane, and I definitely stand by this, and the paper you're linking me to 
also seems to confirm this. I keep giving you more reasonable behaviors, but 
I don't think you're acknowledging them.

> I guess, since you are an expert in language design (and an active user of 
> PHP), you are aware of this behavior:
>
> class T1 {
>  public function foo() {
>    $this->prop = 1;
>   }
> }
>
> class T2 extends T1 {
>   public function bar() {
>     $this->prop = 2;
>   }
> }
>
> $o = new T2;
> $o->foo();
> $o->bar();
>
> var_dump($o);
>
>  What is your point again?

My point which I've described multiple times at this point is that traits 
are not classes, we already have classes in PHP, you know? Imitiating 
classes with traits is the wrong mental model for their behavior, and the 
paper you're linking me to confirms tht.

> There is nothing simple in PHP...
> People don't like fatal errors, especially not for things they intend to 
> just work.
> From my example above, the kind of guarantees you expect are just not 
> something that is part of what PHP is.

Letting conflicts overwrite randomly properties is not "just working". It's 
an example of "not working, and it's very hard to debug why."

> I don't know who 'they' are, but I am the insane (thanks for that 
> attribute) person who wrote the RFCs and the implementation. So, either 
> you accept my answer that static properties > just work, and you fix the 
> manual by providing a greatly appreciate patch,
> or, you open a new thread and propose a change, start an RFC, and all that 
> jazz.

Once more, I've not called *you* insane, and I hope you can keep the 
discussion to the problem at hand. The most important thing here is what is 
PHP's problem with the implementation of traits, and it certainly has some. 
My goal isn't to turn this into an exchange of insults, but I feel strongly 
about the current implementation of traits, and I'm gonna qualify it as I 
see is appropriate.

---

## #61924 — Stan Vass — 2012-08-01T14:53:17+00:00

URL: https://externals.io/message/61901#61924 · In reply to: #61921

I'd like to apologize for my language. My goal isn't to insult anybody, 
especially PHP contributors of major features like traits.

I hope we can talk strictly implementation issues, I think I have a good 
idea how we can move this forward, I'll email you a list of behavior changes 
that I think make sense next 48 hours and we can discuss it offlist to keep 
the noise low here, if you'd like.

Stan

---

## #61925 — Stefan Marr — 2012-08-01T15:02:10+00:00

URL: https://externals.io/message/61901#61925 · In reply to: #61923

> > […]
> 
> If traits act like superclasses, why can the class call a private trait method, by that logic?

Traits have a very simple semantics:
Step 1: identify which traits are to be used.
Step 2: resolve conflicts between traits.
Step 3: flatten the result into the class.
Step 4: do in the class what ever you are used to do in a normal class.

This results in a "Glorified copy-and-paste.".
This explains why traits are allowed to access private methods, because they are part of the class. They are flattened into it.
But, they are flattened into the class before the body of the class is processed.


> So what is it? It's neither and both. I expect a mighty confusion and abuse of traits because they can't decide what they are in 5.4. Traits should pick one model and stick to it.

Yes, I agree with you that there are known problems with state.

To quote myself: 
https://wiki.php.net/rfc/horizontalreuse#handling_of_propertiesstate


"Traits do not provide any provisioning for handling state. They are meant to provide a light-weight mechanism for flexible code reuse, with the main goal being to avoid code duplication."


Property support is a best-effort solution at the moment.

Following the rules outlined in the RFC:

"
	• Properties are considered incompatible if they differ in their definition. This means, they differ in the applied modifiers (static, public, protected, private) or their initial value.
	• Incompatible properties result in a fatal error.
	• In all other cases, i.e., when the definitions are identical, an `E_STRICT` notice is shown to raise awareness about the potentially problematic, and discouraged use of properties.
	• For those checks, all properties are treated equal. Properties from the base class and the composing class have to be compatible with properties from traits as well as the properties between all traits have to be compatible.
	• Non-coliding properties, and properties which are not considered incompatible behave exactly the same as if they would have been defined in the composing class.
"

Please provide a fix in form of an RFC and a patch.
Thank you!



> My proposal is what I said above. Either aliases should be *dropped* as a feature from traits and the feature has to be rethought, or they have to work sensibly as outlined in 1) and 2) above.
> 
> IMO they do more harm than help in their current form. Sometimes not implementing a compromise is a feature itself.

I disagree with the premise that renaming is necessary.


> > […]
> 
> Let me quote that PDF you've linked me to:
> 
> "By default, variables are private to the trait that deﬁnes them. Because variables are private, CONFLICTS BETWEEN VARIABLES CANNOT OCCUR when traits are composed. If, for example, traits T1 and T2 each deﬁne a variable x, then the composition of T1 + T2 does not yield a variable conﬂict. Variables are only visible to the trait that deﬁnes them"
> 
> "By default, instance variables are private to their trait. If the scope of variables is not broadened at composition time using the variable access operator, CONFLICTS DO NOT OCCUR AND THE TRAITS DO NOT SHARE STATE."
> 
> Emphasis mine.
> 
> This is absolutely not what is implemented in 5.4, and this is in part why we're having this thread now.

You are missing the point of the complexity that comes with the whole proposal.
Granting access, and merging add quite some complexity to the language (not only the implementation).
There have been proposal like a 'local' keyword for traits before.
They never got implemented.


> My point which I've described multiple times at this point is that traits are not classes, we already have classes in PHP, you know? Imitiating classes with traits is the wrong mental model for their behavior, and the paper you're linking me to confirms tht.

How do you prevent a user from using dynamic (properties that are not predeclared) in a method that comes from a trait?



> > […]
> 
> Letting conflicts overwrite randomly properties is not "just working". It's an example of "not working, and it's very hard to debug why."

Again, what is your point? PHP is the wrong language for you then.
PHP is very dynamic and let's you shoot yourself in foot in all kind of strange ways.

Traits do not change these semantics. They try to integrate with them.
And the main problem here is that PHP got dynamic properties.

If you want to change the semantics to 'local-to-trait'-by-default fields, please provide a patch and an RFC. I won't stop you. On the contrary, it is certainly an appealing option.

Throwing a FATAL however is not an appealing option.
We are already pretty strict when we see that it is most likely a bug.
And we give you a `E_STRICT` warning. That's what we can do for the moment.

Best regards
Stefan

-- 
Stefan Marr
Software Languages Lab
Vrije Universiteit Brussel
Pleinlaan 2 / B-1050 Brussels / Belgium
http://soft.vub.ac.be/~smarr
Phone: +32 2 629 2974
Fax:   +32 2 629 3525

---

## #61928 — Christopher Jones — 2012-08-01T16:53:16+00:00

URL: https://externals.io/message/61901#61928 · In reply to: #61901

> I'd like to point out some puzzling behaviors in Traits as they exist in the production releases of PHP 5.4.

Regardless of the outcome of the mail thread, can you review the traits tests and create new tests for
any behaviour not already covered?

Chris

-- 
christopher.jones@oracle.com
http://twitter.com/#!/ghrd
