Hi:
Traits do not provide any special provisioning for handling properties, especially, there is no language solution for handling colliding property names.
The current solution/idiom for handling state safely in a trait is to use either abstract set/get methods or an abstract get that returns a reference to the property in the class.
However, at the moment it is possible to define properties in a trait:
trait Foo {
private $a;
public $foo;
}
For the moment, that information is completely ignored, thus:
class Bar {
use Foo;
}
property_exists('Bar', 'a') === false
Well, and that is a rather inconsistent status-quo.
I would like to have that fixed in one or another way.
One possibility would be to forbid property definition in a trait altogether.
That reduces a bit the possibility to have wrong expectations about properties, however, the dynamic property creation is still possible.
Another way would be to merge the properties in the composing class.
The question here would be how to treat visibility modifiers: how to merge public and private, should it result in public, or private?
And, to discorage users to go this way, should there be a STRICT notice? Options here are a notice whenever a property is defined in a trait, or whenever properties are silently merged.
Comments very welcome.
Thanks
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
Hi:
Traits do not provide any special provisioning for handling properties,
especially, there is no language solution for handling colliding property
names.
The current solution/idiom for handling state safely in a trait is to use
either abstract set/get methods or an abstract get that returns a reference
to the property in the class.However, at the moment it is possible to define properties in a trait:
trait Foo {
private $a;
public $foo;
}For the moment, that information is completely ignored, thus:
class Bar {
use Foo;
}
property_exists('Bar', 'a') === falseWell, and that is a rather inconsistent status-quo.
I would like to have that fixed in one or another way.
One possibility would be to forbid property definition in a trait
altogether.
That reduces a bit the possibility to have wrong expectations about
properties, however, the dynamic property creation is still possible.Another way would be to merge the properties in the composing class.
The question here would be how to treat visibility modifiers: how to merge
public and private, should it result in public, or private?
And, to discorage users to go this way, should there be a STRICT notice?
Options here are a notice whenever a property is defined in a trait, or
whenever properties are silently merged.
I would prefer they be definable within traits and merged into classes,
otherwise traits will not have the chance to be self-contained entities of
reusable logic. Also, I think merging them in is consistent with the
treatment given to methods as they pertain to traits.
As I'm sure you know:
<?php
class A {
use SomeTrait;
}
trait SomeTrait {
public function traitMethod() {}
}
method_exists('A', 'traitMethod') === true;
?>
Regarding visibility modifiers, why not carry them over from the trait
directly, private in the trait definition results in private in the class
definition. Lastly, I'm not sure why you would want to discourage this
usage, I would plan on adding properties in traits myself.
-nathan
Hi Nathan:
Regarding visibility modifiers, why not carry them over from the trait directly, private in the trait definition results in private in the class definition.
The problem will be hopefully more clear in the following example:
trait StateMachineDoor {
private $currentState;
public function open() {....}
}
trait StateMachineElevator {
public $currentState;
public function callElevator() { ... }
}
These two traits are not compatible since currentState has different semantics in both traits.
With the current design of traits, there is no language solution to this problem.
The suggestions to avoid the problem are to use better names like $currentDoorState and $currentElevatorState, or to rely on accessors which is currently the only variant in which the programmer will notice that there is an incompatibility:
trait StateMachineDoor {
abstract function &getCurrentState();
public function open() {....}
}
trait StateMachineElevator {
abstract function &getCurrentState();
public function callElevator() { ... }
}
However, you might have a different situation, one where the traits are composable with respect to their state, i.e., they need to work on the same state:
trait OutputIterator {
public $array; // not sure why that is public here, but the implementor chose to make it public...
public function printNext() {...}
}
trait SortArray {
private $array; // this developer chose to make the array private, for what ever reason...
public function doSort() { ... }
}
I hope that makes the possible situations clear: state can either be composable or not, that really depends
on the trait. And there is no language solution for it build in at the moment.
So, back to my original question:
class SomethingOutputableAndSortable {
use OutoutIterator, SortArray;
}
What is the visibility of $array supposed to be in this class? private or public?
And further, in the very first example of this mail, ideally there should be some warning, however, that warning would be annoying for the last example, since here the state does not collide...
Best regards
Stefan
PS: there has been discussion on stateful traits before, but the language solutions to that where considered to complex.
--
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
Another way would be to merge the properties in the composing class.
+1
The question here would be how to treat visibility modifiers
One option would be to only allow private. That way only methods from
the trait would have access and collisions could be prevented.
And, to discorage users to go this way, should there be a STRICT
notice?
If you want to discourage attribute declaration in a trait, don't
allow it at all.
--
Sebastian Bergmann Co-Founder and Principal Consultant
http://sebastian-bergmann.de/ http://thePHP.cc/
Hi Sebastian:
And, to discorage users to go this way, should there be a STRICT
notice?If you want to discourage attribute declaration in a trait, don't
allow it at all.
Not allowing it is not an option as far as I can tell.
You can always use dynamically defined properties in a method.
Changing that would change the whole character of PHP.
Then we would have two types of methods, methods that are defined in the class directly and can do what ever they want with properties, and methods from traits which are restricted and can't access any state.
I think, that would be to much penalty for all the valid use cases where a naive property usage in a trait is still just fine.
Best regards
Stefan
--
Sebastian Bergmann Co-Founder and Principal Consultant
http://sebastian-bergmann.de/ http://thePHP.cc/--
--
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
If you want to discourage attribute declaration in a trait, don't
allow it at all.
Not allowing it is not an option as far as I can tell.
Good! :-)
--
Sebastian Bergmann Co-Founder and Principal Consultant
http://sebastian-bergmann.de/ http://thePHP.cc/
hi,
Would it be possible to somehow document what you are discussing here?
It is not too easy to keep track of all discussions about traits
(along other things). Maybe in draft RFC or a simple page in the wiki.
Doing so will help to have a quick view about the open questions or
recent changes/propositions.
Thanks!
Hi:
Traits do not provide any special provisioning for handling properties, especially, there is no language solution for handling colliding property names.
The current solution/idiom for handling state safely in a trait is to use either abstract set/get methods or an abstract get that returns a reference to the property in the class.However, at the moment it is possible to define properties in a trait:
trait Foo {
private $a;
public $foo;
}For the moment, that information is completely ignored, thus:
class Bar {
use Foo;
}
property_exists('Bar', 'a') === falseWell, and that is a rather inconsistent status-quo.
I would like to have that fixed in one or another way.
One possibility would be to forbid property definition in a trait altogether.
That reduces a bit the possibility to have wrong expectations about properties, however, the dynamic property creation is still possible.Another way would be to merge the properties in the composing class.
The question here would be how to treat visibility modifiers: how to merge public and private, should it result in public, or private?
And, to discorage users to go this way, should there be a STRICT notice? Options here are a notice whenever a property is defined in a trait, or whenever properties are silently merged.Comments very welcome.
Thanks
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--
--
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
Hi Pierre:
hi,
Would it be possible to somehow document what you are discussing here?
It is not too easy to keep track of all discussions about traits
(along other things). Maybe in draft RFC or a simple page in the wiki.
Doing so will help to have a quick view about the open questions or
recent changes/propositions.
Yes, I try to keep track of all this in the RFC:
http://wiki.php.net/rfc/horizontalreuse?do=revisions
The current status of the property behavior is not yet documented explicitly, it is only implied since it is not handled at all...
And, it is the only open question that 'needs' to be solved since it is an inconsistency.
For instance the 'require Interface' is more like an additional feature.
Best regards
Stefan
Thanks!
Hi:
Traits do not provide any special provisioning for handling properties, especially, there is no language solution for handling colliding property names.
The current solution/idiom for handling state safely in a trait is to use either abstract set/get methods or an abstract get that returns a reference to the property in the class.However, at the moment it is possible to define properties in a trait:
trait Foo {
private $a;
public $foo;
}For the moment, that information is completely ignored, thus:
class Bar {
use Foo;
}
property_exists('Bar', 'a') === falseWell, and that is a rather inconsistent status-quo.
I would like to have that fixed in one or another way.
One possibility would be to forbid property definition in a trait altogether.
That reduces a bit the possibility to have wrong expectations about properties, however, the dynamic property creation is still possible.Another way would be to merge the properties in the composing class.
The question here would be how to treat visibility modifiers: how to merge public and private, should it result in public, or private?
And, to discorage users to go this way, should there be a STRICT notice? Options here are a notice whenever a property is defined in a trait, or whenever properties are silently merged.Comments very welcome.
Thanks
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--
--
Pierre@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
--
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
The current status of the property behavior is not yet documented explicitly
On the assumption that traits WILL include properties (with
visibility) and aliasing can do all its magic, how would the situation
be handled where multiple traits define shared properties.
I've not got a use case, but say trait1 and trait2 both define the
same property.
Assuming name conflicts are handled via aliasing, then the property
needs to alert the aliasing code that this property is a non
conflicting property. All traits wanting to access the shared property
would have to reveal their intentions.
I can think of 2 ways to handle this (but I'm no genius here, so take
them apart at your pleasure).
1 - The trait's code marks shared properties with a keyword (shared,
common, virtual, something). During incorporation of the trait into
the main class, any marked properties are checked for visibility only.
2 - The trait's code uses a &$property. The fact that this is a
reference would require the creation of the property (if it doesn't
already exist) whilst the trait is being compiled into the main class.
I'd guess this would be the least difficult to implement, but I know
squat about this.
I'm guessing the order of handling the traits would be significant here.
Trait1 uses $property, Trait2 uses $property - conflict. Must be
resolved by aliasing or an error.
Trait1 uses &$property, Trait2 uses &$property - all ok. Trait1 wants
access a non existing property, so one is created. Trait2 is sharing
the now pre-existing property.
Trait1 uses &$property, Trait2 uses $property - conflict. Trait1 wants
access a non existing property, so one is created. Trait2's $property
must be aliased to an error.
Trait1 uses $property, Trait2 uses &$property - all ok. Trait1's
$property is added as expected. Trait2 is sharing the now pre-existing
property.
Richard.
--
Richard Quadling
Twitter : EE : Zend
@RQuadling : e-e.com/M_248814.html : bit.ly/9O8vFY
Hi Richard:
The current status of the property behavior is not yet documented explicitly
On the assumption that traits WILL include properties (with
visibility) and aliasing can do all its magic, how would the situation
be handled where multiple traits define shared properties.I've not got a use case, but say trait1 and trait2 both define the
same property.Assuming name conflicts are handled via aliasing, then the property
needs to alert the aliasing code that this property is a non
conflicting property.
Just to emphasize this another time: aliasing is no magic, it is NOT renaming.
(And it is only supported for methods.)
The important implication here is, that aliasing is only useful from the viewpoint of the composing class.
Form the trait's perspective, aliasing does not have any effect.
Aliasing can be used to make a function accessible that has a naming conflict with another function.
It enables composition of traits, but does not do anything with regard which function names a trait-function calls. In PHP all function names are late bound, there is no inner binding between functions in traits.
Hope that clarifies what I perceived as a misconception.
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
Hi:
Traits do not provide any special provisioning for handling properties, especially, there is no language solution for handling colliding property names.
The current solution/idiom for handling state safely in a trait is to use either abstract set/get methods or an abstract get that returns a reference to the property in the class.However, at the moment it is possible to define properties in a trait:
trait Foo {
private $a;
public $foo;
}For the moment, that information is completely ignored, thus:
class Bar {
use Foo;
}
property_exists('Bar', 'a') === falseWell, and that is a rather inconsistent status-quo.
I would like to have that fixed in one or another way.
One possibility would be to forbid property definition in a trait altogether.
That reduces a bit the possibility to have wrong expectations about properties, however, the dynamic property creation is still possible.Another way would be to merge the properties in the composing class.
The question here would be how to treat visibility modifiers: how to merge public and private, should it result in public, or private?
And, to discorage users to go this way, should there be a STRICT notice? Options here are a notice whenever a property is defined in a trait, or whenever properties are silently merged.Comments very welcome.
Thanks
Stefan
From the rfc [1], "A Trait is similar to a class, but only intended to
group functionality".
I'm guessing that says it all. A trait has no properties.
But.
If properties are to be added to a trait, I think that should come as
a further enhancement and let traits start out as "methods only".
If a trait is properties, then also supporting constants and the other
sort of set/get properties would provide a strong level of
consistency. If I can put it in a class, I can put it in a trait.
As visibility on the traits methods can be manipulated via the
aliasing mechanism, so should any visibility to a property.
Richard.
[1] http://wiki.php.net/rfc/horizontalreuse
Richard Quadling
Twitter : EE : Zend
@RQuadling : e-e.com/M_248814.html : bit.ly/9O8vFY
Hi Richard:
From the rfc [1], "A Trait is similar to a class, but only intended to
group functionality".I'm guessing that says it all. A trait has no properties.
It is really a practical concern of language consistency for the moment.
I am not talking about any fancy new language feature to handle state.
At the moment, I am just concerned with examples like the following:
trait Foo {
function bar() {
$this->baz = 'abcd';
}
}
If that example becomes more complex, I would consider it to be good software engineering practice to document the usage of $this->baz and its semantic.
For classes this is usually done by explicitly naming the property in the class body.
At the moment, there is nothing which hinders you in doing that for a trait.
However, since traits do not provide any safety provisioning for state, i.e., there is no collision handling for properties, the question is, how do we either promote to use explicit accessors or how do we deal with the inevitable and certainly justified use of properties in one or the other way.
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
However, since traits do not provide any safety provisioning for state,
i.e., there is no collision handling for properties, the question is, how
do we either promote to use explicit accessors or how do we deal with the
inevitable and certainly justified use of properties in one or the other
way.Best regards
Stefan
Thinking about it, I'm not sure that accessors are really a solid solution
either.
Behavior has to have something to behave on. So whether you have
$this->foo
or
$foo = $this->getFoo();
// Do stuff with $foo
$this->setFoo($foo);
You still have a dependency that the composing class have either a property
named $foo or a pair of (frankly pointless) get/set methods.
So either a composing class needs to know about the internal implementation
details of a trait (what it calls variables inside of a method) so that it can
provide what the trait needs, or a trait needs to be able to carry around its
own implementation details.
So it seems to me like we can't not let traits carry properties, which means
we need to resolve them some how.
--Larry Garfield
Hi Larry:
I am fine with this approach, with 2 caveats:
- If you actually do want to make two traits use the same property, it looks like the answer here is "Either have no property and demand the existence of an accessor that returns by reference, or you can't write E_NOTICE-safe code". Is that true?
Yes, that is the tradeoff, perhaps the notice could be restricted be shown in strict-mode only.
The manual says the following about E_STRICT, so it seems to fit, but I am not sure about the usual usage of E_STRICT
throughout the engine.
Manual:
Note:
In PHP 5 a new error levelE_STRICT
is available. AsE_STRICT
is not included withinE_ALL
you have to explicitly enable this kind of error level. EnablingE_STRICT
during development has some benefits. STRICT messages will help you to use the latest and greatest suggested method of coding, for example warn you about using deprecated functions.
- When the visibility collides, should we be folding to the most restrictive or least restrictive? I'm not sure myself; I'm more interested in your reasoning for going for most-restrictive.
So, in general, I think, there is no 'right thing' to do here, because the definitions seem to be incompatible but that is only decidable at the application-level.
There is another, related, edge-case in the test case below.
In the test we have different initial values for the properties, which makes it 'obvious' that they are incompatible.
Thus, in that case I would argue for a E_COMPILE_ERROR
instead of a notice.
The reason to use the stricter modifier is, well, arbitrary.
At least I do not find an argument for either way that completely convinces me.
At the moment, the main reason is that 'public' can be considered to be the default visibility modifier.
Based on the semantics of 'var' and dynamic properties.
Thus, private tends to imply that an additional constraint was consciously applied to the property, which should be preserved.
Well, but as I said, thats kind of arbitrary and if you look at the inheritance rules and try to reason from the view of clients with respect to the external interface, then going with public as the chosen modifier also starts to sound reasonable...
--TEST--
Conflicting properties with different visibility modifiers should be merged
to the most restrictive modifier.
--FILE--
<?php
error_reporting(E_ALL);
trait THello1 {
public $hello = "foo";
}
trait THello2 {
public $hello = "bar";
}
class TraitsTest {
use THello1;
use THello2;
public function getHello() {
return $this->hello;
}
}
$t = new TraitsTest;
?>
--EXPECTF--
Fatal error: Conflicting definitions for property TraitsTest::$hello provided by THello1, THello2 in %s on line %d
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
At least I do not find an argument for either way that completely
convinces me.
At the moment, the main reason is that 'public' can be considered to
be the default visibility modifier.
Based on the semantics of 'var' and dynamic properties.
Thus, private tends to imply that an additional constraint was
consciously applied to the property, which should be preserved.
Well, but as I said, thats kind of arbitrary and if you look at the
inheritance rules and try to reason from the view of clients with
respect to the external interface, then going with public as the
chosen modifier also starts to sound reasonable...
Does the order of the declaration matter?
trait THello1 {
public $foo;
}
trait THello2 {
private $foo;
}
trait THello3 {
protected $foo;
}
class TraitsTest {
use THello1;
use THello2; // E_NOTICE: TraitTest conflict, THello2(private $foo)
ignored, already declared THello1(public $foo)
use THello3; // E_NOTICE: TraitTest conflict, THello3(protected
$foo) ignored, already declared THello1(public $foo)
}
class TraitsTest2 {
use THello3;
use THello2; // E_NOTICE: ..
use THello1; // E_NOTICE: ..
}
It could be that the first property 'wins' and an E_NOTICE
is raised about
the property conflict.
Result:
class TraitsTest {
public $foo;
}
class TraitsTest2 {
protected $foo;
}
It would seem to fit php, though I'd be happy with simply E_FATAL until
people start using traits
The same for:
trait THelloA {
public $foo = 'a';
}
trait THelloB {
public $foo = 'b';
}
class TraitsTest3 {
use THelloA;
use THelloB; // E_NOTICE: TraitTest3 conflict, THelloB(public $foo)
ignored, already declared THelloA(public $foo)
}
class TraitsTest4 {
use THelloB;
use THelloA; // E_NOTICE: TraitTest3 conflict, THelloA(public $foo)
ignored, already declared THelloB(public $foo)
}
class TraitsTest5 {
public $foo = 'c';
use THelloB; // E_NOTICE: TraitTest3 conflict, THelloB(public $foo)
ignored, already declared TraitsTest5(public $foo)
use THelloA; // E_NOTICE: TraitTest3 conflict, THelloA(public $foo)
ignored, already declared THelloB(public $foo)
}
Hi Jonathan:
Does the order of the declaration matter?
No, the order does not matter, and that is one of the key points of traits compared to mixins or Python's way of multiple inheritance.
So, any kind of order-dependent solution would be inconsistent with the design of traits.
though I'd be happy with simply E_FATAL until
people start using traits
What do you mean by the second part? (until people start using traits)
Changing the design retrospectively does not seem to be the best option?
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
Hi Jonathan:
Does the order of the declaration matter?
No, the order does not matter, and that is one of the key points of
traits compared to mixins or Python's way of multiple inheritance.
So, any kind of order-dependent solution would be inconsistent with
the design of traits.though I'd be happy with simply E_FATAL until people start using
traits
What do you mean by the second part? (until people start using traits)
Changing the design retrospectively does not seem to be the best
option?
Most likely not the best option, I think I'm saying I prefer E_FATAL
But if users find it too restrictive / problematic, the auto-resolution
"merge to the most restrictive modifier" could be added in a next release or
an approach that's convenient to the code out there using traits &
properties. That seems better than the other way around.
- When the visibility collides, should we be folding to the most
restrictive or least restrictive? I'm not sure myself; I'm more
interested in your reasoning for going for most-restrictive.
So, in general, I think, there is no 'right thing' to do here, because
the definitions seem to be incompatible but that is only decidable at
the application-level.There is another, related, edge-case in the test case below. In the
test we have different initial values for the properties, which makes
it 'obvious' that they are incompatible. Thus, in that case I would
argue for aE_COMPILE_ERROR
instead of a notice.The reason to use the stricter modifier is, well, arbitrary.
At least I do not find an argument for either way that completely
convinces me. At the moment, the main reason is that 'public' can be
considered to be the default visibility modifier. Based on the
semantics of 'var' and dynamic properties. Thus, private tends to
imply that an additional constraint was consciously applied to the
property, which should be preserved.
I'd argue that traits should follow similar rules to normal class
inheritance. In that paradigm, you can override a previously defined
member by using equal or greater visibility only -- i.e., you can't
make it less visible (e.g., making a property declared as protected in
the parent class private in the extending class). With traits, it seems
like we should go with whatever is most visible, as then no matter
what traits are mixed in, access is guaranteed.
Well, but as I said, thats kind of arbitrary and if you look at the
inheritance rules and try to reason from the view of clients with
respect to the external interface, then going with public as the
chosen modifier also starts to sound reasonable...
Exactly. I wouldn't default to public on conflicts, though -- just with
the highest declared visibility (e.g., if one trait defines as private
and the other as protected, protected wins).
--
Matthew Weier O'Phinney
Project Lead | matthew@zend.com
Zend Framework | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Hi Matthew:
Exactly. I wouldn't default to public on conflicts, though -- just with
the highest declared visibility (e.g., if one trait defines as private
and the other as protected, protected wins).
I am currently actually implementing the most restricted proposal: all differences in the property definition will lead to a fatal error.
The reasoning behind this is, that the semantics of state is not predictable and all changes in the class/traits hierarchies which are incompatible should give the developer an immediate feedback, i.e., make potentially incompatible code break.
That is not the most 'dynamic' of all possible solutions but seems to fit with the rest of PHP.
What I have in mind is also not how the methods integrate into the inheritance chain, thus, a property in the body of the class does not override all property definitions in traits (this is the case for methods).
I think that will be useful for the very same reason. And well, I hope an educative error message will steer the crowed in the right direction to use accessors.
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
Exactly. I wouldn't default to public on conflicts, though -- just with
the highest declared visibility (e.g., if one trait defines as private
and the other as protected, protected wins).
I am currently actually implementing the most restricted proposal: all
differences in the property definition will lead to a fatal error.The reasoning behind this is, that the semantics of state is not
predictable and all changes in the class/traits hierarchies which are
incompatible should give the developer an immediate feedback, i.e.,
make potentially incompatible code break. That is not the most
'dynamic' of all possible solutions but seems to fit with the rest of
PHP.
That makes sense to me as well; having conflicting properties due to
multiple traits implementing them is a good way to lead to inconsistency
and difficult to test/predict code.
What I have in mind is also not how the methods integrate into the
inheritance chain, thus, a property in the body of the class does not
override all property definitions in traits (this is the case for
methods). I think that will be useful for the very same reason. And
well, I hope an educative error message will steer the crowed in the
right direction to use accessors.
--
Matthew Weier O'Phinney
Project Lead | matthew@zend.com
Zend Framework | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Exactly. I wouldn't default to public on conflicts, though -- just with
the highest declared visibility (e.g., if one trait defines as private
and the other as protected, protected wins).
I am currently actually implementing the most restricted proposal: all
differences in the property definition will lead to a fatal error.The reasoning behind this is, that the semantics of state is not
predictable and all changes in the class/traits hierarchies which are
incompatible should give the developer an immediate feedback, i.e.,
make potentially incompatible code break. That is not the most
'dynamic' of all possible solutions but seems to fit with the rest of
PHP.That makes sense to me as well; having conflicting properties due to
multiple traits implementing them is a good way to lead to inconsistency
and difficult to test/predict code.
I will agree up to a point. "Dude, this will probably break" is a
worthwhile message to give. At the same time, though, there does need
to be a way for the developer to say "I know that; trust me, I know what
I'm doing". Otherwise, having two traits that are supposed to operate
on the same base data will become needlessly complicated with
return-by-ref accessors that may also collide.
E.g., if I have three traits that all operate on an internal array, and
a dozen classes that use them, I do want class A to have traits 1 and 2,
class B to have traits 1 and 3, etc., without needing three extra
accessors lying around that serve no purpose other than to work around
an unnecessary PHP restriction. (Stack calls in PHP are not free, aside
from the ugly code that results in.)
Perhaps if both traits use the same variable name, visibility, and
default value then there is no error?
I suspect this issue dovetails with the Traits-and-interfaces thread
from earlier.
--Larry Garfield
Hi Larry:
Perhaps if both traits use the same variable name, visibility, and default value then there is no error?
There is not fatal error, however, currently there isE_STRICT
notice.
I suspect this issue dovetails with the Traits-and-interfaces thread from earlier.
Ehm, not sure what you want to get at.
The idea of expressing that the composing class needs to satisfy an interface, or perhaps inherit from a specific class still seems to have a number of valid use cases.
However, there was a single strong opinion against it, as far as I remember.
Anyway, on the topic of properties.
For the records, I updated the RFC with the following section.
http://wiki.php.net/rfc/horizontalreuse#handling_of_propertiesstate
Comments are welcome.
Thanks
Stefan
===== Handling of Properties/State =====
Traits do not provide any provisioning for handling state.
They are meant to provide a light-weight mechanism for flexible code reuse,
with the mean goal being to avoid code duplication.
Moreover, should not be confused with typical use cases of classes.
When a strong coherence/coupling between methods and state is required,
and certain invariants have to be maintained on the state, this is a good
indication that a class is the right abstraction to implement that problem
with.
However, every behavior needs state to operate on, otherwise it could be just
a static functional helper method.
Thus, trait code will either need to use accessors, which is favorite way to
go since it provides full traits semantics, or they use properties, which
is possible but rather a convenience feature.
Since state is a complex problem, and the knowledge about compatibility of
state form different traits is only present in a concrete composition, proper
state handling would need language features which are currently considered
beyond the scope of what is necessary for PHP. (See [[http://scg.unibe.ch/archive/papers/Berg07eStatefulTraits.pdf|Bergel et al]])
Thus, the goal for a consistent language design is to raise awareness of the
problem, promote the use of accessors, and break early in case the changes to
a trait is potentially problematic for a class using it. This results in the
following rules:
- 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.
This property handling was implemented in [[http://svn.php.net/viewvc?view=revision&revision=306476|SVN revision 306476]] and examples are given in the test cases.
--Larry Garfield
--
--
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
Hi Larry:
Perhaps if both traits use the same variable name, visibility, and
default value then there is no error?There is not fatal error, however, currently there is
E_STRICT
notice.I suspect this issue dovetails with the Traits-and-interfaces thread from
earlier.Ehm, not sure what you want to get at.
The idea of expressing that the composing class needs to satisfy an
interface, or perhaps inherit from a specific class still seems to have a
number of valid use cases. However, there was a single strong opinion
against it, as far as I remember.
I mean, for instance, if you're using an accessor method then you need that
accessor to exist, because you're hard coding its name. If you instead
provide the accessor yourself, the accessor will be hard coded to a variable
name, whether you provide it or not. So either way your trait will die if the
including class doesn't provide some supporting something.
Example:
Trait Foo1 {
function increment() {
// Implicit requirement that a class have a property named foo.
$this->foo++;
}
}
Trait Foo2 {
// Implicit requirement that a class NOT a property named foo.
protected $foo;
function increment() {
$this->foo++;
}
}
Trait Foo3 {
function increment() {
$foo = &$this->getFoo();
$foo++;
}
function &getFoo() {
// Implicit requirement that a class have a property named foo.
return $this->foo;
}
}
Trait Foo4 {
function increment() {
// Implicit requirement that a class have a method named getFoo().
$foo = &$this->getFoo();
$foo++;
}
}
class Test {
use Foo;
}
So one way or another, there is always an implicit requirement placed on the
using class. Implicit requirements suck. :-) If the answer to trait-based
properties is "if it breaks when you do that, don't do that" (which I don't
fully agree with, in part because of how ugly lots of return-by-ref methods
is), then we have to make the methods as easy as possible. Requiring an
interface is one proposed way to do that.
Reading the RFC over again, I actually see that there is support for abstract
methods in a trait. I suppose that serves a similar purpose of causing a
compile-time error (and thus something much more obvious to be fixed), and
becomes becomes a matter of taste to a degree.
I don't believe the RFC mentions how those resolve in case of collision,
though. If two traits define the same abstract method, does that cause a
collision that needs manual resolution or can the using class just define it
once and thereby support both traits?
--Larry Garfield
Hi Larry:
I don't believe the RFC mentions how those resolve in case of collision,
though. If two traits define the same abstract method, does that cause a
collision that needs manual resolution or can the using class just define it
once and thereby support both traits?
Abstract methods do not cause collisions, no. So, there can be an arbitrary number of traits asking for an abstract method with identical name, and the only thing that has to happen is that it is implemented eventually, perhaps by another trait.
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
===== Handling of Properties/State =====
This property handling was implemented in
[[http://svn.php.net/viewvc?view=revision&revision=306476|SVN revision
306476]] and examples are given in the test cases.
+1
The E_STRICT
warning seems to follow nicely the paper's view that a stateful
trait should be its own 'black-box':
http://scg.unibe.ch/archive/papers/Berg07eStatefulTraits.pdf
There are two remaining questions I have:
- How do traits affect the reflection API?
- Do we want to be able to declare trait requirements for properties and
methods? If so what syntax?
A note on the syntax proposed by Nathan:
trait require Foo
An option could be:
trait Foo {
require {
public $var;
function ratio();
}
function doFoo($v) {
$this->var = $v * $this->ratio();
}
}
trait Bar {
require interface Iterator;
}
The idea comes from:
http://code.google.com/p/es-lab/wiki/Traits
I found this trying to look for alternative keyword for 'require'.
Hi Jonathan:
Sorry, was not able to get back to those discussions earlier.
There are two remaining questions I have:
- How do traits affect the reflection API?
Johannes implemented some features in the Reflection API.
However, they are very ad-hoc, and there are some points which could be designed differently to make the concepts more clear.
On of those things is that you actually use ReflectionClass to reflect on a trait.
That is really an implementation detail, and should be changed to not confuse anyone on a conceptional level. We should not expose that kind of engine/implementation detail.
Thus, there remains stuff to be done about reflection. In general, I would like to be able to access all information that was in the source code. However, time constraints are an issue for me. If there would be a new release date for an alpha or something, I think I could get some time to work on it...
- Do we want to be able to declare trait requirements for properties and
methods? If so what syntax?
Well, there was the proposal to use require to express constraints for the composing class, which is something I liked.
However, I would not go to support properties, too, since I still maintain the opinion that we do not actually do something about state with regard to traits.
A note on the syntax proposed by Nathan:
trait require FooAn option could be:
trait Foo {
require {
public $var;
function ratio();
}
Hm, well, we already got abstract methods for the methods part.
trait Bar {
require interface Iterator;
}
And expressing the requirement for an interface or an abstract class seems to be the only thing missing, I think.
However, I would not put that in the body but into the hat.
trait Bar require OneSpecificClass, AndPossiblyAnInterface, OrPossiblyAnotherInterface {}
The idea comes from:
http://code.google.com/p/es-lab/wiki/TraitsI found this trying to look for alternative keyword for 'require'.
Yes, Tom worked on that a while ago, but it was to easy to implement it in a library for JavaScript. To easy to be considered for a language feature...
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
On of those things is that you actually use ReflectionClass to reflect
on a trait.
That is really an implementation detail, and should be changed to not
confuse anyone on a conceptional level. We should not expose that kind
of engine/implementation detail.
This is the same with interfaces. What does class_exists('some_trait')
do? - I assume that returns true too.
Thus, there remains stuff to be done about reflection. In general, I
would like to be able to access all information that was in the source
code. However, time constraints are an issue for me. If there would be
a new release date for an alpha or something, I think I could get some
time to work on it...
I hoped to do some work on it over Christmas vacation, too, but did
other stuff in the end ...
johannes
On of those things is that you actually use ReflectionClass to reflect
on a trait.
That is really an implementation detail, and should be changed to not
confuse anyone on a conceptional level. We should not expose that kind
of engine/implementation detail.This is the same with interfaces. What does class_exists('some_trait')
do? - I assume that returns true too.
It does return false for interfaces, that should be consistent and return false for traits, too.
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
Hi:
This is the same with interfaces. What does class_exists('some_trait')
do? - I assume that returns true too.
It does return false for interfaces, that should be consistent and return false for traits, too.
Ok, that is fixed and I added atrait_exists()
to match the other functions.
Will add a note to the RFC.
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
Hi:
From my point of view the right thing to do with regard to properties is defined in the test cases below.
The rational behind providing this semantics is based on the fact that PHP allows to define properties dynamically anyway, so there is no way around properties.
However, there should be a way that a developer can notice that the code might not behave as expected in the composed class.
It is true that behavior needs state to operate on, however, accessors are a common pattern and fully supported by traits. Furthermore, traits are not supposed to replace classes, and when a trait does more than just providing code that is to be easily reused, then the designed should ask the question whether that is not actually a class, which then provides the necessary guarantees to enforce the invariances the code expects.
Thus, I would like to keep traits as a lightweight concept for code reuse.
Best regards
Stefan
--TEST--
Conflicting properties should result in a notice.
Property use is discorage for traits that are supposed to enable maintainable
code reuse. Accessor methods are the language supported idiom for this.
--FILE--
<?php
error_reporting(E_ALL);
trait THello1 {
public $foo;
}
trait THello2 {
private $foo;
}
class TraitsTest {
use THello1;
use THello2;
}
var_dump(property_exists('TraitsTest', 'foo'));
?>
--EXPECTF--
Notice: Trait THello1 and THello2 define the same property in the composition of TraitsTest. This might be incompatible, to improve maintainability consider using accessor methods instead. Class was composed in %s on line %d.
bool(true)
--TEST--
Non-conflicting properties should work just fine.
--FILE--
<?php
error_reporting(E_ALL);
trait THello1 {
public $hello = "hello";
}
trait THello2 {
private $world = "World!";
}
class TraitsTest {
use THello1;
use THello2;
function test() {
echo $this->hello . ' ' . $this->world;
}
}
var_dump(property_exists('TraitsTest', 'hello'));
var_dump(property_exists('TraitsTest', 'world'));
$t = new TraitsTest;
$t->test();
?>
--EXPECTF--
bool(true)
bool(true)
hello World!
--TEST--
Conflicting properties with different visibility modifiers should be merged
to the most restrictive modifier.
--FILE--
<?php
error_reporting(E_ALL);
trait THello1 {
public $hello;
}
trait THello2 {
private $hello;
}
class TraitsTest {
use THello1;
use THello2;
}
$t = new TraitsTest;
$t->hello = "foo";
?>
--EXPECTF--
Fatal error: Cannot access private property TraitsTest::$foo in %s on line %d
Hi:
Traits do not provide any special provisioning for handling properties, especially, there is no language solution for handling colliding property names.
The current solution/idiom for handling state safely in a trait is to use either abstract set/get methods or an abstract get that returns a reference to the property in the class.However, at the moment it is possible to define properties in a trait:
trait Foo {
private $a;
public $foo;
}For the moment, that information is completely ignored, thus:
class Bar {
use Foo;
}
property_exists('Bar', 'a') === falseWell, and that is a rather inconsistent status-quo.
I would like to have that fixed in one or another way.
One possibility would be to forbid property definition in a trait altogether.
That reduces a bit the possibility to have wrong expectations about properties, however, the dynamic property creation is still possible.Another way would be to merge the properties in the composing class.
The question here would be how to treat visibility modifiers: how to merge public and private, should it result in public, or private?
And, to discorage users to go this way, should there be a STRICT notice? Options here are a notice whenever a property is defined in a trait, or whenever properties are silently merged.Comments very welcome.
Thanks
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--
--
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
Am 16.12.2010 16:25, schrieb Stefan Marr:
From my point of view the right thing to do with regard to
properties is defined in the test cases below.
+1
--
Sebastian Bergmann Co-Founder and Principal Consultant
http://sebastian-bergmann.de/ http://thePHP.cc/
I like it,
additionally if you want to prevent conflicts from the start you can
always go and define prefixed property names like below and use a getter to
access them conveniently.
trait Foo
{
private $Foo_prop;
public function getProp()
{
return $this->Foo_prop;
}
}
I am really looking forward to traits in 5.4!
greetings,
Benjamin
On Thu, 16 Dec 2010 16:25:42 +0100, Stefan Marr php@stefan-marr.de
wrote:
Hi:
From my point of view the right thing to do with regard to properties is
defined in the test cases below.The rational behind providing this semantics is based on the fact that
PHP
allows to define properties dynamically anyway, so there is no way
around
properties.
However, there should be a way that a developer can notice that the code
might not behave as expected in the composed class.It is true that behavior needs state to operate on, however, accessors
are
a common pattern and fully supported by traits. Furthermore, traits are
not
supposed to replace classes, and when a trait does more than just
providing
code that is to be easily reused, then the designed should ask the
question
whether that is not actually a class, which then provides the necessary
guarantees to enforce the invariances the code expects.Thus, I would like to keep traits as a lightweight concept for code
reuse.Best regards
Stefan--TEST--
Conflicting properties should result in a notice.
Property use is discorage for traits that are supposed to enable
maintainable
code reuse. Accessor methods are the language supported idiom for this.
--FILE--
<?php
error_reporting(E_ALL);trait THello1 {
public $foo;
}trait THello2 {
private $foo;
}class TraitsTest {
use THello1;
use THello2;
}var_dump(property_exists('TraitsTest', 'foo'));
?>
--EXPECTF--
Notice: Trait THello1 and THello2 define the same property in the
composition of TraitsTest. This might be incompatible, to improve
maintainability consider using accessor methods instead. Class was
composed
in %s on line %d.bool(true)
--TEST--
Non-conflicting properties should work just fine.
--FILE--
<?php
error_reporting(E_ALL);trait THello1 {
public $hello = "hello";
}trait THello2 {
private $world = "World!";
}class TraitsTest {
use THello1;
use THello2;
function test() {
echo $this->hello . ' ' . $this->world;
}
}var_dump(property_exists('TraitsTest', 'hello'));
var_dump(property_exists('TraitsTest', 'world'));$t = new TraitsTest;
$t->test();
?>
--EXPECTF--
bool(true)
bool(true)hello World!
--TEST--
Conflicting properties with different visibility modifiers should be
merged
to the most restrictive modifier.
--FILE--
<?php
error_reporting(E_ALL);trait THello1 {
public $hello;
}trait THello2 {
private $hello;
}class TraitsTest {
use THello1;
use THello2;
}$t = new TraitsTest;
$t->hello = "foo";
?>
--EXPECTF--
Fatal error: Cannot access private property TraitsTest::$foo in %s on
line
%dHi:
Traits do not provide any special provisioning for handling properties,
especially, there is no language solution for handling colliding
property
names.
The current solution/idiom for handling state safely in a trait is to
use either abstract set/get methods or an abstract get that returns a
reference to the property in the class.However, at the moment it is possible to define properties in a trait:
trait Foo {
private $a;
public $foo;
}For the moment, that information is completely ignored, thus:
class Bar {
use Foo;
}
property_exists('Bar', 'a') === falseWell, and that is a rather inconsistent status-quo.
I would like to have that fixed in one or another way.
One possibility would be to forbid property definition in a trait
altogether.
That reduces a bit the possibility to have wrong expectations about
properties, however, the dynamic property creation is still possible.Another way would be to merge the properties in the composing class.
The question here would be how to treat visibility modifiers: how to
merge public and private, should it result in public, or private?
And, to discorage users to go this way, should there be a STRICT
notice?
Options here are a notice whenever a property is defined in a trait, or
whenever properties are silently merged.Comments very welcome.
Thanks
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--
--
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
From my point of view the right thing to do with regard to properties
is defined in the test cases below.The rational behind providing this semantics is based on the fact that
PHP allows to define properties dynamically anyway, so there is no way
around properties. However, there should be a way that a developer
can notice that the code might not behave as expected in the composed
class.
+1
The test cases you include are exactly how I would expect traits to work
with regards to properties.
--
Matthew Weier O'Phinney
Project Lead | matthew@zend.com
Zend Framework | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
2010/12/16 Stefan Marr php@stefan-marr.de:
Hi:
From my point of view the right thing to do with regard to properties is defined in the test cases below.
The rational behind providing this semantics is based on the fact that PHP allows to define properties dynamically anyway, so there is no way around properties.
However, there should be a way that a developer can notice that the code might not behave as expected in the composed class.It is true that behavior needs state to operate on, however, accessors are a common pattern and fully supported by traits. Furthermore, traits are not supposed to replace classes, and when a trait does more than just providing code that is to be easily reused, then the designed should ask the question whether that is not actually a class, which then provides the necessary guarantees to enforce the invariances the code expects.
Thus, I would like to keep traits as a lightweight concept for code reuse.
Best regards
Stefan--TEST--
Conflicting properties should result in a notice.
Property use is discorage for traits that are supposed to enable maintainable
code reuse. Accessor methods are the language supported idiom for this.
--FILE--
<?php
error_reporting(E_ALL);trait THello1 {
public $foo;
}trait THello2 {
private $foo;
}class TraitsTest {
use THello1;
use THello2;
}var_dump(property_exists('TraitsTest', 'foo'));
?>
--EXPECTF--
Notice: Trait THello1 and THello2 define the same property in the composition of TraitsTest. This might be incompatible, to improve maintainability consider using accessor methods instead. Class was composed in %s on line %d.
In this test, you might want to display some text before the
var_dump()
(or alternatively to hardcode the line number on last line)
to ensure it is the property_exists()
function call or the class
definition which would create such a notice.
Regards,
Patrick
--
Patrick Allaert
http://code.google.com/p/peclapm/ - Alternative PHP Monitor
I am fine with this approach, with 2 caveats:
-
If you actually do want to make two traits use the same property, it
looks like the answer here is "Either have no property and demand the
existence of an accessor that returns by reference, or you can't write
E_NOTICE-safe code". Is that true? -
When the visibility collides, should we be folding to the most
restrictive or least restrictive? I'm not sure myself; I'm more
interested in your reasoning for going for most-restrictive.
--Larry Garfield
Hi:
From my point of view the right thing to do with regard to properties is defined in the test cases below.
The rational behind providing this semantics is based on the fact that PHP allows to define properties dynamically anyway, so there is no way around properties.
However, there should be a way that a developer can notice that the code might not behave as expected in the composed class.It is true that behavior needs state to operate on, however, accessors are a common pattern and fully supported by traits. Furthermore, traits are not supposed to replace classes, and when a trait does more than just providing code that is to be easily reused, then the designed should ask the question whether that is not actually a class, which then provides the necessary guarantees to enforce the invariances the code expects.
Thus, I would like to keep traits as a lightweight concept for code reuse.
Best regards
Stefan--TEST--
Conflicting properties should result in a notice.
Property use is discorage for traits that are supposed to enable maintainable
code reuse. Accessor methods are the language supported idiom for this.
--FILE--
<?php
error_reporting(E_ALL);trait THello1 {
public $foo;
}trait THello2 {
private $foo;
}class TraitsTest {
use THello1;
use THello2;
}var_dump(property_exists('TraitsTest', 'foo'));
?>
--EXPECTF--
Notice: Trait THello1 and THello2 define the same property in the composition of TraitsTest. This might be incompatible, to improve maintainability consider using accessor methods instead. Class was composed in %s on line %d.bool(true)
--TEST--
Non-conflicting properties should work just fine.
--FILE--
<?php
error_reporting(E_ALL);trait THello1 {
public $hello = "hello";
}trait THello2 {
private $world = "World!";
}class TraitsTest {
use THello1;
use THello2;
function test() {
echo $this->hello . ' ' . $this->world;
}
}var_dump(property_exists('TraitsTest', 'hello'));
var_dump(property_exists('TraitsTest', 'world'));$t = new TraitsTest;
$t->test();
?>
--EXPECTF--
bool(true)
bool(true)hello World!
--TEST--
Conflicting properties with different visibility modifiers should be merged
to the most restrictive modifier.
--FILE--
<?php
error_reporting(E_ALL);trait THello1 {
public $hello;
}trait THello2 {
private $hello;
}class TraitsTest {
use THello1;
use THello2;
}$t = new TraitsTest;
$t->hello = "foo";
?>
--EXPECTF--
Fatal error: Cannot access private property TraitsTest::$foo in %s on line %dHi:
Traits do not provide any special provisioning for handling properties, especially, there is no language solution for handling colliding property names.
The current solution/idiom for handling state safely in a trait is to use either abstract set/get methods or an abstract get that returns a reference to the property in the class.However, at the moment it is possible to define properties in a trait:
trait Foo {
private $a;
public $foo;
}For the moment, that information is completely ignored, thus:
class Bar {
use Foo;
}
property_exists('Bar', 'a') === falseWell, and that is a rather inconsistent status-quo.
I would like to have that fixed in one or another way.
One possibility would be to forbid property definition in a trait altogether.
That reduces a bit the possibility to have wrong expectations about properties, however, the dynamic property creation is still possible.Another way would be to merge the properties in the composing class.
The question here would be how to treat visibility modifiers: how to merge public and private, should it result in public, or private?
And, to discorage users to go this way, should there be a STRICT notice? Options here are a notice whenever a property is defined in a trait, or whenever properties are silently merged.Comments very welcome.
Thanks
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
Hi:
Traits do not provide any special provisioning for handling properties, especially, there is no language solution for handling colliding property names.
The current solution/idiom for handling state safely in a trait is to use either abstract set/get methods or an abstract get that returns a reference to the property in the class.However, at the moment it is possible to define properties in a trait:
trait Foo {
private $a;
public $foo;
}For the moment, that information is completely ignored, thus:
class Bar {
use Foo;
}
property_exists('Bar', 'a') === falseWell, and that is a rather inconsistent status-quo.
I would like to have that fixed in one or another way.
One possibility would be to forbid property definition in a trait altogether.
That reduces a bit the possibility to have wrong expectations about properties, however, the dynamic property creation is still possible.Another way would be to merge the properties in the composing class.
The question here would be how to treat visibility modifiers: how to merge public and private, should it result in public, or private?
And, to discorage users to go this way, should there be a STRICT notice? Options here are a notice whenever a property is defined in a trait, or whenever properties are silently merged.Comments very welcome.
Thanks
Stefan
What about extending the way that traits resolve method conflicts to
solve property conflicts in a similar fashion? I can't remember if it's
already been suggested and and maybe shot down already. It would
probably get horrendously messy, but figured I'd mention it anyway.
Cheers,
David
Hi:
Traits do not provide any special provisioning for handling properties,
especially, there is no language solution for handling colliding property names.
The current solution/idiom for handling state safely in a trait is to use either
abstract set/get methods or an abstract get that returns a reference to the
property in the class.However, at the moment it is possible to define properties in a trait:
trait Foo {
private $a;
public $foo;
}For the moment, that information is completely ignored, thus:
class Bar {
use Foo;
}
property_exists('Bar', 'a') === falseWell, and that is a rather inconsistent status-quo.
I would like to have that fixed in one or another way.
One possibility would be to forbid property definition in a trait altogether.
That reduces a bit the possibility to have wrong expectations about properties,
however, the dynamic property creation is still possible.Another way would be to merge the properties in the composing class.
The question here would be how to treat visibility modifiers: how to merge
public and private, should it result in public, or private?
And, to discorage users to go this way, should there be a STRICT notice? Options
here are a notice whenever a property is defined in a trait, or whenever
properties are silently merged.Comments very welcome.
Thanks
StefanWhat about extending the way that traits resolve method conflicts to solve
property conflicts in a similar fashion? I can't remember if it's already been
suggested and and maybe shot down already. It would probably get horrendously
messy, but figured I'd mention it anyway.Cheers,
David
I'm a latecomer here, but...
Stefan, doesn't this conflict with what you've written here (and the
test cases in SVN)?:
http://wiki.php.net/rfc/horizontalreuse#handling_of_propertiesstate
Or is what is happening here that the properties in traits are treated
essentially as declarations rather than definitions, triggering errors
but not actually creating properties, and you think they should actually
create properties?
Ben.
Hi Ben:
I'm a latecomer here, but...
Stefan, doesn't this conflict with what you've written here (and the
test cases in SVN)?:http://wiki.php.net/rfc/horizontalreuse#handling_of_propertiesstate
Or is what is happening here that the properties in traits are treated
essentially as declarations rather than definitions, triggering errors
but not actually creating properties, and you think they should actually
create properties?
Sorry, I do not understand.
Is the text in the RFC contradicting or not clear enough about what the test cases show?
The intention was to provide the developer with hints when it is possible that state is incompatible.
Until now, the reason why there is not fancy mechanism for conflict resolution for properties in traits is, first, the dynamic nature of PHP which makes certain things like 'renaming' inconsistent with the rest of the language, especially its meta-programming facilities, and second, the added problem with state, that you actually have many usecases where the state needs to be merged. However, merging behavior is not possible, which simplifies the language constructs for handling behavioral conflicts.
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
Hi, Stefan,
Sorry, I do not understand.
Haha. Now we are both confused!
In this email thread you seemed to be saying that properties defined in
traits are completely ignored, but in the RFC and svn it seems to be
saying that properties in traits are not ignored, but are merged into
the class and/or trigger errors/warnings. So, which is it? Ignored or
not? Or is some aspect of them ignored and some aspect not?
I hope I'm not making things more confusing....
Cheers,
Ben.
Hi Ben:
In this email thread you seemed to be saying that properties defined in
traits are completely ignored, but in the RFC and svn it seems to be
saying that properties in traits are not ignored, but are merged into
the class and/or trigger errors/warnings. So, which is it? Ignored or
not? Or is some aspect of them ignored and some aspect not?
The RFC was discussed on this list, and the RFC should explain what the current state of the implementation is. So, the RFC and the implementation are the specifications. In case they differ it has to be fixed.
With regard to state, from my academic point of view, it is not handled, however, the language should be now in a shape which provides my interpretation of 'expected behavior' with regard to property definitions.
When they collide, you get a notice, but the language does not help you in solving the problem.
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
Hi, Stefan,
In this email thread you seemed to be saying that properties defined in
traits are completely ignored, but in the RFC and svn it seems to be
saying that properties in traits are not ignored, but are merged into
the class and/or trigger errors/warnings. So, which is it? Ignored or
not? Or is some aspect of them ignored and some aspect not?The RFC was discussed on this list, and the RFC should explain what
the current state of the implementation is. So, the RFC and the
implementation are the specifications. In case they differ it has to
be fixed.
OK. So this comment from your email is outdated?:
However, at the moment it is possible to define properties in a trait:
trait Foo {
private $a;
public $foo;
}For the moment, that information is completely ignored, thus:
class Bar {
use Foo;
}
property_exists('Bar', 'a') === false
in light of this test:
Maybe the only thing that was confusing is that somebody replied to an
old email?
Ben.
Hi Ben:
OK. So this comment from your email is outdated?:
Yes, outdated since this email:
http://marc.info/?l=php-internals&m=129288735205036&w=2
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
Hi Ben:
OK. So this comment from your email is outdated?:
Yes, outdated since this email:
http://marc.info/?l=php-internals&m=129288735205036&w=2Best regards
Stefan
OK, Stefan, I just got confused by reading your outdated message that was quoted
in a recent reply, and by the fact that the "last update" date of 2010-11-18 at
the top of the RFC is inaccurate, so I assumed the email was more current than it
was. Seems like the RFC was actually updated circa 2010-12-20. :-)
Sorry for the noise, guys.
Ben.