From a596838b6bc5403bbdc1a2e2ca3aa3a08aaf49aa Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 6 Jul 2022 13:41:01 +0200 Subject: [PATCH 1/3] Fix GH-8932: Wrong closure scope class reported for static methods `ReflectionFunction::getClosureScopeClass()` needs to report the `called_scope`, if there is one. Unfortunately, it is not yet possible to retrieve that value from the closure object, so we need to introduce a getter. --- Zend/zend_closures.c | 8 ++++++++ Zend/zend_closures.h | 1 + ext/reflection/php_reflection.c | 6 ++++-- ext/reflection/tests/gh8932.phpt | 24 ++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 ext/reflection/tests/gh8932.phpt diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 1dd7715e55b6a..08595e7be297f 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -458,6 +458,14 @@ ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj) /* { } /* }}} */ +ZEND_API const zend_class_entry *zend_get_closure_called_scope(zend_object *obj) /* {{{ */ +{ + zend_closure *closure = (zend_closure *) obj; + return closure->called_scope; + +} +/* }}} */ + ZEND_API zval* zend_get_closure_this_ptr(zval *obj) /* {{{ */ { zend_closure *closure = (zend_closure *)Z_OBJ_P(obj); diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index 2d093fa616800..b7acf9019c0a5 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -37,6 +37,7 @@ ZEND_API void zend_create_closure(zval *res, zend_function *op_array, zend_class ZEND_API void zend_create_fake_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr); ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *obj); ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj); +ZEND_API const zend_class_entry *zend_get_closure_called_scope(zend_object *obj); ZEND_API zval* zend_get_closure_this_ptr(zval *obj); END_EXTERN_C() diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index e31a31de1d38a..e61f1047dec44 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1687,6 +1687,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureScopeClass) { reflection_object *intern; const zend_function *closure_func; + const zend_class_entry *called_scope; if (zend_parse_parameters_none() == FAILURE) { RETURN_THROWS(); @@ -1694,8 +1695,9 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureScopeClass) GET_REFLECTION_OBJECT(); if (!Z_ISUNDEF(intern->obj)) { closure_func = zend_get_closure_method_def(Z_OBJ(intern->obj)); - if (closure_func && closure_func->common.scope) { - zend_reflection_class_factory(closure_func->common.scope, return_value); + called_scope = zend_get_closure_called_scope(Z_OBJ(intern->obj)); + if (closure_func && (called_scope || closure_func->common.scope)) { + zend_reflection_class_factory(called_scope ? (zend_class_entry *) called_scope : closure_func->common.scope, return_value); } } } diff --git a/ext/reflection/tests/gh8932.phpt b/ext/reflection/tests/gh8932.phpt new file mode 100644 index 0000000000000..e208f92466227 --- /dev/null +++ b/ext/reflection/tests/gh8932.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-8932 (Wrong closure scope class reported for static methods) +--FILE-- +getClosureScopeClass()); +$d(); +?> +--EXPECTF-- +object(ReflectionClass)#%d (1) { + ["name"]=> + string(1) "B" +} +B From d71825b3060d24882293bee567a23b17847d6a62 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 1 Aug 2022 17:25:15 +0200 Subject: [PATCH 2/3] Drop getter in favor of get_closure handler --- Zend/zend_closures.c | 8 -------- Zend/zend_closures.h | 1 - ext/reflection/php_reflection.c | 11 ++++++----- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 08595e7be297f..1dd7715e55b6a 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -458,14 +458,6 @@ ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj) /* { } /* }}} */ -ZEND_API const zend_class_entry *zend_get_closure_called_scope(zend_object *obj) /* {{{ */ -{ - zend_closure *closure = (zend_closure *) obj; - return closure->called_scope; - -} -/* }}} */ - ZEND_API zval* zend_get_closure_this_ptr(zval *obj) /* {{{ */ { zend_closure *closure = (zend_closure *)Z_OBJ_P(obj); diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index b7acf9019c0a5..2d093fa616800 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -37,7 +37,6 @@ ZEND_API void zend_create_closure(zval *res, zend_function *op_array, zend_class ZEND_API void zend_create_fake_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr); ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *obj); ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj); -ZEND_API const zend_class_entry *zend_get_closure_called_scope(zend_object *obj); ZEND_API zval* zend_get_closure_this_ptr(zval *obj); END_EXTERN_C() diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index e61f1047dec44..6aa16426d1f05 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1686,17 +1686,18 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureThis) ZEND_METHOD(ReflectionFunctionAbstract, getClosureScopeClass) { reflection_object *intern; - const zend_function *closure_func; - const zend_class_entry *called_scope; if (zend_parse_parameters_none() == FAILURE) { RETURN_THROWS(); } GET_REFLECTION_OBJECT(); if (!Z_ISUNDEF(intern->obj)) { - closure_func = zend_get_closure_method_def(Z_OBJ(intern->obj)); - called_scope = zend_get_closure_called_scope(Z_OBJ(intern->obj)); - if (closure_func && (called_scope || closure_func->common.scope)) { + zend_class_entry *called_scope; + zend_function *closure_func; + zend_object *object; + if (Z_OBJ_HANDLER(intern->obj, get_closure) + && Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &closure_func, &object, 1) == SUCCESS + && closure_func && (called_scope || closure_func->common.scope)) { zend_reflection_class_factory(called_scope ? (zend_class_entry *) called_scope : closure_func->common.scope, return_value); } } From c245fc907171d0058f3890348130ce72df9681c0 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 1 Aug 2022 17:49:03 +0200 Subject: [PATCH 3/3] Fix test expectation That dummy scope doesn't seem to make sense here. Maybe the test was just checking the erroneous implementation. --- Zend/tests/closure_042.phpt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Zend/tests/closure_042.phpt b/Zend/tests/closure_042.phpt index dc849a528211b..bd870b50c0c0b 100644 --- a/Zend/tests/closure_042.phpt +++ b/Zend/tests/closure_042.phpt @@ -1,5 +1,5 @@ --TEST-- -Closure 042: Binding an instance to a non-scoped non-static closures gives it a dummy scope +Closure 042: Binding an instance to non-scoped non-static closures --FILE--