diff --git a/NEWS b/NEWS index 29bb88d40ec0..287f2b1d0a4a 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,8 @@ PHP NEWS of class names in implements, catch, and trait use lists. (Jorg Sowa) . Fixed AST export dropping the braces of dynamic class constant fetches. (Jorg Sowa) + . Fixed bug GH-25238 (ReflectionProperty::isInitialized() skips lazy init once + cached). (lazerg) - Curl: . Fixed curl_reset() retaining CURLOPT_PRIVATE after reset. (lacatoire) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index ea78bf601d48..b799b0347645 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6488,7 +6488,9 @@ ZEND_METHOD(ReflectionProperty, isInitialized) if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { zval *value = OBJ_PROP(Z_OBJ_P(object), prop_offset); - RETURN_BOOL(!Z_ISUNDEF_P(value)); + if (EXPECTED(!Z_ISUNDEF_P(value) || !zend_object_is_lazy(Z_OBJ_P(object)))) { + RETURN_BOOL(!Z_ISUNDEF_P(value)); + } } } diff --git a/ext/reflection/tests/gh25238.phpt b/ext/reflection/tests/gh25238.phpt new file mode 100644 index 000000000000..24c4b754cabc --- /dev/null +++ b/ext/reflection/tests/gh25238.phpt @@ -0,0 +1,74 @@ +--TEST-- +GH-25238 (ReflectionProperty::isInitialized() does not initialize lazy objects once its cache slot is warm) +--FILE-- +newLazyGhost(function (Line $line) use ($label) { + $line->label = $label; + }); + var_dump($property->isInitialized($line)); + var_dump($class->isUninitializedLazyObject($line)); + var_dump($line->label); +} + +foreach (['first', 'second'] as $label) { + $line = $class->newLazyProxy(function () use ($label) { + $line = new Line(); + $line->label = $label; + return $line; + }); + var_dump($property->isInitialized($line)); + var_dump($class->isUninitializedLazyObject($line)); + var_dump($property->isInitialized($line)); + var_dump($line->label); +} + +$line = new Line(); +unset($line->label); +var_dump($property->isInitialized($line)); + +$class = new ReflectionClass(NoDefault::class); +$property = new ReflectionProperty(NoDefault::class, 'label'); + +for ($i = 0; $i < 2; $i++) { + $object = $class->newLazyGhost(function (NoDefault $object) {}); + var_dump($property->isInitialized($object)); + var_dump($class->isUninitializedLazyObject($object)); +} + +?> +--EXPECT-- +bool(true) +bool(false) +string(5) "first" +bool(true) +bool(false) +string(6) "second" +bool(true) +bool(false) +string(5) "third" +bool(true) +bool(false) +bool(true) +string(5) "first" +bool(true) +bool(false) +bool(true) +string(6) "second" +bool(false) +bool(false) +bool(false) +bool(false) +bool(false)