mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Pass zend_execute_data instead of zend_function to fcall init
The motivation for this change is to prevent extensions from having to check executor globals for the current execute_data during function call init. A previous implementation of the observer API initialized the function call from runtime cache initialization before execute_data was allocated which is why zend_function was passed in. But now that the observer API is implemented via opcode specialization, it makes sense to pass in the execute_data. This also keeps the API a bit more consistent for existing extensions that already hook zend_execute_ex. Closes GH-6209
This commit is contained in:
parent
a91cb2f48c
commit
e42abeafec
4 changed files with 139 additions and 6 deletions
|
@ -89,9 +89,10 @@ ZEND_API void zend_observer_shutdown(void) {
|
|||
zend_llist_destroy(&zend_observer_error_callbacks);
|
||||
}
|
||||
|
||||
static void zend_observer_fcall_install(zend_function *function) {
|
||||
static void zend_observer_fcall_install(zend_execute_data *execute_data) {
|
||||
zend_llist_element *element;
|
||||
zend_llist *list = &zend_observers_fcall_list;
|
||||
zend_function *function = execute_data->func;
|
||||
zend_op_array *op_array = &function->op_array;
|
||||
|
||||
if (fcall_handlers_arena == NULL) {
|
||||
|
@ -105,7 +106,7 @@ static void zend_observer_fcall_install(zend_function *function) {
|
|||
for (element = list->head; element; element = element->next) {
|
||||
zend_observer_fcall_init init;
|
||||
memcpy(&init, element->data, sizeof init);
|
||||
zend_observer_fcall_handlers handlers = init(function);
|
||||
zend_observer_fcall_handlers handlers = init(execute_data);
|
||||
if (handlers.begin || handlers.end) {
|
||||
zend_llist_add_element(&handlers_list, &handlers);
|
||||
}
|
||||
|
@ -150,7 +151,7 @@ static void ZEND_FASTCALL _zend_observe_fcall_begin(zend_execute_data *execute_d
|
|||
|
||||
fcall_data = ZEND_OBSERVER_DATA(op_array);
|
||||
if (!fcall_data) {
|
||||
zend_observer_fcall_install((zend_function *)op_array);
|
||||
zend_observer_fcall_install(execute_data);
|
||||
fcall_data = ZEND_OBSERVER_DATA(op_array);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ typedef struct _zend_observer_fcall_handlers {
|
|||
} zend_observer_fcall_handlers;
|
||||
|
||||
/* If the fn should not be observed then return {NULL, NULL} */
|
||||
typedef zend_observer_fcall_handlers (*zend_observer_fcall_init)(zend_function *func);
|
||||
typedef zend_observer_fcall_handlers (*zend_observer_fcall_init)(zend_execute_data *execute_data);
|
||||
|
||||
// Call during minit/startup ONLY
|
||||
ZEND_API void zend_observer_fcall_register(zend_observer_fcall_init);
|
||||
|
|
|
@ -36,6 +36,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test)
|
|||
int observer_observe_functions;
|
||||
int observer_show_return_type;
|
||||
int observer_show_return_value;
|
||||
int observer_show_init_backtrace;
|
||||
int observer_nesting_depth;
|
||||
ZEND_END_MODULE_GLOBALS(zend_test)
|
||||
|
||||
|
@ -315,9 +316,10 @@ 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.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)
|
||||
PHP_INI_END()
|
||||
|
||||
static zend_observer_fcall_handlers observer_fcall_init(zend_function *fbc);
|
||||
static zend_observer_fcall_handlers observer_fcall_init(zend_execute_data *execute_data);
|
||||
|
||||
PHP_MINIT_FUNCTION(zend_test)
|
||||
{
|
||||
|
@ -498,10 +500,34 @@ static void observer_show_init(zend_function *fbc)
|
|||
}
|
||||
}
|
||||
|
||||
static zend_observer_fcall_handlers observer_fcall_init(zend_function *fbc)
|
||||
static void observer_show_init_backtrace(zend_execute_data *execute_data)
|
||||
{
|
||||
zend_execute_data *ex = execute_data;
|
||||
php_printf("%*s<!--\n", 2 * ZT_G(observer_nesting_depth), "");
|
||||
do {
|
||||
zend_function *fbc = ex->func;
|
||||
int indent = 2 * ZT_G(observer_nesting_depth) + 4;
|
||||
if (fbc->common.function_name) {
|
||||
if (fbc->common.scope) {
|
||||
php_printf("%*s%s::%s()\n", indent, "", ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
|
||||
} else {
|
||||
php_printf("%*s%s()\n", indent, "", ZSTR_VAL(fbc->common.function_name));
|
||||
}
|
||||
} else {
|
||||
php_printf("%*s{main} %s\n", indent, "", ZSTR_VAL(fbc->op_array.filename));
|
||||
}
|
||||
} while ((ex = ex->prev_execute_data) != NULL);
|
||||
php_printf("%*s-->\n", 2 * ZT_G(observer_nesting_depth), "");
|
||||
}
|
||||
|
||||
static zend_observer_fcall_handlers observer_fcall_init(zend_execute_data *execute_data)
|
||||
{
|
||||
zend_function *fbc = execute_data->func;
|
||||
if (ZT_G(observer_show_output)) {
|
||||
observer_show_init(fbc);
|
||||
if (ZT_G(observer_show_init_backtrace)) {
|
||||
observer_show_init_backtrace(execute_data);
|
||||
}
|
||||
}
|
||||
|
||||
if (ZT_G(observer_observe_all)) {
|
||||
|
|
106
ext/zend_test/tests/observer_backtrace_01.phpt
Normal file
106
ext/zend_test/tests/observer_backtrace_01.phpt
Normal file
|
@ -0,0 +1,106 @@
|
|||
--TEST--
|
||||
Observer: Show backtrace on init
|
||||
--SKIPIF--
|
||||
<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?>
|
||||
--INI--
|
||||
zend_test.observer.enabled=1
|
||||
zend_test.observer.observe_all=1
|
||||
zend_test.observer.show_init_backtrace=1
|
||||
--FILE--
|
||||
<?php
|
||||
class TestClass
|
||||
{
|
||||
private function bar($number)
|
||||
{
|
||||
return $number + 2;
|
||||
}
|
||||
|
||||
public function foo()
|
||||
{
|
||||
return array_map(function ($value) {
|
||||
return $this->bar($value);
|
||||
}, [40, 1335]);
|
||||
}
|
||||
}
|
||||
|
||||
function gen()
|
||||
{
|
||||
$test = new TestClass();
|
||||
yield $test->foo();
|
||||
}
|
||||
|
||||
function foo()
|
||||
{
|
||||
return gen()->current();
|
||||
}
|
||||
|
||||
var_dump(foo());
|
||||
?>
|
||||
--EXPECTF--
|
||||
<!-- init '%s/observer_backtrace_%d.php' -->
|
||||
<!--
|
||||
{main} %s/observer_backtrace_%d.php
|
||||
-->
|
||||
<file '%s/observer_backtrace_%d.php'>
|
||||
<!-- init foo() -->
|
||||
<!--
|
||||
foo()
|
||||
{main} %s/observer_backtrace_%d.php
|
||||
-->
|
||||
<foo>
|
||||
<!-- init gen() -->
|
||||
<!--
|
||||
gen()
|
||||
Generator::current()
|
||||
foo()
|
||||
{main} %s/observer_backtrace_%d.php
|
||||
-->
|
||||
<gen>
|
||||
<!-- init TestClass::foo() -->
|
||||
<!--
|
||||
TestClass::foo()
|
||||
gen()
|
||||
Generator::current()
|
||||
foo()
|
||||
{main} %s/observer_backtrace_%d.php
|
||||
-->
|
||||
<TestClass::foo>
|
||||
<!-- init TestClass::{closure}() -->
|
||||
<!--
|
||||
TestClass::{closure}()
|
||||
array_map()
|
||||
TestClass::foo()
|
||||
gen()
|
||||
Generator::current()
|
||||
foo()
|
||||
{main} %s/observer_backtrace_%d.php
|
||||
-->
|
||||
<TestClass::{closure}>
|
||||
<!-- init TestClass::bar() -->
|
||||
<!--
|
||||
TestClass::bar()
|
||||
TestClass::{closure}()
|
||||
array_map()
|
||||
TestClass::foo()
|
||||
gen()
|
||||
Generator::current()
|
||||
foo()
|
||||
{main} %s/observer_backtrace_%d.php
|
||||
-->
|
||||
<TestClass::bar>
|
||||
</TestClass::bar>
|
||||
</TestClass::{closure}>
|
||||
<TestClass::{closure}>
|
||||
<TestClass::bar>
|
||||
</TestClass::bar>
|
||||
</TestClass::{closure}>
|
||||
</TestClass::foo>
|
||||
</gen>
|
||||
</foo>
|
||||
array(2) {
|
||||
[0]=>
|
||||
int(42)
|
||||
[1]=>
|
||||
int(1337)
|
||||
}
|
||||
</file '%s/observer_backtrace_%d.php'>
|
Loading…
Add table
Add a link
Reference in a new issue