Skip to content

Commit

Permalink
Fix #69804: ::getStaticPropertyValue() throws on protected props
Browse files Browse the repository at this point in the history
`ReflectionClass` allows reading of the values of private and protected
constants, and also to get private and protected static methods.
Therefore getting the values of private and protected static properties
is also permissible, especially since `::getStaticProperties()` already
allows to do so.

We also allow ::setStaticPropertyValue() to modify private and
protected properties, because otherwise this method is useless, as
modifying public properties can be done directly.
  • Loading branch information
cmb69 committed Jun 24, 2020
1 parent ef2130d commit 26aefb7
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 37 deletions.
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ PHP NEWS
- Reflection:
. Fixed bug #79487 (::getStaticProperties() ignores property modifications).
(cmb, Nikita)
. Fixed bug #69804 ()::getStaticPropertyValue() throws on protected props).
(cmb, Nikita)

?? ??? 2020, PHP 7.4.8

Expand Down
14 changes: 11 additions & 3 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -3814,7 +3814,7 @@ ZEND_METHOD(reflection_class, getStaticProperties)
ZEND_METHOD(reflection_class, getStaticPropertyValue)
{
reflection_object *intern;
zend_class_entry *ce;
zend_class_entry *ce, *old_scope;
zend_string *name;
zval *prop, *def_value = NULL;

Expand All @@ -3827,7 +3827,12 @@ ZEND_METHOD(reflection_class, getStaticPropertyValue)
if (UNEXPECTED(zend_update_class_constants(ce) != SUCCESS)) {
return;
}

old_scope = EG(fake_scope);
EG(fake_scope) = ce;
prop = zend_std_get_static_property(ce, name, BP_VAR_IS);
EG(fake_scope) = old_scope;

if (!prop) {
if (def_value) {
ZVAL_COPY(return_value, def_value);
Expand All @@ -3847,7 +3852,7 @@ ZEND_METHOD(reflection_class, getStaticPropertyValue)
ZEND_METHOD(reflection_class, setStaticPropertyValue)
{
reflection_object *intern;
zend_class_entry *ce;
zend_class_entry *ce, *old_scope;
zend_property_info *prop_info;
zend_string *name;
zval *variable_ptr, *value;
Expand All @@ -3861,7 +3866,10 @@ ZEND_METHOD(reflection_class, setStaticPropertyValue)
if (UNEXPECTED(zend_update_class_constants(ce) != SUCCESS)) {
return;
}
variable_ptr = zend_std_get_static_property_with_info(ce, name, BP_VAR_W, &prop_info);
old_scope = EG(fake_scope);
EG(fake_scope) = ce;
variable_ptr = zend_std_get_static_property_with_info(ce, name, BP_VAR_W, &prop_info);
EG(fake_scope) = old_scope;
if (!variable_ptr) {
zend_clear_exception();
zend_throw_exception_ex(reflection_exception_ptr, 0,
Expand Down
24 changes: 18 additions & 6 deletions ext/reflection/tests/006.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,28 @@ TestDerived::testing();
string(3) "pub"
string(3) "pub"
string(7) "updated"
EXCEPTION
EXCEPTION
string(3) "pro"
string(3) "pro"
string(7) "updated"
string(3) "pri"
string(3) "pri"
string(7) "updated"
string(7) "updated"
string(7) "updated"
string(7) "updated"
string(7) "updated"
string(7) "updated"
string(7) "updated"
string(7) "updated"
string(7) "updated"
string(7) "updated"
string(7) "updated"
string(7) "updated"
string(7) "updated"
string(7) "updated"
string(7) "updated"
string(7) "updated"
EXCEPTION
EXCEPTION
string(7) "updated"
string(7) "updated"
string(7) "updated"
EXCEPTION
EXCEPTION
===DONE===
Original file line number Diff line number Diff line change
Expand Up @@ -19,41 +19,47 @@ class B extends A {

echo "Retrieving static values from A:\n";
$rcA = new ReflectionClass('A');
var_dump($rcA->getStaticPropertyValue("privateOverridden", "default value"));
var_dump($rcA->getStaticPropertyValue("\0A\0privateOverridden"));
var_dump($rcA->getStaticPropertyValue("protectedOverridden", "default value"));
var_dump($rcA->getStaticPropertyValue("\0*\0protectedOverridden"));
var_dump($rcA->getStaticPropertyValue("privateDoesNotExist", "default value"));
var_dump($rcA->getStaticPropertyValue("privateOverridden"));
var_dump($rcA->getStaticPropertyValue("protectedDoesNotExist", "default value"));
var_dump($rcA->getStaticPropertyValue("protectedOverridden"));
var_dump($rcA->getStaticPropertyValue("publicOverridden"));

echo "\nRetrieving static values from B:\n";
$rcB = new ReflectionClass('B');
var_dump($rcB->getStaticPropertyValue("\0A\0privateOverridden"));
var_dump($rcB->getStaticPropertyValue("\0B\0privateOverridden"));
var_dump($rcB->getStaticPropertyValue("\0*\0protectedOverridden"));
var_dump($rcB->getStaticPropertyValue("privateOverridden"));
var_dump($rcB->getStaticPropertyValue("protectedOverridden"));
var_dump($rcB->getStaticPropertyValue("publicOverridden"));

echo "\nRetrieving non-existent values from A with no default value:\n";
try {
var_dump($rcA->getStaticPropertyValue("protectedOverridden"));
var_dump($rcA->getStaticPropertyValue("protectedDoesNotExist"));
echo "you should not see this";
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}

try {
var_dump($rcA->getStaticPropertyValue("privateOverridden"));
var_dump($rcA->getStaticPropertyValue("privateDoesNotExist"));
echo "you should not see this";
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}

?>
--EXPECTF--
--EXPECT--
Retrieving static values from A:
string(13) "default value"
string(16) "original private"
string(13) "default value"
string(18) "original protected"
string(15) "original public"

Retrieving static values from B:
string(15) "changed private"
string(17) "changed protected"
string(14) "changed public"

Fatal error: Uncaught ReflectionException: Class A does not have a property named in %s:%d
Stack trace:
#0 %s(%d): ReflectionClass->getStaticPropertyValue('\x00A\x00privateOverr...')
#1 {main}
thrown in %s on line %d
Retrieving non-existent values from A with no default value:
Class A does not have a property named protectedDoesNotExist
Class A does not have a property named privateDoesNotExist
Original file line number Diff line number Diff line change
Expand Up @@ -19,41 +19,59 @@ class B extends A {

echo "Set static values in A:\n";
$rcA = new ReflectionClass('A');
$rcA->setStaticPropertyValue("\0A\0privateOverridden", "new value 1");
$rcA->setStaticPropertyValue("\0*\0protectedOverridden", "new value 2");
$rcA->setStaticPropertyValue("privateOverridden", "new value 1");
$rcA->setStaticPropertyValue("protectedOverridden", "new value 2");
$rcA->setStaticPropertyValue("publicOverridden", "new value 3");
print_r($rcA->getStaticProperties());

echo "\nSet static values in B:\n";
$rcB = new ReflectionClass('B');
$rcB->setStaticPropertyValue("\0A\0privateOverridden", "new value 4");
$rcB->setStaticPropertyValue("\0B\0privateOverridden", "new value 5");
$rcB->setStaticPropertyValue("\0*\0protectedOverridden", "new value 6");
$rcB->setStaticPropertyValue("privateOverridden", "new value 4");
$rcB->setStaticPropertyValue("privateOverridden", "new value 5");
$rcB->setStaticPropertyValue("protectedOverridden", "new value 6");
$rcB->setStaticPropertyValue("publicOverridden", "new value 7");
print_r($rcA->getStaticProperties());
print_r($rcB->getStaticProperties());

echo "\nSet non-existent values from A with no default value:\n";
try {
var_dump($rcA->setStaticPropertyValue("protectedOverridden", "new value 8"));
var_dump($rcA->setStaticPropertyValue("protectedDoesNotExist", "new value 8"));
echo "you should not see this";
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}

try {
var_dump($rcA->setStaticPropertyValue("privateOverridden", "new value 9"));
var_dump($rcA->setStaticPropertyValue("privateDoesNotExist", "new value 9"));
echo "you should not see this";
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}

?>
--EXPECTF--
--EXPECT--
Set static values in A:
Array
(
[privateOverridden] => new value 1
[protectedOverridden] => new value 2
[publicOverridden] => new value 3
)

Fatal error: Uncaught ReflectionException: Class A does not have a property named in %s:%d
Stack trace:
#0 %s(%d): ReflectionClass->setStaticPropertyValue('\x00A\x00privateOverr...', 'new value 1')
#1 {main}
thrown in %s on line %d
Set static values in B:
Array
(
[privateOverridden] => new value 1
[protectedOverridden] => new value 2
[publicOverridden] => new value 3
)
Array
(
[privateOverridden] => new value 5
[protectedOverridden] => new value 6
[publicOverridden] => new value 7
)

Set non-existent values from A with no default value:
Class A does not have a property named protectedDoesNotExist
Class A does not have a property named privateDoesNotExist

0 comments on commit 26aefb7

Please sign in to comment.