diff --git a/ext/opcache/jit/zend_jit_arm64.dasc b/ext/opcache/jit/zend_jit_arm64.dasc index 05219b3cfae31..e7eb0fa7034ec 100644 --- a/ext/opcache/jit/zend_jit_arm64.dasc +++ b/ext/opcache/jit/zend_jit_arm64.dasc @@ -10877,21 +10877,6 @@ static int zend_jit_return(dasm_State **Dst, const zend_op *opline, const zend_o return_value_used = -1; } - if (ZEND_OBSERVER_ENABLED) { - if (Z_MODE(op1_addr) == IS_REG) { - zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var); - - if (!zend_jit_spill_store(Dst, op1_addr, dst, op1_info, 1)) { - return 0; - } - op1_addr = dst; - } - | LOAD_ZVAL_ADDR FCARG2x, op1_addr - | mov FCARG1x, FP - | SET_EX_OPLINE opline, REG0 - | EXT_CALL zend_observer_fcall_end, REG0 - } - // if (!EX(return_value)) if (Z_MODE(op1_addr) == IS_REG && Z_REG(op1_addr) == ZREG_REG1) { if (return_value_used != 0) { @@ -10951,16 +10936,14 @@ static int zend_jit_return(dasm_State **Dst, const zend_op *opline, const zend_o } if (return_value_used == 0) { - |9: - return 1; - } - - if (opline->op1_type == IS_CONST) { + /* pass */ + } else if (opline->op1_type == IS_CONST) { zval *zv = RT_CONSTANT(opline, opline->op1); | ZVAL_COPY_CONST ret_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_REG0, ZREG_TMP1, ZREG_FPR0 if (Z_REFCOUNTED_P(zv)) { | ADDREF_CONST zv, REG0, TMP1 } + op1_addr = ret_addr; } else if (opline->op1_type == IS_TMP_VAR) { | ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_REG0, ZREG_REG2, ZREG_TMP1, ZREG_TMP2, ZREG_FPR0 } else if (opline->op1_type == IS_CV) { @@ -11021,6 +11004,20 @@ static int zend_jit_return(dasm_State **Dst, const zend_op *opline, const zend_o } |9: + if (ZEND_OBSERVER_ENABLED) { + if (Z_MODE(op1_addr) == IS_REG) { + zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var); + + if (!zend_jit_spill_store(Dst, op1_addr, dst, op1_info, 1)) { + return 0; + } + op1_addr = dst; + } + | LOAD_ZVAL_ADDR FCARG2x, op1_addr + | mov FCARG1x, FP + | SET_EX_OPLINE opline, REG0 + | EXT_CALL zend_observer_fcall_end, REG0 + } return 1; } diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 999295ef01a94..33acccf11d84e 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -11584,21 +11584,6 @@ static int zend_jit_return(dasm_State **Dst, const zend_op *opline, const zend_o return_value_used = -1; } - if (ZEND_OBSERVER_ENABLED) { - if (Z_MODE(op1_addr) == IS_REG) { - zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var); - - if (!zend_jit_spill_store(Dst, op1_addr, dst, op1_info, 1)) { - return 0; - } - op1_addr = dst; - } - | LOAD_ZVAL_ADDR FCARG2a, op1_addr - | mov FCARG1a, FP - | SET_EX_OPLINE opline, r0 - | EXT_CALL zend_observer_fcall_end, r0 - } - // if (!EX(return_value)) if (Z_MODE(op1_addr) == IS_REG && Z_REG(op1_addr) == ZREG_R1) { if (return_value_used != 0) { @@ -11664,16 +11649,14 @@ static int zend_jit_return(dasm_State **Dst, const zend_op *opline, const zend_o } if (return_value_used == 0) { - |9: - return 1; - } - - if (opline->op1_type == IS_CONST) { + /* pass */ + } else if (opline->op1_type == IS_CONST) { zval *zv = RT_CONSTANT(opline, opline->op1); | ZVAL_COPY_CONST ret_addr, MAY_BE_ANY, MAY_BE_ANY, zv, ZREG_R0 if (Z_REFCOUNTED_P(zv)) { | ADDREF_CONST zv, r0 } + op1_addr = ret_addr; } else if (opline->op1_type == IS_TMP_VAR) { | ZVAL_COPY_VALUE ret_addr, MAY_BE_ANY, op1_addr, op1_info, ZREG_R0, ZREG_R2 } else if (opline->op1_type == IS_CV) { @@ -11733,6 +11716,20 @@ static int zend_jit_return(dasm_State **Dst, const zend_op *opline, const zend_o } |9: + if (ZEND_OBSERVER_ENABLED) { + if (Z_MODE(op1_addr) == IS_REG) { + zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var); + + if (!zend_jit_spill_store(Dst, op1_addr, dst, op1_info, 1)) { + return 0; + } + op1_addr = dst; + } + | LOAD_ZVAL_ADDR FCARG2a, op1_addr + | mov FCARG1a, FP + | SET_EX_OPLINE opline, r0 + | EXT_CALL zend_observer_fcall_end, r0 + } return 1; } diff --git a/ext/zend_test/observer.c b/ext/zend_test/observer.c index af45f91a5da5b..d5f039fa8da89 100644 --- a/ext/zend_test/observer.c +++ b/ext/zend_test/observer.c @@ -129,6 +129,46 @@ static void observer_end(zend_execute_data *execute_data, zval *retval) if (EG(exception)) { php_printf("%*s\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(EG(exception)->ce->name)); } + + if(ZT_G(observer_observe_end_call_function_name) && strlen(ZT_G(observer_observe_end_call_function_name)) > 0) { + zend_fcall_info fci = empty_fcall_info; + zend_fcall_info_cache fcc = empty_fcall_info_cache; + + zval function_name = {.u1.type_info = IS_UNDEF}; + + // Initialize the zval with the zend_string value + ZVAL_STRING(&function_name, ZT_G(observer_observe_end_call_function_name)); + + char* error = 0; + if (zend_fcall_info_init(&function_name, 0, &fci, &fcc, NULL, &error) != SUCCESS) { + + php_printf("\n", Z_STRVAL(function_name), error); + efree(error); + } else { + + zval ret = {.u1.type_info = IS_UNDEF}; + fci.param_count = 0; + fci.params = NULL; + fci.named_params = NULL; + fci.retval = &ret; + + if (zend_call_function(&fci, &fcc) == SUCCESS) { + if (!Z_ISUNDEF(ret) && + (fcc.function_handler->op_array.fn_flags & + ZEND_ACC_HAS_RETURN_TYPE) && + !(ZEND_TYPE_PURE_MASK( + fcc.function_handler->common.arg_info[-1].type) & + MAY_BE_VOID)) { + if (execute_data->return_value) { + zval_ptr_dtor(execute_data->return_value); + ZVAL_COPY(execute_data->return_value, &ret); + ZVAL_UNDEF(&ret); + } + } + } + } + zval_ptr_dtor(&function_name); + } observer_show_opcode(execute_data); ZT_G(observer_nesting_depth)--; if (execute_data->func && execute_data->func->common.function_name) { @@ -346,6 +386,7 @@ PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("zend_test.observer.observe_functions", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_functions, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.observer.observe_declaring", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_declaring, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_ENTRY("zend_test.observer.observe_function_names", "", PHP_INI_ALL, zend_test_observer_OnUpdateCommaList, observer_observe_function_names, zend_zend_test_globals, zend_test_globals) + STD_PHP_INI_ENTRY("zend_test.observer.observe_end_call_function_name", "", PHP_INI_ALL, OnUpdateString, observer_observe_end_call_function_name, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.observer.show_return_type", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_return_type, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.observer.show_return_value", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_return_value, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.observer.show_init_backtrace", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_init_backtrace, zend_zend_test_globals, zend_test_globals) diff --git a/ext/zend_test/php_test.h b/ext/zend_test/php_test.h index 89570d1155cea..c090772f2fd47 100644 --- a/ext/zend_test/php_test.h +++ b/ext/zend_test/php_test.h @@ -40,6 +40,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test) int observer_observe_functions; int observer_observe_declaring; zend_array *observer_observe_function_names; + const char *observer_observe_end_call_function_name; int observer_show_return_type; int observer_show_return_value; int observer_show_init_backtrace; diff --git a/ext/zend_test/tests/observer_retval_alter_01.phpt b/ext/zend_test/tests/observer_retval_alter_01.phpt new file mode 100644 index 0000000000000..42be9010e2be6 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_alter_01.phpt @@ -0,0 +1,34 @@ +--TEST-- +Observer: Function return values are modifiable by observers +--EXTENSIONS-- +zend_test +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.show_output=1 +zend_test.observer.show_return_value=1 +zend_test.observer.observe_function_names=foo +zend_test.observer.observe_end_call_function_name=hook +--FILE-- + +--EXPECTF-- + + + + + + +string(10) "hook value" +Done