Hi all,
If a protected static property is overridden by a public static
property, both properties share the same value:
<?php
class A {
protected static $a;
public static function test() {
A::$a = 'A::$a';
echo A::$a . "\n"; // Prints 'A::$a'
echo B::$a . "\n"; // Also prints 'A::$a', because
A::$a and B::$a share values.
}
}
class B extends A {
public static $a = 'B::$a';
}
A::test();
?>
This behaviour feels strange to me, because:
-
It only applies to that specific combination of visibility
modifiers. If you change A::$a to public OR B::$a to protected, then
A::$a and B::$a are treated as two separate entities. See
pastebin.com/fca2cd5b and pastebin.com/f4f94b32d . -
It is inconsistent with the behaviour of static methods. See:
pastebin.com/f27f009c4 . -
It differs from the behaviours of C#, Java and C++ (whereas, in
most other respects, PHP and these languages share a lot of common
ground regarding the concepts of visibility & static-ness). See:
http://pastebin.ca/871576, http://pastebin.ca/871975 and
http://pastebin.ca/871583.
The code for this behaviour was added to zend_compile.c in rev 1.474
(back in sept 2003 :). If I change it to ensure that protected A::$a
and public B::$a are treated as separate entities (quick patch against
5.2 snap here: pastebin.com/f7f175924 ), the only tests that fail are
those specifically designed to check for this behaviour:
Zend/tests/errmsg_024.phpt and
ext/reflection/tests/static_properties_002.phpt.
Is this a bug? If not, could you help me understand the decision to go
with this inheritance rule?
Many thanks!
Robin
Hello Robin,
expected behavior. The static property is inherited to the sub class. As
that subclass repeats the declaration with a different visibility you simply
change the visibility. If the base class had the property defined as private
then the property is private to that specific class and not directly
accessible by derived classes that is it's name gets prefixed with the class
name. So in:
class A { private static $p; } class B extends A { private static $p; }
we have two different properties:
- A::p
- B::p
while in
class A { protected static $p; } class B extends A { public static $p; }
we get just p in A, which then gets into B, so that the public line in p
simply changes the visibility as described above.
marcus
Monday, January 28, 2008, 2:20:56 PM, you wrote:
Hi all,
If a protected static property is overridden by a public static
property, both properties share the same value:
<?php
class A {
protected static $a;
public static function test() {
A::$a = 'A::$a';
echo A::$a . "\n"; // Prints 'A::$a'
echo B::$a . "\n"; // Also prints 'A::$a', because
A::$a and B::$a share values.
}
}
class B extends A {
public static $a = 'B::$a';
}
A::test();
?>>
This behaviour feels strange to me, because:
- It only applies to that specific combination of visibility
modifiers. If you change A::$a to public OR B::$a to protected, then
A::$a and B::$a are treated as two separate entities. See
pastebin.com/fca2cd5b and pastebin.com/f4f94b32d .
- It is inconsistent with the behaviour of static methods. See:
pastebin.com/f27f009c4 .
- It differs from the behaviours of C#, Java and C++ (whereas, in
most other respects, PHP and these languages share a lot of common
ground regarding the concepts of visibility & static-ness). See:
http://pastebin.ca/871576, http://pastebin.ca/871975 and
http://pastebin.ca/871583.
The code for this behaviour was added to zend_compile.c in rev 1.474
(back in sept 2003 :). If I change it to ensure that protected A::$a
and public B::$a are treated as separate entities (quick patch against
5.2 snap here: pastebin.com/f7f175924 ), the only tests that fail are
those specifically designed to check for this behaviour:
Zend/tests/errmsg_024.phpt and
ext/reflection/tests/static_properties_002.phpt.
Is this a bug? If not, could you help me understand the decision to go
with this inheritance rule?
Many thanks!
Robin
Best regards,
Marcus
Hi Marcus,
Thanks for the prompt reply and explanation. I have some further
questions though:
If the base class had the property defined as private
then the property is private to that specific class and not directly
accessible by derived classes that is it's name gets prefixed with the class
name.. So in:
class A { private static $p; } class B extends A { private static $p; }
we have two different properties:
Understood. But if we have two separate properties for the reason that
A::$p is not visible to B, then how about these cases?
class A { protected static $p; } class B extends A { protected static $p; }
class A { public static $p; } class B extends A { public static $p; }
In both of those cases, A::$p is visible to the derived class, but the
re-declaration results in A::$p and B::$p being two separate
properties (see pastebin.com/fca2cd5b and pastebin.com/f4f94b32d for a
demonstration). This is one of the reasons I find the case where we
end up with only one property value to be surprising.
Another reason is that, as illustrated in my previous post, PHP's
behaviour doesn't seem to correlate with the inheritance rules of
other languages I'm familiar with: you always end up with two distinct
static properties in Java, C++ and C# (though of course I understand
this fact on its own is does not mean PHP is wrong :).
Lastly, with overridden static methods, PHP always yields two distinct
methods, regardless of the visibility modifiers. See
http://pastebin.com/f27f009c4 . Granted, with methods any other
behaviour would be very odd indeed, but it does emphasize an
inconsistency between method and property inheritance rules in PHP.
So for now I continue to feel this is a little strange. Any further
explanations would be greatly appreciated. :)
Thanks,
Robin
Best regards,
Marcus
Hi All,
A property can share it's value with it's parent but methods need to call
parent::$method() and this is not done by default that is why the behavior
is different. Is that the right behavior?! I really don't know. I think the
inheritance of the static methods as two distinct methods is right
regardless any visibility modifiers. So if something needs to be patched it
need to be in property inheritance rules, or just accept the evil reality :)
Best Regards, Dimitar Isusov
Hello Robin,
I checked it out in more detail and it is indeed broken as in it is not
consistent:
[marcus@zaphod PHP_5_3]$ php -r 'class A { protected static $p=1; } class B extends A { protected static $p=2; } ReflectionClass::Export("B");'
-> works == 2 properties
-> but should fail because of changed value
[marcus@zaphod PHP_5_3]$ php -r 'class A { protected static $p=1; } class B extends A { public static $p=2; } ReflectionClass::Export("B");'
Fatal error: Cannot change initial value of property static protected A::$p in class B in Command line code on line 1
[marcus@zaphod PHP_5_3]$ php -r 'class A { public static $p=1; } class B extends A { protected static $p=2; } ReflectionClass::Export("B");'
Fatal error: Access level to B::$p must be public (as in class A) in Command line code on line 1
[marcus@zaphod PHP_5_3]$ php -r 'class A { public static $p=1; } class B extends A { public static $p=2; } ReflectionClass::Export("B");'
-> works == 2 properties
-> but should fail becasue of changed value
So we need to fix this.
marcus
Monday, January 28, 2008, 4:18:50 PM, you wrote:
Hi Marcus,
Thanks for the prompt reply and explanation. I have some further
questions though:
If the base class had the property defined as private
then the property is private to that specific class and not directly
accessible by derived classes that is it's name gets prefixed with the class
name.. So in:
class A { private static $p; } class B extends A { private static $p; }
we have two different properties:
Understood. But if we have two separate properties for the reason that
A::$p is not visible to B, then how about these cases?
class A { protected static $p; } class B extends A { protected static $p; }
class A { public static $p; } class B extends A { public static $p; }
In both of those cases, A::$p is visible to the derived class, but the
re-declaration results in A::$p and B::$p being two separate
properties (see pastebin.com/fca2cd5b and pastebin.com/f4f94b32d for a
demonstration). This is one of the reasons I find the case where we
end up with only one property value to be surprising.
Another reason is that, as illustrated in my previous post, PHP's
behaviour doesn't seem to correlate with the inheritance rules of
other languages I'm familiar with: you always end up with two distinct
static properties in Java, C++ and C# (though of course I understand
this fact on its own is does not mean PHP is wrong :).
Lastly, with overridden static methods, PHP always yields two distinct
methods, regardless of the visibility modifiers. See
http://pastebin.com/f27f009c4 . Granted, with methods any other
behaviour would be very odd indeed, but it does emphasize an
inconsistency between method and property inheritance rules in PHP.
So for now I continue to feel this is a little strange. Any further
explanations would be greatly appreciated. :)
Thanks,
Robin
Best regards,
Marcus
Best regards,
Marcus
Marcus Boerger schreef:
Hello Robin,
I checked it out in more detail and it is indeed broken as in it is not
consistent:
[marcus@zaphod PHP_5_3]$ php -r 'class A { protected static $p=1; } class B extends A { protected static $p=2; } ReflectionClass::Export("B");'
-> works == 2 properties
-> but should fail because of changed value[marcus@zaphod PHP_5_3]$ php -r 'class A { protected static $p=1; } class B extends A { public static $p=2; } ReflectionClass::Export("B");'
Fatal error: Cannot change initial value of property static protected A::$p in class B in Command line code on line 1[marcus@zaphod PHP_5_3]$ php -r 'class A { public static $p=1; } class B extends A { protected static $p=2; } ReflectionClass::Export("B");'
Fatal error: Access level to B::$p must be public (as in class A) in Command line code on line 1[marcus@zaphod PHP_5_3]$ php -r 'class A { public static $p=1; } class B extends A { public static $p=2; } ReflectionClass::Export("B");'
-> works == 2 properties
-> but should fail becasue of changed valueSo we need to fix this.
I don't get the 'but should fail because of changed value' - isn't the changed value akin
to overloading a method - where the body of the method changes (obviously)?
I also surmise that the issue becomes more tricky if you consider late static binding,
which I am assuming is still scheduled for php6, and as such it may be worth holding back
making a fix until it is clearer what the larger ramifications are?
marcus
Monday, January 28, 2008, 4:18:50 PM, you wrote:
Hi Marcus,
Thanks for the prompt reply and explanation. I have some further
questions though:If the base class had the property defined as private
then the property is private to that specific class and not directly
accessible by derived classes that is it's name gets prefixed with the class
name.. So in:
class A { private static $p; } class B extends A { private static $p; }
we have two different properties:Understood. But if we have two separate properties for the reason that
A::$p is not visible to B, then how about these cases?
class A { protected static $p; } class B extends A { protected static $p; }
class A { public static $p; } class B extends A { public static $p; }In both of those cases, A::$p is visible to the derived class, but the
re-declaration results in A::$p and B::$p being two separate
properties (see pastebin.com/fca2cd5b and pastebin.com/f4f94b32d for a
demonstration). This is one of the reasons I find the case where we
end up with only one property value to be surprising.Another reason is that, as illustrated in my previous post, PHP's
behaviour doesn't seem to correlate with the inheritance rules of
other languages I'm familiar with: you always end up with two distinct
static properties in Java, C++ and C# (though of course I understand
this fact on its own is does not mean PHP is wrong :).Lastly, with overridden static methods, PHP always yields two distinct
methods, regardless of the visibility modifiers. See
http://pastebin.com/f27f009c4 . Granted, with methods any other
behaviour would be very odd indeed, but it does emphasize an
inconsistency between method and property inheritance rules in PHP.So for now I continue to feel this is a little strange. Any further
explanations would be greatly appreciated. :)Thanks,
RobinBest regards,
MarcusBest regards,
Marcus
Marcus,
Thanks for looking in more detail.
I checked it out in more detail and it is indeed broken as in it is not
consistent:
[marcus@zaphod PHP_5_3]$ php -r 'class A { protected static $p=1; } class B extends A { protected static $p=2; } ReflectionClass::Export("B");'
-> works == 2 properties
-> but should fail because of changed value[marcus@zaphod PHP_5_3]$ php -r 'class A { protected static $p=1; } class B extends A { public static $p=2; } ReflectionClass::Export("B");'
Fatal error: Cannot change initial value of property static protected A::$p in class B in Command line code on line 1[marcus@zaphod PHP_5_3]$ php -r 'class A { public static $p=1; } class B extends A { protected static $p=2; } ReflectionClass::Export("B");'
Fatal error: Access level to B::$p must be public (as in class A) in Command line code on line 1[marcus@zaphod PHP_5_3]$ php -r 'class A { public static $p=1; } class B extends A { public static $p=2; } ReflectionClass::Export("B");'
-> works == 2 properties
-> but should fail becasue of changed valueSo we need to fix this.
I agree with Jochem: I'm not fully confident about the statement 'but
should fail because of changed value', for two reasons:
- The issues doesn't stop with default values. Consider the case
below. If we were go with your suggested fix, the parent and child
class "feel" like they have separate properties, but in fact point to
the same value. It is imho more natural for the two classes to
genuinely have separate values. Another alternative would be to forbid
redeclaring statics with the same visibility modifier altogther
(default value or no default value), though I would personally be less
happy with that.
<?php
Class Frog {
public static $colour;
}
Class SpecialFrog extends Frog {
public static $colour;
}
Frog::$colour = 'green';
SpecialFrog::$colour = 'blue';
var_dump(Frog::$colour, SpecialFrog::$colour);
// string(4) "blue"
// string(4) "blue"
?>
- The suggested fix would create an inconsistency with class
constants, where it is legal to shadow inherited values:
<?php
Class Frog {
const COLOUR = 'green';
}
Class SpecialFrog extends Frog {
const COLOUR = 'blue';
}
var_dump(Frog::COLOUR, SpecialFrog::COLOUR);
// string(5) "green"
// string(4) "blue"
?>
Many thanks,
Robin
Hi Marcus,
I checked it out in more detail and it is indeed broken as in it is not
consistent
If possible, I'd like to revive this discussion. Patch included. :)
History: http://thread.gmane.org/gmane.comp.php.devel/47956/focus=47956
To summarize, we agree that there is an inconsistency related to
static property inheritance, because:
--> In the following case, A::$p and B::$p share the same value:
class A { protected static $p; } class B extends A { public static $p; }
--> Yet in the following cases, A::$p and B::$p have two separate values:
class A { protected static $p; } class B extends A { protected static $p; }
class A { public static $p; } class B extends A { public static $p; }
You originally suggested fixing the inconsistency by changing the last
2 cases, so that A::$p and B::$p always share the same value. However,
that approach raised some concerns described by myself and Jochem, for
example:
--> Inconsistency with overridden methods
--> Inconsistency with shadowed class constants.
Consequently, I'd like to propose resolving this by changing the first
case, so that A::$p and B::$p always have separate values.
Here's a patch that implements this. It includes relevant changes to
existing tests, plus some supplementary tests to address Jochem's
concern about the impact on LSB.
Patch against snap php6.0-200802251530: http://pastebin.ca/919094
Disclaimer: I am not an internals expert - patch might overlook some
cases, feedback welcome. :)
Let me know what you think.
Regards,
Robin
I checked it out in more detail and it is indeed broken as in it is not
consistentIf possible, I'd like to revive this discussion. Patch included. :)
For the record, with helly's agreement I committed the patch to 5_3
and HEAD - thanks helly!
Ilia / others,
How would you feel about me back porting this to 5_2?
The purpose of the change is to make static property inheritance rules
more consistent - both amongst themselves and compared to static
method inheritance rules.
The patch does introduce a small change in behaviour (the old
behaviour is imho buggy). See previous posts in this thread for the
details ( http://turl.ca/phhhf ) and here for an example:
http://pastebin.ca/926106 .
I know at least a couple of people would rather this fix be 5.3+ only.
More opinions welcome!
Cheers,
Robin