Skip to content

Commit c8201ed

Browse files
committed
Extend zend_test to allow hooks to be executed while observing the end of a function
Add a test verifying we can modify return values
1 parent dc74c5b commit c8201ed

File tree

3 files changed

+75
-0
lines changed

3 files changed

+75
-0
lines changed

ext/zend_test/observer.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,46 @@ static void observer_end(zend_execute_data *execute_data, zval *retval)
129129
if (EG(exception)) {
130130
php_printf("%*s<!-- Exception: %s -->\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(EG(exception)->ce->name));
131131
}
132+
133+
if(ZT_G(observer_observe_end_call_function_name) && strlen(ZT_G(observer_observe_end_call_function_name)) > 0) {
134+
zend_fcall_info fci = empty_fcall_info;
135+
zend_fcall_info_cache fcc = empty_fcall_info_cache;
136+
137+
zval function_name = {.u1.type_info = IS_UNDEF};
138+
139+
// Initialize the zval with the zend_string value
140+
ZVAL_STRING(&function_name, ZT_G(observer_observe_end_call_function_name));
141+
142+
char* error = 0;
143+
if (zend_fcall_info_init(&function_name, 0, &fci, &fcc, NULL, &error) != SUCCESS) {
144+
145+
php_printf("<!-- Hook (%s) not found: %s-->\n", Z_STRVAL(function_name), error);
146+
efree(error);
147+
} else {
148+
149+
zval ret = {.u1.type_info = IS_UNDEF};
150+
fci.param_count = 0;
151+
fci.params = NULL;
152+
fci.named_params = NULL;
153+
fci.retval = &ret;
154+
155+
if (zend_call_function(&fci, &fcc) == SUCCESS) {
156+
if (!Z_ISUNDEF(ret) &&
157+
(fcc.function_handler->op_array.fn_flags &
158+
ZEND_ACC_HAS_RETURN_TYPE) &&
159+
!(ZEND_TYPE_PURE_MASK(
160+
fcc.function_handler->common.arg_info[-1].type) &
161+
MAY_BE_VOID)) {
162+
if (execute_data->return_value) {
163+
zval_ptr_dtor(execute_data->return_value);
164+
ZVAL_COPY(execute_data->return_value, &ret);
165+
ZVAL_UNDEF(&ret);
166+
}
167+
}
168+
}
169+
}
170+
zval_ptr_dtor(&function_name);
171+
}
132172
observer_show_opcode(execute_data);
133173
ZT_G(observer_nesting_depth)--;
134174
if (execute_data->func && execute_data->func->common.function_name) {
@@ -346,6 +386,7 @@ PHP_INI_BEGIN()
346386
STD_PHP_INI_BOOLEAN("zend_test.observer.observe_functions", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_functions, zend_zend_test_globals, zend_test_globals)
347387
STD_PHP_INI_BOOLEAN("zend_test.observer.observe_declaring", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_declaring, zend_zend_test_globals, zend_test_globals)
348388
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)
389+
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)
349390
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)
350391
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)
351392
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)

ext/zend_test/php_test.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test)
4040
int observer_observe_functions;
4141
int observer_observe_declaring;
4242
zend_array *observer_observe_function_names;
43+
const char *observer_observe_end_call_function_name;
4344
int observer_show_return_type;
4445
int observer_show_return_value;
4546
int observer_show_init_backtrace;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
Observer: Function return values are modifiable by observers
3+
--EXTENSIONS--
4+
zend_test
5+
--INI--
6+
zend_test.observer.enabled=1
7+
zend_test.observer.show_output=1
8+
zend_test.observer.show_return_value=1
9+
zend_test.observer.observe_function_names=foo
10+
zend_test.observer.observe_end_call_function_name=hook
11+
--FILE--
12+
<?php
13+
function foo(string $pin): string {
14+
15+
return 'original return value';
16+
}
17+
18+
function hook(): string {
19+
return 'hook value';
20+
}
21+
22+
$res = foo('some value'); // Retval used
23+
var_dump($res);
24+
echo 'Done' . PHP_EOL;
25+
?>
26+
--EXPECTF--
27+
<!-- init '%s/observer_retval_alter_%d.php' -->
28+
<!-- init foo() -->
29+
<foo>
30+
<!-- init hook() -->
31+
</foo:'original return value'>
32+
string(10) "hook value"
33+
Done

0 commit comments

Comments
 (0)