diff --git a/NEWS b/NEWS index 285bf4e23a80d..dc464b1abecab 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,8 @@ PHP NEWS (ilutov) . Fixed bug GH-17376 (Broken JIT polymorphism for property hooks added to child class). (ilutov) + . Fixed bug GH-17913 (ReflectionFunction::isDeprecated() returns incorrect + results for closures created from magic __call()). (timwolla) 27 Feb 2025, PHP 8.4.5 diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 049c026845b1c..6ad2f6ac40246 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -361,11 +361,12 @@ static zend_result zend_create_closure_from_callable(zval *return_value, zval *c memset(&call, 0, sizeof(zend_internal_function)); call.type = ZEND_INTERNAL_FUNCTION; - call.fn_flags = mptr->common.fn_flags & ZEND_ACC_STATIC; + call.fn_flags = mptr->common.fn_flags & (ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED); call.handler = zend_closure_call_magic; call.function_name = mptr->common.function_name; call.scope = mptr->common.scope; call.doc_comment = NULL; + call.attributes = mptr->common.attributes; zend_free_trampoline(mptr); mptr = (zend_function *) &call; @@ -871,7 +872,7 @@ void zend_closure_from_frame(zval *return_value, zend_execute_data *call) { /* { memset(&trampoline, 0, sizeof(zend_internal_function)); trampoline.type = ZEND_INTERNAL_FUNCTION; - trampoline.fn_flags = mptr->common.fn_flags & (ZEND_ACC_STATIC | ZEND_ACC_VARIADIC | ZEND_ACC_RETURN_REFERENCE); + trampoline.fn_flags = mptr->common.fn_flags & (ZEND_ACC_STATIC | ZEND_ACC_VARIADIC | ZEND_ACC_RETURN_REFERENCE | ZEND_ACC_DEPRECATED); trampoline.handler = zend_closure_call_magic; trampoline.function_name = mptr->common.function_name; trampoline.scope = mptr->common.scope; @@ -879,6 +880,7 @@ void zend_closure_from_frame(zval *return_value, zend_execute_data *call) { /* { if (trampoline.fn_flags & ZEND_ACC_VARIADIC) { trampoline.arg_info = trampoline_arg_info; } + trampoline.attributes = mptr->common.attributes; zend_free_trampoline(mptr); mptr = (zend_function *) &trampoline; diff --git a/ext/reflection/tests/ReflectionFunction_isDeprecated_magic_call.phpt b/ext/reflection/tests/ReflectionFunction_isDeprecated_magic_call.phpt new file mode 100644 index 0000000000000..fa63816c44ec7 --- /dev/null +++ b/ext/reflection/tests/ReflectionFunction_isDeprecated_magic_call.phpt @@ -0,0 +1,70 @@ +--TEST-- +GH-17913: ReflectionClassConstant::isDeprecated() with __call() and __callStatic() +--FILE-- +getAttributes()[0]->newInstance()); +var_dump($rc->isDeprecated()); + +$closure = $foo->test(...); + +$rc = new ReflectionFunction($closure); +var_dump($rc->getAttributes()[0]->newInstance()); +var_dump($rc->isDeprecated()); + +$closure = Closure::fromCallable('Clazz::test'); + +$rc = new ReflectionFunction($closure); +var_dump($rc->getAttributes()[0]->newInstance()); +var_dump($rc->isDeprecated()); + +$closure = Clazz::test(...); + +$rc = new ReflectionFunction($closure); +var_dump($rc->getAttributes()[0]->newInstance()); +var_dump($rc->isDeprecated()); + +?> +--EXPECTF-- +object(Deprecated)#%d (2) { + ["message"]=> + NULL + ["since"]=> + NULL +} +bool(true) +object(Deprecated)#%d (2) { + ["message"]=> + NULL + ["since"]=> + NULL +} +bool(true) +object(Deprecated)#%d (2) { + ["message"]=> + string(18) "due to some reason" + ["since"]=> + NULL +} +bool(true) +object(Deprecated)#%d (2) { + ["message"]=> + string(18) "due to some reason" + ["since"]=> + NULL +} +bool(true)