From ca49e53670631ed9754810d7ec6d56ede0d06cca Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 15 Apr 2021 15:30:10 +0300 Subject: [PATCH] Stop inserting fake frames on VM stack. Now similar "fake" frames now materialized when fetching debug backtraces. The patch also fixes few incorrect backtraces for generators in *.phpt tests. --- .../generators/exception_during_shutdown.phpt | 3 +- Zend/zend_builtin_functions.c | 241 ++++++++---------- Zend/zend_exceptions.c | 2 +- Zend/zend_execute_API.c | 60 +---- Zend/zend_objects.c | 3 + .../tests/ReflectionGenerator_basic.phpt | 37 ++- .../tests/ReflectionGenerator_getTrace.phpt | 10 +- .../ReflectionGenerator_in_Generator.phpt | 20 +- 8 files changed, 177 insertions(+), 199 deletions(-) diff --git a/Zend/tests/generators/exception_during_shutdown.phpt b/Zend/tests/generators/exception_during_shutdown.phpt index d9c8bd0175..acbdec8ab8 100644 --- a/Zend/tests/generators/exception_during_shutdown.phpt +++ b/Zend/tests/generators/exception_during_shutdown.phpt @@ -26,5 +26,6 @@ before yield in finally Fatal error: Uncaught Error: Cannot yield from finally in a force-closed generator in %s:%d Stack trace: -#0 {main} +#0 [internal function]: gen() +#1 {main} thrown in %s on line %d diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 268fcfaadc..3c6c6c5f2f 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1661,25 +1661,12 @@ void debug_print_backtrace_args(zval *arg_array) /* {{{ */ } /* }}} */ -static inline bool skip_internal_handler(zend_execute_data *skip) /* {{{ */ -{ - return !(skip->func && ZEND_USER_CODE(skip->func->common.type)) - && skip->prev_execute_data - && skip->prev_execute_data->func - && ZEND_USER_CODE(skip->prev_execute_data->func->common.type) - && skip->prev_execute_data->opline->opcode != ZEND_DO_FCALL - && skip->prev_execute_data->opline->opcode != ZEND_DO_ICALL - && skip->prev_execute_data->opline->opcode != ZEND_DO_UCALL - && skip->prev_execute_data->opline->opcode != ZEND_DO_FCALL_BY_NAME - && skip->prev_execute_data->opline->opcode != ZEND_INCLUDE_OR_EVAL; -} -/* {{{ */ - /* {{{ */ ZEND_FUNCTION(debug_print_backtrace) { - zend_execute_data *call, *ptr, *skip; + zend_execute_data *call; zend_object *object; + bool fake_frame = 0; int lineno, frameno = 0; zend_function *func; const char *function_name; @@ -1696,61 +1683,67 @@ ZEND_FUNCTION(debug_print_backtrace) RETURN_THROWS(); } - ZVAL_UNDEF(&arg_array); - ptr = EX(prev_execute_data); - /* skip debug_backtrace() */ - call = ptr; - ptr = ptr->prev_execute_data; + call = EX(prev_execute_data); + + while (call && (limit == 0 || frameno < limit)) { + zend_execute_data *prev = call->prev_execute_data; + + if (!prev) { + /* add frame for a handler call without {main} code */ + if (EXPECTED((ZEND_CALL_INFO(call) & ZEND_CALL_TOP_FUNCTION) == 0)) { + break; + } + } else if (UNEXPECTED((ZEND_CALL_INFO(call) & ZEND_CALL_GENERATOR) != 0)) { + prev = zend_generator_check_placeholder_frame(prev); + } - while (ptr && (limit == 0 || frameno < limit)) { frameno++; class_name = NULL; call_type = NULL; ZVAL_UNDEF(&arg_array); - if (UNEXPECTED((ZEND_CALL_INFO(call) & ZEND_CALL_GENERATOR) != 0)) { - ptr = zend_generator_check_placeholder_frame(ptr); - } - - skip = ptr; - /* skip internal handler */ - if (skip_internal_handler(skip)) { - skip = skip->prev_execute_data; - } - - if (skip->func && ZEND_USER_CODE(skip->func->common.type)) { - filename = ZSTR_VAL(skip->func->op_array.filename); - if (skip->opline->opcode == ZEND_HANDLE_EXCEPTION) { + if (prev && prev->func && ZEND_USER_CODE(prev->func->common.type)) { + filename = ZSTR_VAL(prev->func->op_array.filename); + if (prev->opline->opcode == ZEND_HANDLE_EXCEPTION) { if (EG(opline_before_exception)) { lineno = EG(opline_before_exception)->lineno; } else { - lineno = skip->func->op_array.line_end; + lineno = prev->func->op_array.line_end; } } else { - lineno = skip->opline->lineno; + lineno = prev->opline->lineno; } } else { + zend_execute_data *prev_call = prev; + filename = NULL; lineno = 0; + while (prev_call) { + zend_execute_data *prev; + + if (prev_call && + prev_call->func && + !ZEND_USER_CODE(prev_call->func->common.type) && + !(prev_call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + break; + } + + prev = prev_call->prev_execute_data; + if (prev && prev->func && ZEND_USER_CODE(prev->func->common.type)) { + filename = ZSTR_VAL(prev->func->op_array.filename); + lineno = prev->opline->lineno; + break; + } + prev_call = prev; + } } /* $this may be passed into regular internal functions */ object = (Z_TYPE(call->This) == IS_OBJECT) ? Z_OBJ(call->This) : NULL; - - if (call->func) { - func = call->func; - if (func->common.function_name) { - function_name = ZSTR_VAL(func->common.function_name); - } else { - function_name = NULL; - } - } else { - func = NULL; - function_name = NULL; - } - - if (function_name) { + func = call->func; + if (!fake_frame && func->common.function_name) { + function_name = ZSTR_VAL(func->common.function_name); if (object) { if (func->common.scope) { class_name = func->common.scope->name; @@ -1777,8 +1770,8 @@ ZEND_FUNCTION(debug_print_backtrace) /* i know this is kinda ugly, but i'm trying to avoid extra cycles in the main execution loop */ bool build_filename_arg = 1; uint32_t include_kind = 0; - if (ptr->func && ZEND_USER_CODE(ptr->func->common.type) && ptr->opline->opcode == ZEND_INCLUDE_OR_EVAL) { - include_kind = ptr->opline->extended_value; + if (prev && prev->func && ZEND_USER_CODE(prev->func->common.type) && prev->opline->opcode == ZEND_INCLUDE_OR_EVAL) { + include_kind = prev->opline->extended_value; } switch (include_kind) { @@ -1833,33 +1826,22 @@ ZEND_FUNCTION(debug_print_backtrace) if (filename) { zend_printf(") called at [%s:%d]\n", filename, lineno); } else { - zend_execute_data *prev_call = skip; - zend_execute_data *prev = skip->prev_execute_data; - - while (prev) { - if (prev_call && - prev_call->func && - !ZEND_USER_CODE(prev_call->func->common.type)) { - prev = NULL; - break; - } - if (prev->func && ZEND_USER_CODE(prev->func->common.type)) { - zend_printf(") called at [%s:%d]\n", ZSTR_VAL(prev->func->op_array.filename), prev->opline->lineno); - break; - } - prev_call = prev; - prev = prev->prev_execute_data; - } - if (!prev) { - ZEND_PUTS(")\n"); - } + ZEND_PUTS(")\n"); } ++indent; skip_frame: - include_filename = filename; - call = skip; - ptr = skip->prev_execute_data; + if (UNEXPECTED((ZEND_CALL_INFO(call) & ZEND_CALL_TOP_FUNCTION) != 0) + && !fake_frame + && prev + && prev->func + && ZEND_USER_CODE(prev->func->common.type) + && prev->opline->opcode == ZEND_INCLUDE_OR_EVAL) { + fake_frame = 1; + } else { + include_filename = filename; + call = prev; + } } } @@ -1867,11 +1849,11 @@ skip_frame: ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int options, int limit) /* {{{ */ { - zend_execute_data *ptr, *skip, *call = NULL; + zend_execute_data *call; zend_object *object; + bool fake_frame = 0; int lineno, frameno = 0; zend_function *func; - zend_string *function_name; zend_string *filename; zend_string *include_filename = NULL; zval tmp; @@ -1879,60 +1861,44 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int array_init(return_value); - if (!(ptr = EG(current_execute_data))) { + call = EG(current_execute_data); + if (!call) { return; } - if (!ptr->func || !ZEND_USER_CODE(ptr->func->common.type)) { - call = ptr; - ptr = ptr->prev_execute_data; + if (skip_last) { + /* skip debug_backtrace() */ + call = call->prev_execute_data; } - if (ptr) { - if (skip_last) { - /* skip debug_backtrace() */ - call = ptr; - ptr = ptr->prev_execute_data; - } else { - /* skip "new Exception()" */ - if (ptr->func && ZEND_USER_CODE(ptr->func->common.type) && (ptr->opline->opcode == ZEND_NEW)) { - call = ptr; - ptr = ptr->prev_execute_data; + while (call && (limit == 0 || frameno < limit)) { + zend_execute_data *prev = call->prev_execute_data; + + if (!prev) { + /* add frame for a handler call without {main} code */ + if (EXPECTED((ZEND_CALL_INFO(call) & ZEND_CALL_TOP_FUNCTION) == 0)) { + break; } + } else if (UNEXPECTED((ZEND_CALL_INFO(call) & ZEND_CALL_GENERATOR) != 0)) { + prev = zend_generator_check_placeholder_frame(prev); } - if (!call) { - call = ptr; - ptr = ptr->prev_execute_data; - } - } - while (ptr && (limit == 0 || frameno < limit)) { frameno++; /* We use _zend_hash_append*() and the array must be preallocated */ stack_frame = zend_new_array(8); zend_hash_real_init_mixed(stack_frame); - if (UNEXPECTED((ZEND_CALL_INFO(call) & ZEND_CALL_GENERATOR) != 0)) { - ptr = zend_generator_check_placeholder_frame(ptr); - } - - skip = ptr; - /* skip internal handler */ - if (skip_internal_handler(skip)) { - skip = skip->prev_execute_data; - } - - if (skip->func && ZEND_USER_CODE(skip->func->common.type)) { - filename = skip->func->op_array.filename; - if (skip->opline->opcode == ZEND_HANDLE_EXCEPTION) { + if (prev && prev->func && ZEND_USER_CODE(prev->func->common.type)) { + filename = prev->func->op_array.filename; + if (prev->opline->opcode == ZEND_HANDLE_EXCEPTION) { if (EG(opline_before_exception)) { lineno = EG(opline_before_exception)->lineno; } else { - lineno = skip->func->op_array.line_end; + lineno = prev->func->op_array.line_end; } } else { - lineno = skip->opline->lineno; + lineno = prev->opline->lineno; } ZVAL_STR_COPY(&tmp, filename); _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp, 1); @@ -1943,17 +1909,20 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int * and debug_backtrace() might have been called by the error_handler. in this case we don't * want to pop anything of the argument-stack */ } else { - zend_execute_data *prev_call = skip; - zend_execute_data *prev = skip->prev_execute_data; + zend_execute_data *prev_call = prev; + + while (prev_call) { + zend_execute_data *prev; - while (prev) { if (prev_call && prev_call->func && !ZEND_USER_CODE(prev_call->func->common.type) && !(prev_call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { break; } - if (prev->func && ZEND_USER_CODE(prev->func->common.type)) { + + prev = prev_call->prev_execute_data; + if (prev && prev->func && ZEND_USER_CODE(prev->func->common.type)) { ZVAL_STR_COPY(&tmp, prev->func->op_array.filename); _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp, 1); ZVAL_LONG(&tmp, prev->opline->lineno); @@ -1961,27 +1930,18 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int break; } prev_call = prev; - prev = prev->prev_execute_data; } filename = NULL; } - /* $this may be passed into regular internal functions */ - object = (call && (Z_TYPE(call->This) == IS_OBJECT)) ? Z_OBJ(call->This) : NULL; - - if (call && call->func) { - func = call->func; - function_name = func->common.function_name; - } else { - func = NULL; - function_name = NULL; - } - - if (function_name) { - ZVAL_STR_COPY(&tmp, function_name); + func = call->func; + if (!fake_frame && func->common.function_name) { + ZVAL_STR_COPY(&tmp, func->common.function_name); _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp, 1); - if (object) { + if (Z_TYPE(call->This) == IS_OBJECT) { + object = Z_OBJ(call->This); + /* $this may be passed into regular internal functions */ if (func->common.scope) { ZVAL_STR_COPY(&tmp, func->common.scope->name); } else if (object->handlers->get_class_name == zend_std_get_class_name) { @@ -2015,8 +1975,8 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int bool build_filename_arg = 1; zend_string *pseudo_function_name; uint32_t include_kind = 0; - if (ptr->func && ZEND_USER_CODE(ptr->func->common.type) && ptr->opline->opcode == ZEND_INCLUDE_OR_EVAL) { - include_kind = ptr->opline->extended_value; + if (prev && prev->func && ZEND_USER_CODE(prev->func->common.type) && prev->opline->opcode == ZEND_INCLUDE_OR_EVAL) { + include_kind = prev->opline->extended_value; } switch (include_kind) { @@ -2070,9 +2030,18 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); skip_frame: - include_filename = filename; - call = skip; - ptr = skip->prev_execute_data; + if (UNEXPECTED((ZEND_CALL_INFO(call) & ZEND_CALL_TOP_FUNCTION) != 0) + && !fake_frame + && prev + && prev->func + && ZEND_USER_CODE(prev->func->common.type) + && prev->opline->opcode == ZEND_INCLUDE_OR_EVAL) { + fake_frame = 1; + } else { + fake_frame = 0; + include_filename = filename; + call = prev; + } } } /* }}} */ diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index b1676e03af..61a0aa923d 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -276,7 +276,7 @@ static zend_object *zend_default_exception_new(zend_class_entry *class_type) /* static zend_object *zend_error_exception_new(zend_class_entry *class_type) /* {{{ */ { - return zend_default_exception_new_ex(class_type, 2); + return zend_default_exception_new_ex(class_type, 0); } /* }}} */ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 854d803020..7d5adad304 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -683,7 +683,7 @@ zend_result _call_user_function_impl(zval *object, zval *function_name, zval *re zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /* {{{ */ { uint32_t i; - zend_execute_data *call, dummy_execute_data; + zend_execute_data *call; zend_fcall_info_cache fci_cache_local; zend_function *func; uint32_t call_info; @@ -702,29 +702,6 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ ZEND_ASSERT(fci->size == sizeof(zend_fcall_info)); - /* Initialize execute_data */ - if (!EG(current_execute_data)) { - /* This only happens when we're called outside any execute()'s - * It shouldn't be strictly necessary to NULL execute_data out, - * but it may make bugs easier to spot - */ - memset(&dummy_execute_data, 0, sizeof(zend_execute_data)); - EG(current_execute_data) = &dummy_execute_data; - } else if (EG(current_execute_data)->func && - ZEND_USER_CODE(EG(current_execute_data)->func->common.type) && - EG(current_execute_data)->opline->opcode != ZEND_DO_FCALL && - EG(current_execute_data)->opline->opcode != ZEND_DO_ICALL && - EG(current_execute_data)->opline->opcode != ZEND_DO_UCALL && - EG(current_execute_data)->opline->opcode != ZEND_DO_FCALL_BY_NAME) { - /* Insert fake frame in case of include or magic calls */ - dummy_execute_data = *EG(current_execute_data); - dummy_execute_data.prev_execute_data = EG(current_execute_data); - dummy_execute_data.call = NULL; - dummy_execute_data.opline = NULL; - dummy_execute_data.func = NULL; - EG(current_execute_data) = &dummy_execute_data; - } - if (!fci_cache || !fci_cache->function_handler) { char *error = NULL; @@ -740,9 +717,6 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ efree(error); zend_string_release_ex(callable_name, 0); } - if (EG(current_execute_data) == &dummy_execute_data) { - EG(current_execute_data) = dummy_execute_data.prev_execute_data; - } return FAILURE; } @@ -768,10 +742,6 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ if (UNEXPECTED(EG(exception))) { zend_vm_stack_free_call_frame(call); - if (EG(current_execute_data) == &dummy_execute_data) { - EG(current_execute_data) = dummy_execute_data.prev_execute_data; - zend_rethrow_exception(EG(current_execute_data)); - } return FAILURE; } } @@ -799,9 +769,6 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ cleanup_args: zend_vm_stack_free_args(call); zend_vm_stack_free_call_frame(call); - if (EG(current_execute_data) == &dummy_execute_data) { - EG(current_execute_data) = dummy_execute_data.prev_execute_data; - } return FAILURE; } } @@ -885,9 +852,6 @@ cleanup_args: if (zend_handle_undef_args(call) == FAILURE) { zend_vm_stack_free_args(call); zend_vm_stack_free_call_frame(call); - if (EG(current_execute_data) == &dummy_execute_data) { - EG(current_execute_data) = dummy_execute_data.prev_execute_data; - } return SUCCESS; } } @@ -903,25 +867,20 @@ cleanup_args: ZEND_ADD_CALL_FLAG(call, call_info); } + if (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { + fci_cache->function_handler = NULL; + } + orig_fake_scope = EG(fake_scope); EG(fake_scope) = NULL; if (func->type == ZEND_USER_FUNCTION) { - int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0; - const zend_op *current_opline_before_exception = EG(opline_before_exception); uint32_t orig_jit_trace_num = EG(jit_trace_num); zend_init_func_execute_data(call, &func->op_array, fci->retval); ZEND_OBSERVER_FCALL_BEGIN(call); zend_execute_ex(call); EG(jit_trace_num) = orig_jit_trace_num; - EG(opline_before_exception) = current_opline_before_exception; - if (call_via_handler) { - /* We must re-initialize function again */ - fci_cache->function_handler = NULL; - } } else { - int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0; - ZEND_ASSERT(func->type == ZEND_INTERNAL_FUNCTION); ZVAL_NULL(fci->retval); call->prev_execute_data = EG(current_execute_data); @@ -943,11 +902,6 @@ cleanup_args: ZVAL_UNDEF(fci->retval); } - if (call_via_handler) { - /* We must re-initialize function again */ - fci_cache->function_handler = NULL; - } - /* This flag is regularly checked while running user functions, but not internal * So see whether interrupt flag was set while the function was running... */ if (EG(vm_interrupt)) { @@ -963,10 +917,6 @@ cleanup_args: zend_vm_stack_free_call_frame(call); - if (EG(current_execute_data) == &dummy_execute_data) { - EG(current_execute_data) = dummy_execute_data.prev_execute_data; - } - if (UNEXPECTED(EG(exception))) { if (UNEXPECTED(!EG(current_execute_data))) { zend_throw_exception_internal(NULL); diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index d7770ecad5..c0eb532ada 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -97,6 +97,7 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) if (destructor) { zend_object *old_exception; + const zend_op *old_opline_before_exception; if (destructor->op_array.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) { if (destructor->op_array.fn_flags & ZEND_ACC_PRIVATE) { @@ -156,6 +157,7 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) zend_error_noreturn(E_CORE_ERROR, "Attempt to destruct pending exception"); } else { old_exception = EG(exception); + old_opline_before_exception = EG(opline_before_exception); EG(exception) = NULL; } } @@ -163,6 +165,7 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) zend_call_known_instance_method_with_0_params(destructor, object, NULL); if (old_exception) { + EG(opline_before_exception) = old_opline_before_exception; if (EG(exception)) { zend_exception_set_previous(EG(exception), old_exception); } else { diff --git a/ext/reflection/tests/ReflectionGenerator_basic.phpt b/ext/reflection/tests/ReflectionGenerator_basic.phpt index f9ecc8e90e..9f497234c2 100644 --- a/ext/reflection/tests/ReflectionGenerator_basic.phpt +++ b/ext/reflection/tests/ReflectionGenerator_basic.phpt @@ -37,7 +37,7 @@ foreach ($gens as $gen) { --EXPECTF-- object(Generator)#2 (0) { } -array(1) { +array(2) { [0]=> array(4) { ["file"]=> @@ -50,6 +50,21 @@ array(1) { array(0) { } } + [1]=> + array(5) { + ["function"]=> + string(1) "a" + ["class"]=> + string(%d) "class@anonymous%s" + ["object"]=> + object(class@anonymous)#%d (0) { + } + ["type"]=> + string(2) "->" + ["args"]=> + array(0) { + } + } } int(%d) string(%d) "%sReflectionGenerator_basic.%s" @@ -65,7 +80,15 @@ object(class@anonymous)#1 (0) { } object(Generator)#4 (0) { } -array(0) { +array(1) { + [0]=> + array(2) { + ["function"]=> + string(9) "{closure}" + ["args"]=> + array(0) { + } + } } int(%d) string(%d) "%sReflectionGenerator_basic.%s" @@ -78,7 +101,15 @@ object(ReflectionFunction)#7 (1) { NULL object(Generator)#5 (0) { } -array(0) { +array(1) { + [0]=> + array(2) { + ["function"]=> + string(3) "foo" + ["args"]=> + array(0) { + } + } } int(%d) string(%d) "%sReflectionGenerator_basic.%s" diff --git a/ext/reflection/tests/ReflectionGenerator_getTrace.phpt b/ext/reflection/tests/ReflectionGenerator_getTrace.phpt index 91b7160d4b..d6c8d53257 100644 --- a/ext/reflection/tests/ReflectionGenerator_getTrace.phpt +++ b/ext/reflection/tests/ReflectionGenerator_getTrace.phpt @@ -25,7 +25,7 @@ var_dump((new ReflectionGenerator($gen))->getTrace()); ?> --EXPECTF-- -array(2) { +array(3) { [0]=> array(4) { ["file"]=> @@ -50,4 +50,12 @@ array(2) { array(0) { } } + [2]=> + array(2) { + ["function"]=> + string(3) "baz" + ["args"]=> + array(0) { + } + } } diff --git a/ext/reflection/tests/ReflectionGenerator_in_Generator.phpt b/ext/reflection/tests/ReflectionGenerator_in_Generator.phpt index a665d45c30..1f78fcb6be 100644 --- a/ext/reflection/tests/ReflectionGenerator_in_Generator.phpt +++ b/ext/reflection/tests/ReflectionGenerator_in_Generator.phpt @@ -33,7 +33,15 @@ function doCalls(ReflectionGenerator $ref) { ?> --EXPECTF-- -array(0) { +array(1) { + [0]=> + array(2) { + ["function"]=> + string(9) "{closure}" + ["args"]=> + array(0) { + } + } } int(%d) string(%d) "%sReflectionGenerator_in_Generator.%s" @@ -44,7 +52,7 @@ object(ReflectionFunction)#4 (1) { string(9) "{closure}" } NULL -array(1) { +array(2) { [0]=> array(4) { ["file"]=> @@ -57,6 +65,14 @@ array(1) { array(0) { } } + [1]=> + array(2) { + ["function"]=> + string(9) "{closure}" + ["args"]=> + array(0) { + } + } } int(%d) string(%d) "%sReflectionGenerator_in_Generator.%s"