From 6413ebd8191fd1c1b431c99149ab40506b9b612b Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 6 Jul 2022 13:41:01 +0200 Subject: [PATCH] Fix GH-8932: Provide a way to get the called-scope of closures --- ext/reflection/php_reflection.c | 22 ++++++ ext/reflection/php_reflection.stub.php | 3 + ext/reflection/php_reflection_arginfo.h | 6 +- ...lectionFunction_getClosureCalledClass.phpt | 70 +++++++++++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 ext/reflection/tests/ReflectionFunction_getClosureCalledClass.phpt diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index e6c63b852e096..ae5b0f1ac246e 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1633,6 +1633,28 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureScopeClass) } /* }}} */ +/* {{{ Returns the called scope associated to the closure */ +ZEND_METHOD(ReflectionFunctionAbstract, getClosureCalledClass) +{ + reflection_object *intern; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + GET_REFLECTION_OBJECT(); + if (!Z_ISUNDEF(intern->obj)) { + 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); + } + } +} +/* }}} */ + /* {{{ Returns a dynamically created closure for the function */ ZEND_METHOD(ReflectionFunction, getClosure) { diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index b0bfade520f10..31d8d0530c95c 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -48,6 +48,9 @@ public function getClosureThis() {} /** @return ReflectionClass|null */ public function getClosureScopeClass() {} + /** @return ReflectionClass|null */ + public function getClosureCalledClass() {} + /** @return string|false */ public function getDocComment() {} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 6fdc0c44d22ee..788485b2dd701 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: 6e98777552147f4a413db16ecd87c9a6931f9c00 */ + * Stub hash: 9309c0d567aae3041255b5f9b9782add9b6ac783 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -27,6 +27,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionFunctionAbstract_getClosureScopeClass arginfo_class_ReflectionFunctionAbstract_inNamespace +#define arginfo_class_ReflectionFunctionAbstract_getClosureCalledClass arginfo_class_ReflectionFunctionAbstract_inNamespace + #define arginfo_class_ReflectionFunctionAbstract_getDocComment arginfo_class_ReflectionFunctionAbstract_inNamespace #define arginfo_class_ReflectionFunctionAbstract_getEndLine arginfo_class_ReflectionFunctionAbstract_inNamespace @@ -501,6 +503,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, isGenerator); ZEND_METHOD(ReflectionFunctionAbstract, isVariadic); ZEND_METHOD(ReflectionFunctionAbstract, getClosureThis); ZEND_METHOD(ReflectionFunctionAbstract, getClosureScopeClass); +ZEND_METHOD(ReflectionFunctionAbstract, getClosureCalledClass); ZEND_METHOD(ReflectionFunctionAbstract, getDocComment); ZEND_METHOD(ReflectionFunctionAbstract, getEndLine); ZEND_METHOD(ReflectionFunctionAbstract, getExtension); @@ -719,6 +722,7 @@ static const zend_function_entry class_ReflectionFunctionAbstract_methods[] = { ZEND_ME(ReflectionFunctionAbstract, isVariadic, arginfo_class_ReflectionFunctionAbstract_isVariadic, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getClosureThis, arginfo_class_ReflectionFunctionAbstract_getClosureThis, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getClosureScopeClass, arginfo_class_ReflectionFunctionAbstract_getClosureScopeClass, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFunctionAbstract, getClosureCalledClass, arginfo_class_ReflectionFunctionAbstract_getClosureCalledClass, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getDocComment, arginfo_class_ReflectionFunctionAbstract_getDocComment, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getEndLine, arginfo_class_ReflectionFunctionAbstract_getEndLine, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getExtension, arginfo_class_ReflectionFunctionAbstract_getExtension, ZEND_ACC_PUBLIC) diff --git a/ext/reflection/tests/ReflectionFunction_getClosureCalledClass.phpt b/ext/reflection/tests/ReflectionFunction_getClosureCalledClass.phpt new file mode 100644 index 0000000000000..3c556c2e37cd4 --- /dev/null +++ b/ext/reflection/tests/ReflectionFunction_getClosureCalledClass.phpt @@ -0,0 +1,70 @@ +--TEST-- +GH-8932 (Provide a way to get the called-scope of closures) +--FILE-- +'.$name, "\n"; + } + + public static function b() { + echo static::class.'::b', "\n"; + } + + + public function c() { + echo static::class.'->c', "\n"; + } +} + +class B extends A {} + +$c = ['B', 'b']; +$d = \Closure::fromCallable($c); +$r = new \ReflectionFunction($d); +var_dump($r->getClosureCalledClass()); +$d(); + +$c = [new B(), 'c']; +$d = \Closure::fromCallable($c); +$r = new \ReflectionFunction($d); +var_dump($r->getClosureCalledClass()); +$d(); + +$c = ['B', 'd']; +$d = \Closure::fromCallable($c); +$r = new \ReflectionFunction($d); +var_dump($r->getClosureCalledClass()); +$d(); + +$c = [new B(), 'e']; +$d = \Closure::fromCallable($c); +$r = new \ReflectionFunction($d); +var_dump($r->getClosureCalledClass()); +$d(); +?> +--EXPECTF-- +object(ReflectionClass)#%d (1) { + ["name"]=> + string(1) "B" +} +B::b +object(ReflectionClass)#%d (1) { + ["name"]=> + string(1) "B" +} +B->c +object(ReflectionClass)#%d (1) { + ["name"]=> + string(1) "B" +} +B::d +object(ReflectionClass)#%d (1) { + ["name"]=> + string(1) "B" +} +B->e