diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 94d8bb7d149c..a43a436ca06f 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6731,6 +6731,12 @@ ZEND_METHOD(ReflectionProperty, isReadable) zval member; ZVAL_STR(&member, ref->unmangled_name); zend_call_known_instance_method_with_1_params(ce->__isset, obj, return_value, &member); + + // if it's a reference, unwrap + if (Z_TYPE_P(return_value) == IS_REFERENCE) { + zend_unwrap_reference(return_value); + } + *guard &= ~ZEND_GUARD_PROPERTY_ISSET; OBJ_RELEASE(obj); return; diff --git a/ext/reflection/tests/gh22000.phpt b/ext/reflection/tests/gh22000.phpt new file mode 100644 index 000000000000..04c9214e60fe --- /dev/null +++ b/ext/reflection/tests/gh22000.phpt @@ -0,0 +1,61 @@ +--TEST-- +GH-22000 - Ensure __isset is not returning a reference in ReflectionProperty::isReadable() +--FILE-- +b); + unset($this->c); + } + + public function __isset($name) { + return $name === 'b'; + } + + public function __get($name) {} +} + +class TestClass2 { + public int $d; + public int $e; + public int $f; + + public function __construct() { + unset($this->e); + unset($this->f); + } + + public function &__isset($name) { + return $name === 'f'; + } + + public function __get($name) {} +} + + +function test($class) { + $rc = new ReflectionClass($class); + foreach ($rc->getProperties() as $rp) { + echo $rp->getName() . ' from global:'; + var_dump($rp->isReadable(null, new $class)); + } +} + +test('TestClass1'); +test('TestClass2'); +?> +--EXPECTF-- +a from global:bool(true) +b from global:bool(true) +c from global:bool(false) +d from global:bool(false) +e from global: +Notice: Only variable references should be returned by reference in %s%eext%ereflection%etests%egh22000.php on line %d +bool(false) +f from global: +Notice: Only variable references should be returned by reference in %s%eext%ereflection%etests%egh22000.php on line %d +bool(true)