Skip to content

Commit fb5617f

Browse files
committed
Add consumed_args support for fcall arguments
1 parent 61fd5ce commit fb5617f

15 files changed

+235
-6
lines changed

Zend/zend_API.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4238,6 +4238,7 @@ ZEND_API zend_result zend_fcall_info_init(zval *callable, uint32_t check_flags,
42384238
fci->param_count = 0;
42394239
fci->params = NULL;
42404240
fci->named_params = NULL;
4241+
fci->consumed_args = 0;
42414242

42424243
return SUCCESS;
42434244
}

Zend/zend_API.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ typedef struct _zend_fcall_info {
4949
zval *params;
5050
zend_object *object;
5151
uint32_t param_count;
52+
uint32_t consumed_args;
5253
/* This hashtable can also contain positional arguments (with integer keys),
5354
* which will be appended to the normal params[]. This makes it easier to
5455
* integrate APIs like call_user_func_array(). The usual restriction that

Zend/zend_closures.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ ZEND_METHOD(Closure, call)
155155
fci_cache.object = fci.object = newobj;
156156

157157
fci.size = sizeof(fci);
158+
fci.consumed_args = 0;
158159
ZVAL_OBJ(&fci.function_name, &closure->std);
159160
ZVAL_UNDEF(&closure_result);
160161
fci.retval = &closure_result;

Zend/zend_execute_API.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,7 @@ zend_result _call_user_function_impl(zval *object, zval *function_name, zval *re
797797
fci.param_count = param_count;
798798
fci.params = params;
799799
fci.named_params = named_params;
800+
fci.consumed_args = 0;
800801

801802
return zend_call_function(&fci, NULL);
802803
}
@@ -905,7 +906,16 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
905906
}
906907

907908
if (EXPECTED(!must_wrap)) {
908-
ZVAL_COPY(param, arg);
909+
if (EXPECTED(fci->consumed_args == 0)) {
910+
ZVAL_COPY(param, arg);
911+
} else {
912+
if (i < 32 && (fci->consumed_args & (1u << i)) && !Z_ISREF_P(arg) && arg == &fci->params[i]) {
913+
ZVAL_COPY_VALUE(param, arg);
914+
ZVAL_UNDEF(arg);
915+
} else {
916+
ZVAL_COPY(param, arg);
917+
}
918+
}
909919
} else {
910920
Z_TRY_ADDREF_P(arg);
911921
ZVAL_NEW_REF(param, arg);
@@ -1091,6 +1101,7 @@ ZEND_API void zend_call_known_function(
10911101
fci.param_count = param_count;
10921102
fci.params = params;
10931103
fci.named_params = named_params;
1104+
fci.consumed_args = 0;
10941105
ZVAL_UNDEF(&fci.function_name); /* Unused */
10951106

10961107
fcic.function_handler = fn;

ext/dom/xpath_callbacks.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ static zend_result php_dom_xpath_callback_dispatch(php_dom_xpath_callbacks *xpat
408408
fci.param_count = param_count;
409409
fci.params = params;
410410
fci.named_params = NULL;
411+
fci.consumed_args = 0;
411412
ZVAL_STRINGL(&fci.function_name, function_name, function_name_length);
412413

413414
zend_call_function(&fci, NULL);

ext/ffi/ffi.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,7 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v
950950
fci.object = NULL;
951951
fci.param_count = callback_data->arg_count;
952952
fci.named_params = NULL;
953+
fci.consumed_args = 0;
953954

954955
if (callback_data->type->func.args) {
955956
int n = 0;

ext/zend_test/test.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,61 @@ static ZEND_FUNCTION(zend_call_method_if_exists)
548548
}
549549
}
550550

551+
static ZEND_FUNCTION(zend_test_call_with_consumed_args)
552+
{
553+
zend_fcall_info fci = empty_fcall_info;
554+
zend_fcall_info_cache fcc = empty_fcall_info_cache;
555+
zval *args;
556+
zend_long consumed_args;
557+
zval retval;
558+
uint32_t actual_consumed_args = 0;
559+
uint32_t i;
560+
zend_result call_result;
561+
562+
ZEND_PARSE_PARAMETERS_START(3, 3)
563+
Z_PARAM_FUNC(fci, fcc)
564+
Z_PARAM_ARRAY(args)
565+
Z_PARAM_LONG(consumed_args)
566+
ZEND_PARSE_PARAMETERS_END();
567+
568+
if (UNEXPECTED(consumed_args < 0 || consumed_args > UINT32_MAX)) {
569+
zend_argument_value_error(3, "must be between 0 and 4294967295");
570+
RETURN_THROWS();
571+
}
572+
573+
zend_fcall_info_args(&fci, args);
574+
575+
ZVAL_UNDEF(&retval);
576+
fci.retval = &retval;
577+
fci.consumed_args = (uint32_t) consumed_args;
578+
579+
call_result = zend_call_function(&fci, &fcc);
580+
581+
for (i = 0; i < fci.param_count && i < 32; i++) {
582+
if (Z_ISUNDEF(fci.params[i])) {
583+
actual_consumed_args |= (1u << i);
584+
}
585+
}
586+
587+
zend_fcall_info_args_clear(&fci, true);
588+
589+
if (call_result == FAILURE || EG(exception)) {
590+
if (!Z_ISUNDEF(retval)) {
591+
zval_ptr_dtor(&retval);
592+
}
593+
RETURN_THROWS();
594+
}
595+
596+
array_init(return_value);
597+
add_assoc_long(return_value, "consumed_args", actual_consumed_args);
598+
599+
if (Z_ISUNDEF(retval)) {
600+
add_assoc_null(return_value, "retval");
601+
} else {
602+
add_assoc_zval(return_value, "retval", &retval);
603+
}
604+
}
605+
551606
static ZEND_FUNCTION(zend_get_unit_enum)
552607
{
553608
ZEND_PARSE_PARAMETERS_NONE();

ext/zend_test/test.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,8 @@ function zend_object_init_with_constructor(string $class, mixed ...$args): mixed
303303

304304
function zend_call_method_if_exists(object $obj, string $method, mixed ...$args): mixed {}
305305

306+
function zend_test_call_with_consumed_args(callable $cb, array $args, int $consumed_args): array {}
307+
306308
function zend_test_zend_ini_parse_quantity(string $str): int {}
307309
function zend_test_zend_ini_parse_uquantity(string $str): int {}
308310

ext/zend_test/test_arginfo.h

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/zend_test/test_decl.h

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)