From 54a40f3bdec6841c1ba893a28c9f5956487e046a Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 10 Oct 2024 16:22:24 +0200 Subject: [PATCH] Add ReflectionProperty::isLazy() Closes GH-16342 --- NEWS | 1 + Zend/tests/lazy_objects/isLazy.phpt | 140 ++++++++++++++++++++++++ ext/reflection/php_reflection.c | 24 ++++ ext/reflection/php_reflection.stub.php | 2 + ext/reflection/php_reflection_arginfo.h | 6 +- 5 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/lazy_objects/isLazy.phpt diff --git a/NEWS b/NEWS index 683700f3a1c..deeeaa259f2 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ PHP NEWS . Fixed bug GH-16577 (EG(strtod_state).freelist leaks with opcache.preload). (nielsdos) . Fixed bug GH-16615 (Assertion failure in zend_std_read_property). (Arnaud) + . Fixed bug GH-16342 (Added ReflectionProperty::isLazy()). (Arnaud) - DOM: . Fixed bug GH-16594 (Assertion failure in DOM -> before). (nielsdos) diff --git a/Zend/tests/lazy_objects/isLazy.phpt b/Zend/tests/lazy_objects/isLazy.phpt new file mode 100644 index 00000000000..691b42b708d --- /dev/null +++ b/Zend/tests/lazy_objects/isLazy.phpt @@ -0,0 +1,140 @@ +--TEST-- +Lazy Objects: ReflectionProperty::isLazy() +--FILE-- +dynamic = 1; + $pr = new ReflectionProperty($tmp, $name); + } else { + $pr = $reflector->getProperty($name); + } + printf("%s: %d\n", $name, $pr->isLazy($obj)); + } +} + +$reflector = new ReflectionClass(C::class); + +print "# Ghost\n"; + +$obj = $reflector->newLazyGhost(function () { }); + +testProps($reflector, $obj); + +$pr = $reflector->getProperty('typed'); +$pr->skipLazyInitialization($obj); +printf("typed (skipped): %d\n", $pr->isLazy($obj)); + +print "# Initialized Ghost\n"; + +$reflector->initializeLazyObject($obj); + +testProps($reflector, $obj); + +print "# Proxy\n"; + +$obj = $reflector->newLazyProxy(function () { + return new C(); +}); + +testProps($reflector, $obj); + +$pr = $reflector->getProperty('typed'); +$pr->skipLazyInitialization($obj); +printf("typed (skipped prop): %d\n", $pr->isLazy($obj)); + +print "# Initialized Proxy\n"; + +$reflector->initializeLazyObject($obj); + +testProps($reflector, $obj); + +print "# Nested Proxy\n"; + +$nested = new C(); +$obj = $reflector->newLazyProxy(function () use ($nested) { + return $nested; +}); +$reflector->initializeLazyObject($obj); +$reflector->resetAsLazyProxy($nested, function () { + return new C(); +}); + +testProps($reflector, $obj); + +print "# Nested Proxy (nested initialized)\n"; + +$nested = new C(); +$obj = $reflector->newLazyProxy(function () use ($nested) { + return $nested; +}); +$reflector->initializeLazyObject($obj); +$reflector->resetAsLazyProxy($nested, function () { + return new C(); +}); +$reflector->initializeLazyObject($nested); + +testProps($reflector, $obj); + +print "# Internal\n"; + +$obj = (new DateTime())->diff(new DateTime()); +$reflector = new ReflectionClass(DateInterval::class); +$pr = new ReflectionProperty($obj, 'y'); +printf("y: %d\n", $pr->isLazy($obj)); + +?> +--EXPECT-- +# Ghost +staticProp: 0 +typed: 1 +untyped: 1 +virtual: 0 +dynamic: 0 +typed (skipped): 0 +# Initialized Ghost +staticProp: 0 +typed: 0 +untyped: 0 +virtual: 0 +dynamic: 0 +# Proxy +staticProp: 0 +typed: 1 +untyped: 1 +virtual: 0 +dynamic: 0 +typed (skipped prop): 0 +# Initialized Proxy +staticProp: 0 +typed: 0 +untyped: 0 +virtual: 0 +dynamic: 0 +# Nested Proxy +staticProp: 0 +typed: 1 +untyped: 1 +virtual: 0 +dynamic: 0 +# Nested Proxy (nested initialized) +staticProp: 0 +typed: 0 +untyped: 0 +virtual: 0 +dynamic: 0 +# Internal +y: 0 diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 36edb6b527e..e2a7bda46d3 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6271,6 +6271,30 @@ ZEND_METHOD(ReflectionProperty, skipLazyInitialization) } } +ZEND_METHOD(ReflectionProperty, isLazy) +{ + reflection_object *intern; + property_reference *ref; + zend_object *object; + + GET_REFLECTION_OBJECT_PTR(ref); + + ZEND_PARSE_PARAMETERS_START(1, 1) { + Z_PARAM_OBJ_OF_CLASS(object, intern->ce) + } ZEND_PARSE_PARAMETERS_END(); + + if (!ref->prop || ref->prop->flags & (ZEND_ACC_STATIC | ZEND_ACC_VIRTUAL)) { + RETURN_FALSE; + } + + while (zend_object_is_lazy_proxy(object) + && zend_lazy_object_initialized(object)) { + object = zend_lazy_object_get_instance(object); + } + + RETURN_BOOL(Z_PROP_FLAG_P(OBJ_PROP(object, ref->prop->offset)) & IS_PROP_LAZY); +} + /* {{{ Returns true if property was initialized */ ZEND_METHOD(ReflectionProperty, isInitialized) { diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 2b865591680..a4671cdf773 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -502,6 +502,8 @@ class ReflectionProperty implements Reflector public function skipLazyInitialization(object $object): void {} + public function isLazy(object $object): bool {} + /** @tentative-return-type */ public function isInitialized(?object $object = null): bool {} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index f55e96a7ed7..9f44ee949b0 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 273777e0bc50a3d5059bb2db7b0a1e293b26e338 */ + * Stub hash: 2620fb2d8fc950283bfc174372c72361b37e9d41 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -399,6 +399,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionProperty_skipLaz ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) ZEND_END_ARG_INFO() +#define arginfo_class_ReflectionProperty_isLazy arginfo_class_ReflectionClass_isUninitializedLazyObject + ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionProperty_isInitialized, 0, 0, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, object, IS_OBJECT, 1, "null") ZEND_END_ARG_INFO() @@ -843,6 +845,7 @@ ZEND_METHOD(ReflectionProperty, getRawValue); ZEND_METHOD(ReflectionProperty, setRawValue); ZEND_METHOD(ReflectionProperty, setRawValueWithoutLazyInitialization); ZEND_METHOD(ReflectionProperty, skipLazyInitialization); +ZEND_METHOD(ReflectionProperty, isLazy); ZEND_METHOD(ReflectionProperty, isInitialized); ZEND_METHOD(ReflectionProperty, isPublic); ZEND_METHOD(ReflectionProperty, isPrivate); @@ -1140,6 +1143,7 @@ static const zend_function_entry class_ReflectionProperty_methods[] = { ZEND_ME(ReflectionProperty, setRawValue, arginfo_class_ReflectionProperty_setRawValue, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionProperty, setRawValueWithoutLazyInitialization, arginfo_class_ReflectionProperty_setRawValueWithoutLazyInitialization, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionProperty, skipLazyInitialization, arginfo_class_ReflectionProperty_skipLazyInitialization, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionProperty, isLazy, arginfo_class_ReflectionProperty_isLazy, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionProperty, isInitialized, arginfo_class_ReflectionProperty_isInitialized, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionProperty, isPublic, arginfo_class_ReflectionProperty_isPublic, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionProperty, isPrivate, arginfo_class_ReflectionProperty_isPrivate, ZEND_ACC_PUBLIC)