Implement Closure::getCurrent() to retrieve current closure

Fixes GH-18163
Closes GH-18167
This commit is contained in:
Ilija Tovilo 2025-03-28 01:01:24 +01:00
parent d20f4fca69
commit eb65ec41b7
No known key found for this signature in database
GPG key ID: 5050C66BFCD1015A
5 changed files with 90 additions and 1 deletions

View file

@ -421,6 +421,7 @@ PHP 8.5 UPGRADE NOTES
. The clone language construct is now a function and supports reassigning
(readonly) properties during cloning via the new $withProperties parameter.
RFC: https://wiki.php.net/rfc/clone_with_v2
. Added Closure::getCurrent() to receive currently executing closure.
- Curl:
. curl_multi_get_handles() allows retrieving all CurlHandles current

View file

@ -0,0 +1,64 @@
--TEST--
Closure::getCurrent()
--FILE--
<?php
$i = 1;
$c = function ($p) use (&$i) {
$self = Closure::getCurrent();
var_dump($p, $i);
$i++;
if ($p < 10) {
$self($p + 1);
}
};
$c(1);
var_dump($i);
function fail() {
Closure::getCurrent();
}
try {
fail();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
function foo() {
var_dump(Closure::getCurrent());
}
try {
foo(...)();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
int(1)
int(1)
int(2)
int(2)
int(3)
int(3)
int(4)
int(4)
int(5)
int(5)
int(6)
int(6)
int(7)
int(7)
int(8)
int(8)
int(9)
int(9)
int(10)
int(10)
int(11)
Current function is not a closure
Current function is not a closure

View file

@ -418,6 +418,23 @@ ZEND_METHOD(Closure, fromCallable)
}
/* }}} */
ZEND_METHOD(Closure, getCurrent)
{
ZEND_PARSE_PARAMETERS_NONE();
zend_execute_data *prev_ex = EX(prev_execute_data);
if (!prev_ex
|| !prev_ex->func
|| (prev_ex->func->common.fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_FAKE_CLOSURE)) != ZEND_ACC_CLOSURE) {
zend_throw_error(NULL, "Current function is not a closure");
RETURN_THROWS();
}
zend_object *obj = ZEND_CLOSURE_OBJECT(prev_ex->func);
RETURN_OBJ_COPY(obj);
}
static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object) /* {{{ */
{
zend_throw_error(NULL, "Instantiation of class Closure is not allowed");

View file

@ -21,4 +21,6 @@ final class Closure
public function call(object $newThis, mixed ...$args): mixed {}
public static function fromCallable(callable $callback): Closure {}
public static function getCurrent(): Closure {}
}

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: e3b480674671a698814db282c5ea34d438fe519d */
* Stub hash: e0626e52adb2d38dad1140c1a28cc7774cc84500 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Closure___construct, 0, 0, 0)
ZEND_END_ARG_INFO()
@ -24,11 +24,15 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Closure_fromCallable, 0, 1,
ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Closure_getCurrent, 0, 0, Closure, 0)
ZEND_END_ARG_INFO()
ZEND_METHOD(Closure, __construct);
ZEND_METHOD(Closure, bind);
ZEND_METHOD(Closure, bindTo);
ZEND_METHOD(Closure, call);
ZEND_METHOD(Closure, fromCallable);
ZEND_METHOD(Closure, getCurrent);
static const zend_function_entry class_Closure_methods[] = {
ZEND_ME(Closure, __construct, arginfo_class_Closure___construct, ZEND_ACC_PRIVATE)
@ -36,6 +40,7 @@ static const zend_function_entry class_Closure_methods[] = {
ZEND_ME(Closure, bindTo, arginfo_class_Closure_bindTo, ZEND_ACC_PUBLIC)
ZEND_ME(Closure, call, arginfo_class_Closure_call, ZEND_ACC_PUBLIC)
ZEND_ME(Closure, fromCallable, arginfo_class_Closure_fromCallable, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(Closure, getCurrent, arginfo_class_Closure_getCurrent, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_FE_END
};