Include internal functions in the observer API

There are two main motivations to this:
a) The logic for handling internal and userland observation can be unified.
b) Unwinding of observed functions on a bailout does notably not include observers. Even if users of observers were to ensure such handling themselves, it would be impossible to retain the relative ordering - either the user has to unwind all internal observed frames before the automatic unwinding (zend_observer_fcall_end_all) or afterwards, but not properly interleaved.

Signed-off-by: Bob Weinand <bobwei9@hotmail.com>
This commit is contained in:
Bob Weinand 2022-07-20 15:50:12 +02:00
parent 0c225a2f57
commit 625f164963
49 changed files with 1675 additions and 1257 deletions

View file

@ -51,6 +51,7 @@ PHP 8.2 INTERNALS UPGRADE NOTES
are deprecated (see main UPGRADING notes). To suppress the notice, e.g. to are deprecated (see main UPGRADING notes). To suppress the notice, e.g. to
avoid duplicates when processing the same value multiple times, pass or add avoid duplicates when processing the same value multiple times, pass or add
IS_CALLABLE_SUPPRESS_DEPRECATIONS to the check_flags parameter. IS_CALLABLE_SUPPRESS_DEPRECATIONS to the check_flags parameter.
* Registered zend_observer_fcall_init handlers are now also called for internal functions.
======================== ========================
2. Build system changes 2. Build system changes

View file

@ -1226,6 +1226,7 @@ ZEND_API void zend_activate(void) /* {{{ */
if (CG(map_ptr_last)) { if (CG(map_ptr_last)) {
memset(CG(map_ptr_real_base), 0, CG(map_ptr_last) * sizeof(void*)); memset(CG(map_ptr_real_base), 0, CG(map_ptr_last) * sizeof(void*));
} }
zend_init_internal_run_time_cache();
zend_observer_activate(); zend_observer_activate();
} }
/* }}} */ /* }}} */

View file

@ -2694,6 +2694,11 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
internal_function->scope = scope; internal_function->scope = scope;
internal_function->prototype = NULL; internal_function->prototype = NULL;
internal_function->attributes = NULL; internal_function->attributes = NULL;
if (EG(active)) { // at run-time: this ought to only happen if registered with dl() or somehow temporarily at runtime
ZEND_MAP_PTR_INIT(internal_function->run_time_cache, zend_arena_alloc(&CG(arena), zend_internal_run_time_cache_reserved_size()));
} else {
ZEND_MAP_PTR_NEW(internal_function->run_time_cache);
}
if (ptr->flags) { if (ptr->flags) {
if (!(ptr->flags & ZEND_ACC_PPP_MASK)) { if (!(ptr->flags & ZEND_ACC_PPP_MASK)) {
if (ptr->flags != ZEND_ACC_DEPRECATED && scope) { if (ptr->flags != ZEND_ACC_DEPRECATED && scope) {

View file

@ -33,6 +33,7 @@
#include "zend_inheritance.h" #include "zend_inheritance.h"
#include "zend_vm.h" #include "zend_vm.h"
#include "zend_enum.h" #include "zend_enum.h"
#include "zend_observer.h"
#define SET_NODE(target, src) do { \ #define SET_NODE(target, src) do { \
target ## _type = (src)->op_type; \ target ## _type = (src)->op_type; \

View file

@ -448,6 +448,7 @@ struct _zend_op_array {
uint32_t required_num_args; uint32_t required_num_args;
zend_arg_info *arg_info; zend_arg_info *arg_info;
HashTable *attributes; HashTable *attributes;
ZEND_MAP_PTR_DEF(void **, run_time_cache);
/* END of common elements */ /* END of common elements */
int cache_size; /* number of run_time_cache_slots * sizeof(void*) */ int cache_size; /* number of run_time_cache_slots * sizeof(void*) */
@ -456,7 +457,6 @@ struct _zend_op_array {
uint32_t last; /* number of opcodes */ uint32_t last; /* number of opcodes */
zend_op *opcodes; zend_op *opcodes;
ZEND_MAP_PTR_DEF(void **, run_time_cache);
ZEND_MAP_PTR_DEF(HashTable *, static_variables_ptr); ZEND_MAP_PTR_DEF(HashTable *, static_variables_ptr);
HashTable *static_variables; HashTable *static_variables;
zend_string **vars; /* names of CV variables */ zend_string **vars; /* names of CV variables */
@ -503,6 +503,7 @@ typedef struct _zend_internal_function {
uint32_t required_num_args; uint32_t required_num_args;
zend_internal_arg_info *arg_info; zend_internal_arg_info *arg_info;
HashTable *attributes; HashTable *attributes;
ZEND_MAP_PTR_DEF(void **, run_time_cache);
/* END of common elements */ /* END of common elements */
zif_handler handler; zif_handler handler;
@ -527,6 +528,7 @@ union _zend_function {
uint32_t required_num_args; uint32_t required_num_args;
zend_arg_info *arg_info; /* index -1 represents the return value info, if any */ zend_arg_info *arg_info; /* index -1 represents the return value info, if any */
HashTable *attributes; HashTable *attributes;
ZEND_MAP_PTR_DEF(void **, run_time_cache);
} common; } common;
zend_op_array op_array; zend_op_array op_array;

View file

@ -22,6 +22,7 @@
#include "zend_enum_arginfo.h" #include "zend_enum_arginfo.h"
#include "zend_interfaces.h" #include "zend_interfaces.h"
#include "zend_enum.h" #include "zend_enum.h"
#include "zend_extensions.h"
#define ZEND_ENUM_DISALLOW_MAGIC_METHOD(propertyName, methodName) \ #define ZEND_ENUM_DISALLOW_MAGIC_METHOD(propertyName, methodName) \
do { \ do { \
@ -401,59 +402,48 @@ static ZEND_NAMED_FUNCTION(zend_enum_try_from_func)
zend_enum_from_base(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); zend_enum_from_base(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
} }
static void zend_enum_register_func(zend_class_entry *ce, zend_known_string_id name_id, zend_internal_function *zif) {
zend_string *name = ZSTR_KNOWN(name_id);
zif->type = ZEND_INTERNAL_FUNCTION;
zif->module = EG(current_module);
zif->scope = ce;
ZEND_MAP_PTR_NEW(zif->run_time_cache);
ZEND_MAP_PTR_SET(zif->run_time_cache, zend_arena_alloc(&CG(arena), zend_internal_run_time_cache_reserved_size()));
if (!zend_hash_add_ptr(&ce->function_table, name, zif)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(name));
}
}
void zend_enum_register_funcs(zend_class_entry *ce) void zend_enum_register_funcs(zend_class_entry *ce)
{ {
const uint32_t fn_flags = const uint32_t fn_flags =
ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_ARENA_ALLOCATED; ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_ARENA_ALLOCATED;
zend_internal_function *cases_function = zend_internal_function *cases_function = zend_arena_calloc(&CG(arena), sizeof(zend_internal_function), 1);
zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
memset(cases_function, 0, sizeof(zend_internal_function));
cases_function->type = ZEND_INTERNAL_FUNCTION;
cases_function->module = EG(current_module);
cases_function->handler = zend_enum_cases_func; cases_function->handler = zend_enum_cases_func;
cases_function->function_name = ZSTR_KNOWN(ZEND_STR_CASES); cases_function->function_name = ZSTR_KNOWN(ZEND_STR_CASES);
cases_function->scope = ce;
cases_function->fn_flags = fn_flags; cases_function->fn_flags = fn_flags;
cases_function->arg_info = (zend_internal_arg_info *) (arginfo_class_UnitEnum_cases + 1); cases_function->arg_info = (zend_internal_arg_info *) (arginfo_class_UnitEnum_cases + 1);
if (!zend_hash_add_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_CASES), cases_function)) { zend_enum_register_func(ce, ZEND_STR_CASES, cases_function);
zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare %s::cases()", ZSTR_VAL(ce->name));
}
if (ce->enum_backing_type != IS_UNDEF) { if (ce->enum_backing_type != IS_UNDEF) {
zend_internal_function *from_function = zend_internal_function *from_function = zend_arena_calloc(&CG(arena), sizeof(zend_internal_function), 1);
zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
memset(from_function, 0, sizeof(zend_internal_function));
from_function->type = ZEND_INTERNAL_FUNCTION;
from_function->module = EG(current_module);
from_function->handler = zend_enum_from_func; from_function->handler = zend_enum_from_func;
from_function->function_name = ZSTR_KNOWN(ZEND_STR_FROM); from_function->function_name = ZSTR_KNOWN(ZEND_STR_FROM);
from_function->scope = ce;
from_function->fn_flags = fn_flags; from_function->fn_flags = fn_flags;
from_function->num_args = 1; from_function->num_args = 1;
from_function->required_num_args = 1; from_function->required_num_args = 1;
from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_from + 1); from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_from + 1);
if (!zend_hash_add_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_FROM), from_function)) { zend_enum_register_func(ce, ZEND_STR_FROM, from_function);
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot redeclare %s::from()", ZSTR_VAL(ce->name));
}
zend_internal_function *try_from_function = zend_internal_function *try_from_function = zend_arena_calloc(&CG(arena), sizeof(zend_internal_function), 1);
zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
memset(try_from_function, 0, sizeof(zend_internal_function));
try_from_function->type = ZEND_INTERNAL_FUNCTION;
try_from_function->module = EG(current_module);
try_from_function->handler = zend_enum_try_from_func; try_from_function->handler = zend_enum_try_from_func;
try_from_function->function_name = ZSTR_KNOWN(ZEND_STR_TRYFROM); try_from_function->function_name = ZSTR_KNOWN(ZEND_STR_TRYFROM);
try_from_function->scope = ce;
try_from_function->fn_flags = fn_flags; try_from_function->fn_flags = fn_flags;
try_from_function->num_args = 1; try_from_function->num_args = 1;
try_from_function->required_num_args = 1; try_from_function->required_num_args = 1;
try_from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_tryFrom + 1); try_from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_tryFrom + 1);
if (!zend_hash_add_ptr( zend_enum_register_func(ce, ZEND_STR_TRYFROM_LOWERCASE, try_from_function);
&ce->function_table, ZSTR_KNOWN(ZEND_STR_TRYFROM_LOWERCASE), try_from_function)) {
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot redeclare %s::tryFrom()", ZSTR_VAL(ce->name));
}
} }
} }

View file

@ -144,6 +144,7 @@ ZEND_API const zend_internal_function zend_pass_function = {
0, /* required_num_args */ 0, /* required_num_args */
(zend_internal_arg_info *) zend_pass_function_arg_info + 1, /* arg_info */ (zend_internal_arg_info *) zend_pass_function_arg_info + 1, /* arg_info */
NULL, /* attributes */ NULL, /* attributes */
NULL, /* run_time_cache */
ZEND_FN(pass), /* handler */ ZEND_FN(pass), /* handler */
NULL, /* module */ NULL, /* module */
{NULL,NULL,NULL,NULL} /* reserved */ {NULL,NULL,NULL,NULL} /* reserved */

View file

@ -935,6 +935,7 @@ cleanup_args:
#if ZEND_DEBUG #if ZEND_DEBUG
bool should_throw = zend_internal_call_should_throw(func, call); bool should_throw = zend_internal_call_should_throw(func, call);
#endif #endif
ZEND_OBSERVER_FCALL_BEGIN(call);
if (EXPECTED(zend_execute_internal == NULL)) { if (EXPECTED(zend_execute_internal == NULL)) {
/* saves one function call if zend_execute_internal is not used */ /* saves one function call if zend_execute_internal is not used */
func->internal_function.handler(call, fci->retval); func->internal_function.handler(call, fci->retval);
@ -953,6 +954,7 @@ cleanup_args:
? Z_ISREF_P(fci->retval) : !Z_ISREF_P(fci->retval)); ? Z_ISREF_P(fci->retval) : !Z_ISREF_P(fci->retval));
} }
#endif #endif
ZEND_OBSERVER_FCALL_END(call, fci->retval);
EG(current_execute_data) = call->prev_execute_data; EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_args(call); zend_vm_stack_free_args(call);
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) { if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {

View file

@ -280,6 +280,40 @@ ZEND_API int zend_get_op_array_extension_handles(const char *module_name, int ha
return handle; return handle;
} }
ZEND_API size_t zend_internal_run_time_cache_reserved_size() {
return zend_op_array_extension_handles * sizeof(void *);
}
ZEND_API void zend_init_internal_run_time_cache() {
size_t rt_size = zend_internal_run_time_cache_reserved_size();
if (rt_size) {
size_t functions = zend_hash_num_elements(CG(function_table));
zend_class_entry *ce;
ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) {
functions += zend_hash_num_elements(&ce->function_table);
} ZEND_HASH_FOREACH_END();
char *ptr = zend_arena_calloc(&CG(arena), functions, rt_size);
zend_internal_function *zif;
ZEND_HASH_MAP_FOREACH_PTR(CG(function_table), zif) {
if (!ZEND_USER_CODE(zif->type) && ZEND_MAP_PTR_GET(zif->run_time_cache) == NULL)
{
ZEND_MAP_PTR_SET(zif->run_time_cache, (void *)ptr);
ptr += rt_size;
}
} ZEND_HASH_FOREACH_END();
ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) {
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, zif) {
if (!ZEND_USER_CODE(zif->type) && ZEND_MAP_PTR_GET(zif->run_time_cache) == NULL)
{
ZEND_MAP_PTR_SET(zif->run_time_cache, (void *)ptr);
ptr += rt_size;
}
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FOREACH_END();
}
}
ZEND_API zend_extension *zend_get_extension(const char *extension_name) ZEND_API zend_extension *zend_get_extension(const char *extension_name)
{ {
zend_llist_element *element; zend_llist_element *element;

View file

@ -145,6 +145,9 @@ void zend_startup_extensions_mechanism(void);
void zend_startup_extensions(void); void zend_startup_extensions(void);
void zend_shutdown_extensions(void); void zend_shutdown_extensions(void);
ZEND_API size_t zend_internal_run_time_cache_reserved_size(void);
ZEND_API void zend_init_internal_run_time_cache(void);
BEGIN_EXTERN_C() BEGIN_EXTERN_C()
ZEND_API zend_result zend_load_extension(const char *path); ZEND_API zend_result zend_load_extension(const char *path);
ZEND_API zend_result zend_load_extension_handle(DL_HANDLE handle, const char *path); ZEND_API zend_result zend_load_extension_handle(DL_HANDLE handle, const char *path);

View file

@ -23,13 +23,13 @@
#include "zend_llist.h" #include "zend_llist.h"
#include "zend_vm.h" #include "zend_vm.h"
#define ZEND_OBSERVER_DATA(op_array) \ #define ZEND_OBSERVER_DATA(function) \
ZEND_OP_ARRAY_EXTENSION(op_array, zend_observer_fcall_op_array_extension) ZEND_OP_ARRAY_EXTENSION((&(function)->common), zend_observer_fcall_op_array_extension)
#define ZEND_OBSERVER_NOT_OBSERVED ((void *) 2) #define ZEND_OBSERVER_NOT_OBSERVED ((void *) 2)
#define ZEND_OBSERVABLE_FN(fn_flags) \ #define ZEND_OBSERVABLE_FN(function) \
(!(fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) (ZEND_MAP_PTR(function->common.run_time_cache) && !(function->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))
zend_llist zend_observers_fcall_list; zend_llist zend_observers_fcall_list;
zend_llist zend_observer_error_callbacks; zend_llist zend_observer_error_callbacks;
@ -100,12 +100,9 @@ static void zend_observer_fcall_install(zend_execute_data *execute_data)
{ {
zend_llist *list = &zend_observers_fcall_list; zend_llist *list = &zend_observers_fcall_list;
zend_function *function = execute_data->func; zend_function *function = execute_data->func;
zend_op_array *op_array = &function->op_array;
ZEND_ASSERT(function->type != ZEND_INTERNAL_FUNCTION); ZEND_ASSERT(RUN_TIME_CACHE(&function->common));
zend_observer_fcall_begin_handler *begin_handlers = (zend_observer_fcall_begin_handler *)&ZEND_OBSERVER_DATA(function);
ZEND_ASSERT(RUN_TIME_CACHE(op_array));
zend_observer_fcall_begin_handler *begin_handlers = (zend_observer_fcall_begin_handler *)&ZEND_OBSERVER_DATA(op_array);
zend_observer_fcall_end_handler *end_handlers = (zend_observer_fcall_end_handler *)begin_handlers + list->count, *end_handlers_start = end_handlers; zend_observer_fcall_end_handler *end_handlers = (zend_observer_fcall_end_handler *)begin_handlers + list->count, *end_handlers_start = end_handlers;
*begin_handlers = ZEND_OBSERVER_NOT_OBSERVED; *begin_handlers = ZEND_OBSERVER_NOT_OBSERVED;
@ -152,9 +149,9 @@ static bool zend_observer_remove_handler(void **first_handler, void *old_handler
return false; return false;
} }
ZEND_API void zend_observer_add_begin_handler(zend_op_array *op_array, zend_observer_fcall_begin_handler begin) { ZEND_API void zend_observer_add_begin_handler(zend_function *function, zend_observer_fcall_begin_handler begin) {
size_t registered_observers = zend_observers_fcall_list.count; size_t registered_observers = zend_observers_fcall_list.count;
zend_observer_fcall_begin_handler *first_handler = (void *)&ZEND_OBSERVER_DATA(op_array), *last_handler = first_handler + registered_observers - 1; zend_observer_fcall_begin_handler *first_handler = (void *)&ZEND_OBSERVER_DATA(function), *last_handler = first_handler + registered_observers - 1;
if (*first_handler == ZEND_OBSERVER_NOT_OBSERVED) { if (*first_handler == ZEND_OBSERVER_NOT_OBSERVED) {
*first_handler = begin; *first_handler = begin;
} else { } else {
@ -169,13 +166,13 @@ ZEND_API void zend_observer_add_begin_handler(zend_op_array *op_array, zend_obse
} }
} }
ZEND_API bool zend_observer_remove_begin_handler(zend_op_array *op_array, zend_observer_fcall_begin_handler begin) { ZEND_API bool zend_observer_remove_begin_handler(zend_function *function, zend_observer_fcall_begin_handler begin) {
return zend_observer_remove_handler((void **)&ZEND_OBSERVER_DATA(op_array), begin); return zend_observer_remove_handler((void **)&ZEND_OBSERVER_DATA(function), begin);
} }
ZEND_API void zend_observer_add_end_handler(zend_op_array *op_array, zend_observer_fcall_end_handler end) { ZEND_API void zend_observer_add_end_handler(zend_function *function, zend_observer_fcall_end_handler end) {
size_t registered_observers = zend_observers_fcall_list.count; size_t registered_observers = zend_observers_fcall_list.count;
zend_observer_fcall_end_handler *end_handler = (zend_observer_fcall_end_handler *)&ZEND_OBSERVER_DATA(op_array) + registered_observers; zend_observer_fcall_end_handler *end_handler = (zend_observer_fcall_end_handler *)&ZEND_OBSERVER_DATA(function) + registered_observers;
// to allow to preserve the invariant that end handlers are in reverse order of begin handlers, push the new end handler in front // to allow to preserve the invariant that end handlers are in reverse order of begin handlers, push the new end handler in front
if (*end_handler != ZEND_OBSERVER_NOT_OBSERVED) { if (*end_handler != ZEND_OBSERVER_NOT_OBSERVED) {
// there's no space for new handlers, then it's forbidden to call this function // there's no space for new handlers, then it's forbidden to call this function
@ -185,9 +182,9 @@ ZEND_API void zend_observer_add_end_handler(zend_op_array *op_array, zend_observ
*end_handler = end; *end_handler = end;
} }
ZEND_API bool zend_observer_remove_end_handler(zend_op_array *op_array, zend_observer_fcall_end_handler end) { ZEND_API bool zend_observer_remove_end_handler(zend_function *function, zend_observer_fcall_end_handler end) {
size_t registered_observers = zend_observers_fcall_list.count; size_t registered_observers = zend_observers_fcall_list.count;
return zend_observer_remove_handler((void **)&ZEND_OBSERVER_DATA(op_array) + registered_observers, end); return zend_observer_remove_handler((void **)&ZEND_OBSERVER_DATA(function) + registered_observers, end);
} }
static void ZEND_FASTCALL _zend_observe_fcall_begin(zend_execute_data *execute_data) static void ZEND_FASTCALL _zend_observe_fcall_begin(zend_execute_data *execute_data)
@ -196,14 +193,13 @@ static void ZEND_FASTCALL _zend_observe_fcall_begin(zend_execute_data *execute_d
return; return;
} }
zend_op_array *op_array = &execute_data->func->op_array; zend_function *function = execute_data->func;
uint32_t fn_flags = op_array->fn_flags;
if (!ZEND_OBSERVABLE_FN(fn_flags)) { if (!ZEND_OBSERVABLE_FN(function)) {
return; return;
} }
zend_observer_fcall_begin_handler *handler = (zend_observer_fcall_begin_handler *)&ZEND_OBSERVER_DATA(op_array); zend_observer_fcall_begin_handler *handler = (zend_observer_fcall_begin_handler *)&ZEND_OBSERVER_DATA(function);
if (!*handler) { if (!*handler) {
zend_observer_fcall_install(execute_data); zend_observer_fcall_install(execute_data);
} }
@ -243,11 +239,11 @@ ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin(zend_execute_data *execute
static inline bool zend_observer_is_skipped_frame(zend_execute_data *execute_data) { static inline bool zend_observer_is_skipped_frame(zend_execute_data *execute_data) {
zend_function *func = execute_data->func; zend_function *func = execute_data->func;
if (!func || func->type == ZEND_INTERNAL_FUNCTION || !ZEND_OBSERVABLE_FN(func->common.fn_flags)) { if (!func || !ZEND_OBSERVABLE_FN(func)) {
return true; return true;
} }
zend_observer_fcall_end_handler end_handler = (&ZEND_OBSERVER_DATA(&func->op_array))[zend_observers_fcall_list.count]; zend_observer_fcall_end_handler end_handler = (&ZEND_OBSERVER_DATA(func))[zend_observers_fcall_list.count];
if (end_handler == NULL || end_handler == ZEND_OBSERVER_NOT_OBSERVED) { if (end_handler == NULL || end_handler == ZEND_OBSERVER_NOT_OBSERVED) {
return true; return true;
} }
@ -259,11 +255,11 @@ ZEND_API void ZEND_FASTCALL zend_observer_fcall_end(zend_execute_data *execute_d
{ {
zend_function *func = execute_data->func; zend_function *func = execute_data->func;
if (!ZEND_OBSERVER_ENABLED || !ZEND_OBSERVABLE_FN(func->common.fn_flags)) { if (!ZEND_OBSERVER_ENABLED || !ZEND_OBSERVABLE_FN(func)) {
return; return;
} }
zend_observer_fcall_end_handler *handler = (zend_observer_fcall_end_handler *)&ZEND_OBSERVER_DATA(&func->op_array) + zend_observers_fcall_list.count; zend_observer_fcall_end_handler *handler = (zend_observer_fcall_end_handler *)&ZEND_OBSERVER_DATA(func) + zend_observers_fcall_list.count;
// TODO: Fix exceptions from generators // TODO: Fix exceptions from generators
// ZEND_ASSERT(fcall_data); // ZEND_ASSERT(fcall_data);
if (!*handler || *handler == ZEND_OBSERVER_NOT_OBSERVED) { if (!*handler || *handler == ZEND_OBSERVER_NOT_OBSERVED) {
@ -291,7 +287,7 @@ ZEND_API void zend_observer_fcall_end_all(void)
{ {
zend_execute_data *ex = current_observed_frame; zend_execute_data *ex = current_observed_frame;
while (ex != NULL) { while (ex != NULL) {
if (ex->func && ex->func->type != ZEND_INTERNAL_FUNCTION) { if (ex->func) {
zend_observer_fcall_end(ex, NULL); zend_observer_fcall_end(ex, NULL);
} }
ex = ex->prev_execute_data; ex = ex->prev_execute_data;

View file

@ -58,10 +58,10 @@ ZEND_API void zend_observer_fcall_register(zend_observer_fcall_init);
// Call during runtime, but only if you have used zend_observer_fcall_register. // Call during runtime, but only if you have used zend_observer_fcall_register.
// You must not have more than one begin and one end handler active at the same time. Remove the old one first, if there is an existing one. // You must not have more than one begin and one end handler active at the same time. Remove the old one first, if there is an existing one.
ZEND_API void zend_observer_add_begin_handler(zend_op_array *op_array, zend_observer_fcall_begin_handler begin); ZEND_API void zend_observer_add_begin_handler(zend_function *function, zend_observer_fcall_begin_handler begin);
ZEND_API bool zend_observer_remove_begin_handler(zend_op_array *op_array, zend_observer_fcall_begin_handler begin); ZEND_API bool zend_observer_remove_begin_handler(zend_function *function, zend_observer_fcall_begin_handler begin);
ZEND_API void zend_observer_add_end_handler(zend_op_array *op_array, zend_observer_fcall_end_handler end); ZEND_API void zend_observer_add_end_handler(zend_function *function, zend_observer_fcall_end_handler end);
ZEND_API bool zend_observer_remove_end_handler(zend_op_array *op_array, zend_observer_fcall_end_handler end); ZEND_API bool zend_observer_remove_end_handler(zend_function *function, zend_observer_fcall_end_handler end);
ZEND_API void zend_observer_startup(void); // Called by engine before MINITs ZEND_API void zend_observer_startup(void); // Called by engine before MINITs
ZEND_API void zend_observer_post_startup(void); // Called by engine after MINITs ZEND_API void zend_observer_post_startup(void); // Called by engine after MINITs

View file

@ -405,7 +405,7 @@ ZEND_VM_HANDLER(8, ZEND_CONCAT, CONST|TMPVAR|CV, CONST|TMPVAR|CV, SPEC(NO_CONST_
} }
} else if (OP1_TYPE != IS_CONST && OP1_TYPE != IS_CV && } else if (OP1_TYPE != IS_CONST && OP1_TYPE != IS_CV &&
!ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { !ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) {
size_t len = ZSTR_LEN(op1_str); size_t len = ZSTR_LEN(op1_str);
if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) { if (UNEXPECTED(len > ZSTR_MAX_LEN - ZSTR_LEN(op2_str))) {
zend_error_noreturn(E_ERROR, "Integer overflow in memory allocation"); zend_error_noreturn(E_ERROR, "Integer overflow in memory allocation");
@ -1998,9 +1998,9 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(93, ZEND_FETCH_DIM_FUNC_ARG, CONST|TMP|VAR|CV, C
#endif #endif
if (UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) { if (UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) {
if ((OP1_TYPE & (IS_CONST|IS_TMP_VAR))) { if ((OP1_TYPE & (IS_CONST|IS_TMP_VAR))) {
ZEND_VM_DISPATCH_TO_HELPER(zend_use_tmp_in_write_context_helper); ZEND_VM_DISPATCH_TO_HELPER(zend_use_tmp_in_write_context_helper);
} }
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_FETCH_DIM_W); ZEND_VM_DISPATCH_TO_HANDLER(ZEND_FETCH_DIM_W);
} else { } else {
if (OP2_TYPE == IS_UNUSED) { if (OP2_TYPE == IS_UNUSED) {
@ -2036,7 +2036,7 @@ ZEND_VM_HOT_OBJ_HANDLER(82, ZEND_FETCH_OBJ_R, CONST|TMPVAR|UNUSED|THIS|CV, CONST
if (OP1_TYPE == IS_CONST || if (OP1_TYPE == IS_CONST ||
(OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) {
do { do {
if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) {
container = Z_REFVAL_P(container); container = Z_REFVAL_P(container);
if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
@ -2084,9 +2084,9 @@ ZEND_VM_C_LABEL(fetch_obj_r_fast_copy):
Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx);
if (EXPECTED(p->key == name) || if (EXPECTED(p->key == name) ||
(EXPECTED(p->h == ZSTR_H(name)) && (EXPECTED(p->h == ZSTR_H(name)) &&
EXPECTED(p->key != NULL) && EXPECTED(p->key != NULL) &&
EXPECTED(zend_string_equal_content(p->key, name)))) { EXPECTED(zend_string_equal_content(p->key, name)))) {
retval = &p->val; retval = &p->val;
if (!ZEND_VM_SPEC || (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) != 0) { if (!ZEND_VM_SPEC || (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) != 0) {
ZEND_VM_C_GOTO(fetch_obj_r_copy); ZEND_VM_C_GOTO(fetch_obj_r_copy);
@ -2248,9 +2248,9 @@ ZEND_VM_C_LABEL(fetch_obj_is_fast_copy):
Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx);
if (EXPECTED(p->key == name) || if (EXPECTED(p->key == name) ||
(EXPECTED(p->h == ZSTR_H(name)) && (EXPECTED(p->h == ZSTR_H(name)) &&
EXPECTED(p->key != NULL) && EXPECTED(p->key != NULL) &&
EXPECTED(zend_string_equal_content(p->key, name)))) { EXPECTED(zend_string_equal_content(p->key, name)))) {
retval = &p->val; retval = &p->val;
if (!ZEND_VM_SPEC || (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) != 0) { if (!ZEND_VM_SPEC || (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) != 0) {
ZEND_VM_C_GOTO(fetch_obj_is_copy); ZEND_VM_C_GOTO(fetch_obj_is_copy);
@ -3161,7 +3161,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMP
} }
} else if (OP1_TYPE != IS_CONST && OP1_TYPE != IS_CV && } else if (OP1_TYPE != IS_CONST && OP1_TYPE != IS_CV &&
!ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { !ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) {
size_t len = ZSTR_LEN(op1_str); size_t len = ZSTR_LEN(op1_str);
str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0); str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0);
memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1); memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
@ -3503,9 +3503,9 @@ ZEND_VM_HOT_OBJ_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|THIS|CV,
if (OP2_TYPE == IS_CONST && if (OP2_TYPE == IS_CONST &&
EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) { EXPECTED(CACHED_PTR(opline->result.num) == called_scope)) {
fbc = CACHED_PTR(opline->result.num + sizeof(void*)); fbc = CACHED_PTR(opline->result.num + sizeof(void*));
} else { } else {
zend_object *orig_obj = obj; zend_object *orig_obj = obj;
if (OP2_TYPE == IS_CONST) { if (OP2_TYPE == IS_CONST) {
function_name = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); function_name = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
@ -3908,7 +3908,7 @@ ZEND_VM_HOT_HANDLER(61, ZEND_INIT_FCALL, NUM, CONST, NUM|CACHE_SLOT)
ZEND_VM_NEXT_OPCODE(); ZEND_VM_NEXT_OPCODE();
} }
ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL)) ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
{ {
USE_OPLINE USE_OPLINE
zend_execute_data *call = EX(call); zend_execute_data *call = EX(call);
@ -3929,6 +3929,7 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL))
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret); ZVAL_NULL(ret);
ZEND_OBSERVER_FCALL_BEGIN(call);
fbc->internal_function.handler(call, ret); fbc->internal_function.handler(call, ret);
#if ZEND_DEBUG #if ZEND_DEBUG
@ -3943,6 +3944,7 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL))
zend_verify_internal_func_info(call->func, ret); zend_verify_internal_func_info(call->func, ret);
} }
#endif #endif
ZEND_OBSERVER_FCALL_END(call, EG(exception) ? NULL : ret);
EG(current_execute_data) = execute_data; EG(current_execute_data) = execute_data;
zend_vm_stack_free_args(call); zend_vm_stack_free_args(call);
@ -4048,6 +4050,7 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER))
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret); ZVAL_NULL(ret);
ZEND_OBSERVER_FCALL_BEGIN(call);
fbc->internal_function.handler(call, ret); fbc->internal_function.handler(call, ret);
#if ZEND_DEBUG #if ZEND_DEBUG
@ -4062,6 +4065,7 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER))
zend_verify_internal_func_info(call->func, ret); zend_verify_internal_func_info(call->func, ret);
} }
#endif #endif
ZEND_OBSERVER_FCALL_END(call, EG(exception) ? NULL : ret);
EG(current_execute_data) = execute_data; EG(current_execute_data) = execute_data;
@ -4153,6 +4157,7 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret); ZVAL_NULL(ret);
ZEND_OBSERVER_FCALL_BEGIN(call);
if (!zend_execute_internal) { if (!zend_execute_internal) {
/* saves one function call if zend_execute_internal is not used */ /* saves one function call if zend_execute_internal is not used */
fbc->internal_function.handler(call, ret); fbc->internal_function.handler(call, ret);
@ -4172,6 +4177,7 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
zend_verify_internal_func_info(call->func, ret); zend_verify_internal_func_info(call->func, ret);
} }
#endif #endif
ZEND_OBSERVER_FCALL_END(call, EG(exception) ? NULL : ret);
EG(current_execute_data) = execute_data; EG(current_execute_data) = execute_data;
@ -5782,7 +5788,7 @@ ZEND_VM_COLD_CONST_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|THIS|CV, ANY)
do { do {
if (OP1_TYPE == IS_CONST || if (OP1_TYPE == IS_CONST ||
(OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) != IS_OBJECT))) { (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) != IS_OBJECT))) {
if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(obj)) { if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(obj)) {
obj = Z_REFVAL_P(obj); obj = Z_REFVAL_P(obj);
if (EXPECTED(Z_TYPE_P(obj) == IS_OBJECT)) { if (EXPECTED(Z_TYPE_P(obj) == IS_OBJECT)) {
break; break;
@ -6301,7 +6307,7 @@ ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMPVAR|CV, ANY, EVAL, SPEC(OBSER
new_op_array->scope = EX(func)->op_array.scope; new_op_array->scope = EX(func)->op_array.scope;
call = zend_vm_stack_push_call_frame( call = zend_vm_stack_push_call_frame(
(Z_TYPE_INFO(EX(This)) & ZEND_CALL_HAS_THIS) | ZEND_CALL_NESTED_CODE | ZEND_CALL_HAS_SYMBOL_TABLE, (Z_TYPE_INFO(EX(This)) & ZEND_CALL_HAS_THIS) | ZEND_CALL_NESTED_CODE | ZEND_CALL_HAS_SYMBOL_TABLE,
(zend_function*)new_op_array, 0, (zend_function*)new_op_array, 0,
Z_PTR(EX(This))); Z_PTR(EX(This)));
@ -8129,7 +8135,7 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMPVAR|CV|UNUSED
} }
} else if (OP1_TYPE == IS_TMP_VAR) { } else if (OP1_TYPE == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value); ZVAL_COPY_VALUE(&generator->value, value);
} else if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { } else if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) {
ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); ZVAL_COPY(&generator->value, Z_REFVAL_P(value));
FREE_OP1_IF_VAR(); FREE_OP1_IF_VAR();
} else { } else {
@ -8364,9 +8370,9 @@ ZEND_VM_HOT_HANDLER(168, ZEND_BIND_GLOBAL, CV, CONST, CACHE_SLOT)
Bucket *p = (Bucket*)((char*)EG(symbol_table).arData + idx); Bucket *p = (Bucket*)((char*)EG(symbol_table).arData + idx);
if (EXPECTED(p->key == varname) || if (EXPECTED(p->key == varname) ||
(EXPECTED(p->h == ZSTR_H(varname)) && (EXPECTED(p->h == ZSTR_H(varname)) &&
EXPECTED(p->key != NULL) && EXPECTED(p->key != NULL) &&
EXPECTED(zend_string_equal_content(p->key, varname)))) { EXPECTED(zend_string_equal_content(p->key, varname)))) {
value = (zval*)p; /* value = &p->val; */ value = (zval*)p; /* value = &p->val; */
ZEND_VM_C_GOTO(check_indirect); ZEND_VM_C_GOTO(check_indirect);
@ -8710,6 +8716,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY, SPEC(OBSERVER))
} }
ZVAL_NULL(ret); ZVAL_NULL(ret);
ZEND_OBSERVER_FCALL_BEGIN(call);
if (!zend_execute_internal) { if (!zend_execute_internal) {
/* saves one function call if zend_execute_internal is not used */ /* saves one function call if zend_execute_internal is not used */
fbc->internal_function.handler(call, ret); fbc->internal_function.handler(call, ret);
@ -8729,6 +8736,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY, SPEC(OBSERVER))
zend_verify_internal_func_info(call->func, ret); zend_verify_internal_func_info(call->func, ret);
} }
#endif #endif
ZEND_OBSERVER_FCALL_END(call, EG(exception) ? NULL : ret);
EG(current_execute_data) = call->prev_execute_data; EG(current_execute_data) = call->prev_execute_data;

767
Zend/zend_vm_execute.h generated

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -9527,13 +9527,19 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
} }
} }
| // EG(current_execute_data) = execute_data;
| MEM_STORE_64_ZTS str, RX, executor_globals, current_execute_data, REG1
if (ZEND_OBSERVER_ENABLED) {
| mov FCARG1x, RX
| EXT_CALL zend_observer_fcall_begin, REG0
| ldr REG0, EX:RX->func // reload
}
| // ZVAL_NULL(EX_VAR(opline->result.var)); | // ZVAL_NULL(EX_VAR(opline->result.var));
| LOAD_ZVAL_ADDR FCARG2x, res_addr | LOAD_ZVAL_ADDR FCARG2x, res_addr
| SET_Z_TYPE_INFO FCARG2x, IS_NULL, TMP1w | SET_Z_TYPE_INFO FCARG2x, IS_NULL, TMP1w
| // EG(current_execute_data) = execute_data;
| MEM_STORE_64_ZTS str, RX, executor_globals, current_execute_data, REG1
zend_jit_reset_last_valid_opline(); zend_jit_reset_last_valid_opline();
| // fbc->internal_function.handler(call, ret); | // fbc->internal_function.handler(call, ret);
@ -9545,6 +9551,12 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
| blr TMP1 | blr TMP1
} }
if (ZEND_OBSERVER_ENABLED) {
| LOAD_ZVAL_ADDR FCARG2x, res_addr
| mov FCARG1x, RX
| EXT_CALL zend_observer_fcall_end, REG0
}
| // EG(current_execute_data) = execute_data; | // EG(current_execute_data) = execute_data;
| MEM_STORE_64_ZTS str, FP, executor_globals, current_execute_data, REG0 | MEM_STORE_64_ZTS str, FP, executor_globals, current_execute_data, REG0

View file

@ -10231,13 +10231,19 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
} }
} }
| // EG(current_execute_data) = execute_data;
| MEM_STORE_ZTS aword, executor_globals, current_execute_data, RX, r1
if (ZEND_OBSERVER_ENABLED) {
| mov FCARG1a, RX
| EXT_CALL zend_observer_fcall_begin, r0
| mov r0, EX:RX->func // reload
}
| // ZVAL_NULL(EX_VAR(opline->result.var)); | // ZVAL_NULL(EX_VAR(opline->result.var));
| LOAD_ZVAL_ADDR FCARG2a, res_addr | LOAD_ZVAL_ADDR FCARG2a, res_addr
| SET_Z_TYPE_INFO FCARG2a, IS_NULL | SET_Z_TYPE_INFO FCARG2a, IS_NULL
| // EG(current_execute_data) = execute_data;
| MEM_STORE_ZTS aword, executor_globals, current_execute_data, RX, r1
zend_jit_reset_last_valid_opline(); zend_jit_reset_last_valid_opline();
| // fbc->internal_function.handler(call, ret); | // fbc->internal_function.handler(call, ret);
@ -10248,6 +10254,12 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
| call aword [r0 + offsetof(zend_internal_function, handler)] | call aword [r0 + offsetof(zend_internal_function, handler)]
} }
if (ZEND_OBSERVER_ENABLED) {
| LOAD_ZVAL_ADDR FCARG2a, res_addr
| mov FCARG1a, RX
| EXT_CALL zend_observer_fcall_end, r0
}
| // EG(current_execute_data) = execute_data; | // EG(current_execute_data) = execute_data;
| MEM_STORE_ZTS aword, executor_globals, current_execute_data, FP, r0 | MEM_STORE_ZTS aword, executor_globals, current_execute_data, FP, r0

View file

@ -1245,6 +1245,7 @@ bool pdo_hash_methods(pdo_dbh_object_t *dbh_obj, int kind)
func.function_name = zend_string_init(funcs->fname, strlen(funcs->fname), dbh->is_persistent); func.function_name = zend_string_init(funcs->fname, strlen(funcs->fname), dbh->is_persistent);
func.scope = dbh_obj->std.ce; func.scope = dbh_obj->std.ce;
func.prototype = NULL; func.prototype = NULL;
ZEND_MAP_PTR(func.run_time_cache) = NULL;
if (funcs->flags) { if (funcs->flags) {
func.fn_flags = funcs->flags | ZEND_ACC_NEVER_CACHE; func.fn_flags = funcs->flags | ZEND_ACC_NEVER_CACHE;
} else { } else {

View file

@ -264,8 +264,8 @@ static ZEND_INI_MH(zend_test_observer_OnUpdateCommaList)
if (stage != PHP_INI_STAGE_STARTUP && stage != PHP_INI_STAGE_ACTIVATE && stage != PHP_INI_STAGE_DEACTIVATE && stage != PHP_INI_STAGE_SHUTDOWN) { if (stage != PHP_INI_STAGE_STARTUP && stage != PHP_INI_STAGE_ACTIVATE && stage != PHP_INI_STAGE_DEACTIVATE && stage != PHP_INI_STAGE_SHUTDOWN) {
ZEND_HASH_FOREACH_STR_KEY(*p, funcname) { ZEND_HASH_FOREACH_STR_KEY(*p, funcname) {
if ((func = zend_hash_find_ptr(EG(function_table), funcname))) { if ((func = zend_hash_find_ptr(EG(function_table), funcname))) {
zend_observer_remove_begin_handler(&func->op_array, observer_begin); zend_observer_remove_begin_handler(func, observer_begin);
zend_observer_remove_end_handler(&func->op_array, observer_end); zend_observer_remove_end_handler(func, observer_end);
} }
} ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END();
} }
@ -280,8 +280,8 @@ static ZEND_INI_MH(zend_test_observer_OnUpdateCommaList)
if (stage != PHP_INI_STAGE_STARTUP && stage != PHP_INI_STAGE_ACTIVATE && stage != PHP_INI_STAGE_DEACTIVATE && stage != PHP_INI_STAGE_SHUTDOWN) { if (stage != PHP_INI_STAGE_STARTUP && stage != PHP_INI_STAGE_ACTIVATE && stage != PHP_INI_STAGE_DEACTIVATE && stage != PHP_INI_STAGE_SHUTDOWN) {
ZEND_HASH_FOREACH_STR_KEY(*p, funcname) { ZEND_HASH_FOREACH_STR_KEY(*p, funcname) {
if ((func = zend_hash_find_ptr(EG(function_table), funcname))) { if ((func = zend_hash_find_ptr(EG(function_table), funcname))) {
zend_observer_add_begin_handler(&func->op_array, observer_begin); zend_observer_add_begin_handler(func, observer_begin);
zend_observer_add_end_handler(&func->op_array, observer_end); zend_observer_add_end_handler(func, observer_end);
} }
} ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END();
} }

View file

@ -48,38 +48,32 @@ var_dump(foo());
{main} %s%eobserver_backtrace_%d.php {main} %s%eobserver_backtrace_%d.php
--> -->
<foo> <foo>
<!-- init gen() --> <!-- init Generator::current() -->
<!-- <!--
gen()
Generator::current() Generator::current()
foo() foo()
{main} %s%eobserver_backtrace_%d.php {main} %s%eobserver_backtrace_%d.php
--> -->
<gen> <Generator::current>
<!-- init TestClass::foo() --> <!-- init gen() -->
<!-- <!--
TestClass::foo()
gen() gen()
Generator::current() Generator::current()
foo() foo()
{main} %s%eobserver_backtrace_%d.php {main} %s%eobserver_backtrace_%d.php
--> -->
<TestClass::foo> <gen>
<!-- init TestClass::{closure}() --> <!-- init TestClass::foo() -->
<!-- <!--
TestClass::{closure}()
array_map()
TestClass::foo() TestClass::foo()
gen() gen()
Generator::current() Generator::current()
foo() foo()
{main} %s%eobserver_backtrace_%d.php {main} %s%eobserver_backtrace_%d.php
--> -->
<TestClass::{closure}> <TestClass::foo>
<!-- init TestClass::bar() --> <!-- init array_map() -->
<!-- <!--
TestClass::bar()
TestClass::{closure}()
array_map() array_map()
TestClass::foo() TestClass::foo()
gen() gen()
@ -87,20 +81,52 @@ var_dump(foo());
foo() foo()
{main} %s%eobserver_backtrace_%d.php {main} %s%eobserver_backtrace_%d.php
--> -->
<TestClass::bar> <array_map>
</TestClass::bar> <!-- init TestClass::{closure}() -->
</TestClass::{closure}> <!--
<TestClass::{closure}> TestClass::{closure}()
<TestClass::bar> array_map()
</TestClass::bar> TestClass::foo()
</TestClass::{closure}> gen()
</TestClass::foo> Generator::current()
</gen> foo()
{main} %s%eobserver_backtrace_%d.php
-->
<TestClass::{closure}>
<!-- init TestClass::bar() -->
<!--
TestClass::bar()
TestClass::{closure}()
array_map()
TestClass::foo()
gen()
Generator::current()
foo()
{main} %s%eobserver_backtrace_%d.php
-->
<TestClass::bar>
</TestClass::bar>
</TestClass::{closure}>
<TestClass::{closure}>
<TestClass::bar>
</TestClass::bar>
</TestClass::{closure}>
</array_map>
</TestClass::foo>
</gen>
</Generator::current>
</foo> </foo>
<!-- init var_dump() -->
<!--
var_dump()
{main} %s%eobserver_backtrace_%d.php
-->
<var_dump>
array(2) { array(2) {
[0]=> [0]=>
int(42) int(42)
[1]=> [1]=>
int(1337) int(1337)
} }
</var_dump>
</file '%s%eobserver_backtrace_%d.php'> </file '%s%eobserver_backtrace_%d.php'>

View file

@ -26,30 +26,44 @@ foo();
echo 'DONE' . PHP_EOL; echo 'DONE' . PHP_EOL;
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_basic_01.php' --> <!-- init '%s%eobserver_basic_%d.php' -->
<file '%s%eobserver_basic_01.php'> <file '%s%eobserver_basic_%d.php'>
<!-- init foo() --> <!-- init foo() -->
<foo> <foo>
Foo Foo
<!-- init bar() --> <!-- init bar() -->
<bar> <bar>
Bar Bar
<!-- init array_sum() -->
<array_sum>
</array_sum>
<!-- init var_dump() -->
<var_dump>
int(6) int(6)
</var_dump>
</bar> </bar>
</foo> </foo>
<foo> <foo>
Foo Foo
<bar> <bar>
Bar Bar
<array_sum>
</array_sum>
<var_dump>
int(6) int(6)
</var_dump>
</bar> </bar>
</foo> </foo>
<foo> <foo>
Foo Foo
<bar> <bar>
Bar Bar
<array_sum>
</array_sum>
<var_dump>
int(6) int(6)
</var_dump>
</bar> </bar>
</foo> </foo>
DONE DONE
</file '%s%eobserver_basic_01.php'> </file '%s%eobserver_basic_%d.php'>

View file

@ -30,30 +30,44 @@ $test->foo();
echo 'DONE' . PHP_EOL; echo 'DONE' . PHP_EOL;
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_basic_02.php' --> <!-- init '%s%eobserver_basic_%d.php' -->
<file '%s%eobserver_basic_02.php'> <file '%s%eobserver_basic_%d.php'>
<!-- init TestClass::foo() --> <!-- init TestClass::foo() -->
<TestClass::foo> <TestClass::foo>
Foo Foo
<!-- init TestClass::bar() --> <!-- init TestClass::bar() -->
<TestClass::bar> <TestClass::bar>
Bar Bar
<!-- init array_sum() -->
<array_sum>
</array_sum>
<!-- init var_dump() -->
<var_dump>
int(6) int(6)
</var_dump>
</TestClass::bar> </TestClass::bar>
</TestClass::foo> </TestClass::foo>
<TestClass::foo> <TestClass::foo>
Foo Foo
<TestClass::bar> <TestClass::bar>
Bar Bar
<array_sum>
</array_sum>
<var_dump>
int(6) int(6)
</var_dump>
</TestClass::bar> </TestClass::bar>
</TestClass::foo> </TestClass::foo>
<TestClass::foo> <TestClass::foo>
Foo Foo
<TestClass::bar> <TestClass::bar>
Bar Bar
<array_sum>
</array_sum>
<var_dump>
int(6) int(6)
</var_dump>
</TestClass::bar> </TestClass::bar>
</TestClass::foo> </TestClass::foo>
DONE DONE
</file '%s%eobserver_basic_02.php'> </file '%s%eobserver_basic_%d.php'>

View file

@ -34,6 +34,7 @@ Foo
</foo> </foo>
<!-- init bar() --> <!-- init bar() -->
Bar Bar
<!-- init ini_set() -->
Foo Foo
<bar> <bar>
Bar Bar

View file

@ -25,7 +25,16 @@ call_user_func([$r->getAttributes(A::class)[0], 'newInstance']);
--EXPECTF-- --EXPECTF--
<!-- init '%s' --> <!-- init '%s' -->
<file '%s'> <file '%s'>
<!-- init A::__construct() --> <!-- init ReflectionFunction::__construct() -->
<A::__construct> <ReflectionFunction::__construct>
</A::__construct> </ReflectionFunction::__construct>
<!-- init ReflectionFunctionAbstract::getAttributes() -->
<ReflectionFunctionAbstract::getAttributes>
</ReflectionFunctionAbstract::getAttributes>
<!-- init ReflectionAttribute::newInstance() -->
<ReflectionAttribute::newInstance>
<!-- init A::__construct() -->
<A::__construct>
</A::__construct>
</ReflectionAttribute::newInstance>
</file '%s'> </file '%s'>

View file

@ -25,9 +25,24 @@ call_user_func([$r->getAttributes(A::class)[0], 'newInstance']);
--EXPECTF-- --EXPECTF--
<!-- init '%s' --> <!-- init '%s' -->
<file '%s'> <file '%s'>
<!-- init A::__construct() --> <!-- init ReflectionFunction::__construct() -->
<A::__construct> <ReflectionFunction::__construct>
</ReflectionFunction::__construct>
<!-- init ReflectionFunctionAbstract::getAttributes() -->
<ReflectionFunctionAbstract::getAttributes>
</ReflectionFunctionAbstract::getAttributes>
<!-- init ReflectionAttribute::newInstance() -->
<ReflectionAttribute::newInstance>
<!-- init A::__construct() -->
<A::__construct>
<!-- init array_map() -->
<array_map>
<!-- init str_repeat() -->
<str_repeat>
Fatal error: Allowed memory size of %d bytes exhausted %s in %s on line %d Fatal error: Allowed memory size of %d bytes exhausted %s in %s on line %d
</A::__construct> </str_repeat>
</array_map>
</A::__construct>
</ReflectionAttribute::newInstance>
</file '%s'> </file '%s'>

View file

@ -42,6 +42,8 @@ a();
<d> <d>
</d> </d>
<!-- init bailout() --> <!-- init bailout() -->
<!-- init array_map() -->
<!-- init str_repeat() -->
Fatal error: Allowed memory size of 20971520 bytes exhausted %s in %s on line %d Fatal error: Allowed memory size of 20971520 bytes exhausted %s in %s on line %d
</a> </a>

View file

@ -26,14 +26,19 @@ namespace Test {
} }
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_call_user_func_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_call_user_func_%d.php'> <file '%s'>
<!-- init Test\MyClass::myMethod() --> <!-- init call_user_func() -->
<Test\MyClass::myMethod> <call_user_func>
<!-- init Test\MyClass::myMethod() -->
<Test\MyClass::myMethod>
MyClass::myMethod called MyClass::myMethod called
</Test\MyClass::myMethod> </Test\MyClass::myMethod>
<!-- init Test\my_function() --> </call_user_func>
<Test\my_function> <call_user_func>
<!-- init Test\my_function() -->
<Test\my_function>
my_function called my_function called
</Test\my_function> </Test\my_function>
</file '%s%eobserver_call_user_func_%d.php'> </call_user_func>
</file '%s'>

View file

@ -26,14 +26,19 @@ namespace Test {
} }
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_call_user_func_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_call_user_func_%d.php'> <file '%s'>
<!-- init Test\MyClass::myMethod() --> <!-- init call_user_func_array() -->
<Test\MyClass::myMethod> <call_user_func_array>
<!-- init Test\MyClass::myMethod() -->
<Test\MyClass::myMethod>
MyClass::myMethod called MyClass::myMethod called
</Test\MyClass::myMethod> </Test\MyClass::myMethod>
<!-- init Test\my_function() --> </call_user_func_array>
<Test\my_function> <call_user_func_array>
<!-- init Test\my_function() -->
<Test\my_function>
my_function called my_function called
</Test\my_function> </Test\my_function>
</file '%s%eobserver_call_user_func_%d.php'> </call_user_func_array>
</file '%s'>

View file

@ -23,27 +23,41 @@ $foo($bar);
echo 'DONE' . PHP_EOL; echo 'DONE' . PHP_EOL;
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_closure_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_closure_%d.php'> <file '%s'>
<!-- init {closure}() --> <!-- init {closure}() -->
<{closure}> <{closure}>
Answer Answer
<!-- init {closure}() --> <!-- init {closure}() -->
<{closure}> <{closure}>
<!-- init array_sum() -->
<array_sum>
</array_sum>
<!-- init var_dump() -->
<var_dump>
int(42) int(42)
</var_dump>
</{closure}> </{closure}>
</{closure}> </{closure}>
<{closure}> <{closure}>
Answer Answer
<{closure}> <{closure}>
<array_sum>
</array_sum>
<var_dump>
int(42) int(42)
</var_dump>
</{closure}> </{closure}>
</{closure}> </{closure}>
<{closure}> <{closure}>
Answer Answer
<{closure}> <{closure}>
<array_sum>
</array_sum>
<var_dump>
int(42) int(42)
</var_dump>
</{closure}> </{closure}>
</{closure}> </{closure}>
DONE DONE
</file '%s%eobserver_closure_%d.php'> </file '%s'>

View file

@ -22,11 +22,14 @@ $closure();
echo 'DONE' . PHP_EOL; echo 'DONE' . PHP_EOL;
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_closure_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_closure_%d.php'> <file '%s'>
<!-- init Closure::fromCallable() -->
<Closure::fromCallable>
</Closure::fromCallable>
<!-- init Foo::bar() --> <!-- init Foo::bar() -->
<Foo::bar> <Foo::bar>
Called as fake closure. Called as fake closure.
</Foo::bar> </Foo::bar>
DONE DONE
</file '%s%eobserver_closure_%d.php'> </file '%s'>

View file

@ -19,11 +19,14 @@ foo();
echo 'You should not see this.'; echo 'You should not see this.';
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_error_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_error_%d.php'> <file '%s'>
<!-- init foo() --> <!-- init foo() -->
<foo> <foo>
<!-- init str_repeat() -->
<str_repeat>
Fatal error: Allowed memory size of 2097152 bytes exhausted%s(tried to allocate %d bytes) in %s on line %d Fatal error: Allowed memory size of 2097152 bytes exhausted%s(tried to allocate %d bytes) in %s on line %d
</str_repeat:NULL>
</foo:NULL> </foo:NULL>
</file '%s%eobserver_error_%d.php'> </file '%s%eobserver_error_%d.php'>

View file

@ -18,11 +18,14 @@ foo();
echo 'You should not see this.'; echo 'You should not see this.';
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_error_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_error_%d.php'> <file '%s'>
<!-- init foo() --> <!-- init foo() -->
<foo> <foo>
<!-- init trigger_error() -->
<trigger_error>
Fatal error: Foo error in %s on line %d Fatal error: Foo error in %s on line %d
</trigger_error:NULL>
</foo:NULL> </foo:NULL>
</file '%s%eobserver_error_%d.php'> </file '%s'>

View file

@ -36,10 +36,18 @@ echo 'Done.' . PHP_EOL;
<main> <main>
<!-- init foo() --> <!-- init foo() -->
<foo> <foo>
<!-- init SoapClient::__construct() -->
<SoapClient::__construct>
<!-- Exception: SoapFault -->
</SoapClient::__construct:NULL>
<!-- Exception: SoapFault --> <!-- Exception: SoapFault -->
</foo:NULL> </foo:NULL>
<!-- Exception: SoapFault --> <!-- Exception: SoapFault -->
</main:NULL> </main:NULL>
<!-- init Exception::getMessage() -->
<Exception::getMessage>
</Exception::getMessage:'SOAP-ERROR: Parsing WSDL: Couldn\'t load from \'foo\' : failed to load external entity "foo"
'>
SOAP-ERROR: Parsing WSDL: Couldn't load from 'foo' : failed to load external entity "foo" SOAP-ERROR: Parsing WSDL: Couldn't load from 'foo' : failed to load external entity "foo"
Done. Done.

View file

@ -22,14 +22,20 @@ foo();
echo 'You should not see this.'; echo 'You should not see this.';
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_error_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_error_%d.php'> <file '%s'>
<!-- init set_error_handler() -->
<set_error_handler>
</set_error_handler:NULL>
<!-- init foo() --> <!-- init foo() -->
<foo> <foo>
<!-- init {closure}() --> <!-- init {closure}() -->
<{closure}> <{closure}>
<!-- init trigger_error() -->
<trigger_error>
Fatal error: Foo error in %s on line %d Fatal error: Foo error in %s on line %d
</trigger_error:NULL>
</{closure}:NULL> </{closure}:NULL>
</foo:NULL> </foo:NULL>
</file '%s%eobserver_error_%d.php'> </file '%s'>

View file

@ -23,8 +23,8 @@ foo();
echo 'You should not see this' . PHP_EOL; echo 'You should not see this' . PHP_EOL;
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_exception_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_exception_%d.php'> <file '%s'>
<!-- init foo() --> <!-- init foo() -->
<foo> <foo>
Call #0 Call #0
@ -34,10 +34,19 @@ Call #1
</foo> </foo>
<foo> <foo>
Call #2 Call #2
<!-- init Exception::__construct() -->
<Exception::__construct>
</Exception::__construct>
<!-- Exception: RuntimeException --> <!-- Exception: RuntimeException -->
</foo> </foo>
<!-- Exception: RuntimeException --> <!-- Exception: RuntimeException -->
</file '%s%eobserver_exception_%d.php'> </file '%s'>
<!-- init Exception::__toString() -->
<Exception::__toString>
<!-- init Exception::getTraceAsString() -->
<Exception::getTraceAsString>
</Exception::getTraceAsString>
</Exception::__toString>
Fatal error: Uncaught RuntimeException: Third time is a charm in %s%eobserver_exception_%d.php:%d Fatal error: Uncaught RuntimeException: Third time is a charm in %s%eobserver_exception_%d.php:%d
Stack trace: Stack trace:

View file

@ -19,13 +19,17 @@ $fiber->resume();
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%sobserver_fiber_01.php' --> <!-- init '%s' -->
<!-- init Fiber::__construct() -->
<!-- init Fiber::start() -->
<!-- alloc: %s --> <!-- alloc: %s -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<init '%s'> <init '%s'>
<!-- init {closure}() --> <!-- init {closure}() -->
<!-- init Fiber::suspend() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<suspend '%s'> <suspend '%s'>
<!-- init Fiber::resume() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<resume '%s'> <resume '%s'>
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->

View file

@ -16,10 +16,13 @@ $fiber->start();
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%sobserver_fiber_02.php' --> <!-- init '%s' -->
<!-- init Fiber::__construct() -->
<!-- init Fiber::start() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<init '%s'> <init '%s'>
<!-- init {closure}() --> <!-- init {closure}() -->
<!-- init Fiber::suspend() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<suspend '%s'> <suspend '%s'>
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->

View file

@ -39,14 +39,19 @@ $fiber->resume();
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%sobserver_fiber_03.php' --> <!-- init '%s' -->
<!-- init Fiber::__construct() -->
<!-- init Fiber::start() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<init '%s'> <init '%s'>
<!-- init {closure}() --> <!-- init {closure}() -->
<!-- init Fiber::suspend() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<suspend '%s'> <suspend '%s'>
<!-- init Fiber::resume() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<resume '%s'> <resume '%s'>
<!-- init var_dump() -->
int(1) int(1)
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<init '%s'> <init '%s'>

View file

@ -26,12 +26,16 @@ $fiber->resume();
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%sobserver_fiber_04.php' --> <!-- init '%s' -->
<!-- init Fiber::__construct() -->
<!-- init Fiber::start() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<init '%s'> <init '%s'>
<!-- init {closure}() --> <!-- init {closure}() -->
<!-- init Fiber::suspend() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<suspend '%s'> <suspend '%s'>
<!-- init Fiber::resume() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<resume '%s'> <resume '%s'>
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->

View file

@ -25,12 +25,16 @@ $fiber->resume();
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%sobserver_fiber_05.php' --> <!-- init '%s' -->
<!-- init Fiber::__construct() -->
<!-- init Fiber::start() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<init '%s'> <init '%s'>
<!-- init {closure}() --> <!-- init {closure}() -->
<!-- init Fiber::suspend() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<suspend '%s'> <suspend '%s'>
<!-- init Fiber::resume() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<resume '%s'> <resume '%s'>
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->

View file

@ -22,12 +22,17 @@ try {
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%sobserver_fiber_06.php' --> <!-- init '%s' -->
<!-- init Fiber::__construct() -->
<!-- init Fiber::start() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<init '%s'> <init '%s'>
<!-- init {closure}() --> <!-- init {closure}() -->
<!-- init Fiber::suspend() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<suspend '%s'> <suspend '%s'>
<!-- init Exception::__construct() -->
<!-- init Fiber::throw() -->
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->
<resume '%s'> <resume '%s'>
<!-- switching from fiber %s to %s --> <!-- switching from fiber %s to %s -->

View file

@ -28,8 +28,8 @@ function doSomething() {
echo doSomething() . PHP_EOL; echo doSomething() . PHP_EOL;
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_generator_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_generator_%d.php'> <file '%s'>
<!-- init doSomething() --> <!-- init doSomething() -->
<doSomething> <doSomething>
<!-- init getResults() --> <!-- init getResults() -->
@ -44,7 +44,10 @@ echo doSomething() . PHP_EOL;
12 12
<getResults> <getResults>
</getResults:1337> </getResults:1337>
<!-- init Generator::getReturn() -->
<Generator::getReturn>
</Generator::getReturn:1337>
1337 1337
</doSomething:'Done'> </doSomething:'Done'>
Done Done
</file '%s%eobserver_generator_%d.php'> </file '%s'>

View file

@ -33,32 +33,87 @@ function doSomething() {
echo doSomething() . PHP_EOL; echo doSomething() . PHP_EOL;
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_generator_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_generator_%d.php'> <file '%s'>
<!-- init doSomething() --> <!-- init doSomething() -->
<doSomething> <doSomething>
<!-- init fooResults() --> <!-- init Generator::current() -->
<fooResults> <Generator::current>
<!-- init fooResults() -->
<fooResults>
Starting generator Starting generator
</fooResults:0> </fooResults:0>
</Generator::current:0>
<Generator::current>
</Generator::current:0>
0 0
<fooResults> <Generator::current>
</fooResults:1> </Generator::current:0>
<!-- init Generator::next() -->
<Generator::next>
<fooResults>
</fooResults:1>
</Generator::next:NULL>
<Generator::current>
</Generator::current:1>
<Generator::current>
</Generator::current:1>
1 1
<fooResults> <Generator::current>
</fooResults:2> </Generator::current:1>
<Generator::next>
<fooResults>
</fooResults:2>
</Generator::next:NULL>
<Generator::current>
</Generator::current:2>
<Generator::current>
</Generator::current:2>
2 2
<fooResults> <Generator::current>
</fooResults:3> </Generator::current:2>
<Generator::next>
<fooResults>
</fooResults:3>
</Generator::next:NULL>
<Generator::current>
</Generator::current:3>
<Generator::current>
</Generator::current:3>
3 3
<fooResults> <Generator::current>
</fooResults:4> </Generator::current:3>
<Generator::next>
<fooResults>
</fooResults:4>
</Generator::next:NULL>
<Generator::current>
</Generator::current:4>
<Generator::current>
</Generator::current:4>
4 4
<fooResults> <Generator::current>
</fooResults:5> </Generator::current:4>
<Generator::next>
<fooResults>
</fooResults:5>
</Generator::next:NULL>
<Generator::current>
</Generator::current:5>
<Generator::current>
</Generator::current:5>
5 5
<fooResults> <Generator::current>
</fooResults:NULL> </Generator::current:5>
<!-- init Generator::send() -->
<Generator::send>
<fooResults>
</fooResults:NULL>
</Generator::send:NULL>
<Generator::next>
</Generator::next:NULL>
<Generator::current>
</Generator::current:NULL>
</doSomething:'Done'> </doSomething:'Done'>
Done Done
</file '%s%eobserver_generator_%d.php'> </file '%s'>

View file

@ -26,8 +26,8 @@ function doSomething() {
echo doSomething() . PHP_EOL; echo doSomething() . PHP_EOL;
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_generator_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_generator_%d.php'> <file '%s'>
<!-- init doSomething() --> <!-- init doSomething() -->
<doSomething> <doSomething>
<!-- init fooResults() --> <!-- init fooResults() -->
@ -38,12 +38,27 @@ echo doSomething() . PHP_EOL;
</fooResults:1> </fooResults:1>
1 1
<fooResults> <fooResults>
<!-- init Exception::__construct() -->
<Exception::__construct>
</Exception::__construct:NULL>
<!-- Exception: RuntimeException --> <!-- Exception: RuntimeException -->
</fooResults:NULL> </fooResults:NULL>
<!-- Exception: RuntimeException --> <!-- Exception: RuntimeException -->
</doSomething:NULL> </doSomething:NULL>
<!-- Exception: RuntimeException --> <!-- Exception: RuntimeException -->
</file '%s%eobserver_generator_%d.php'> </file '%s'>
<!-- init Exception::__toString() -->
<Exception::__toString>
<!-- init Exception::getTraceAsString() -->
<Exception::getTraceAsString>
</Exception::getTraceAsString:'#0 %s(%d): fooResults()
#1 %s(%d): doSomething()
#2 {main}'>
</Exception::__toString:'RuntimeException: Oops! in %s%eobserver_generator_%d.php:%d
Stack trace:
#0 %s%eobserver_generator_%d.php(%d): fooResults()
#1 %s%eobserver_generator_%d.php(%d): doSomething()
#2 {main}'>
Fatal error: Uncaught RuntimeException: Oops! in %s%eobserver_generator_%d.php:%d Fatal error: Uncaught RuntimeException: Oops! in %s%eobserver_generator_%d.php:%d
Stack trace: Stack trace:

View file

@ -20,9 +20,9 @@ echo array_sum([1,2,3]) . PHP_EOL;
foo(); foo();
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_opline_%d.php' --> <!-- init '%s' -->
<!-- opcode: 'ZEND_INIT_FCALL' --> <!-- opcode: 'ZEND_INIT_FCALL' -->
<file '%s%eobserver_opline_%d.php'> <file '%s'>
<!-- opcode: 'ZEND_INIT_FCALL' --> <!-- opcode: 'ZEND_INIT_FCALL' -->
<!-- init foo() --> <!-- init foo() -->
<!-- opcode: 'ZEND_ECHO' --> <!-- opcode: 'ZEND_ECHO' -->
@ -31,9 +31,9 @@ foo();
Foo Foo
<!-- opcode: 'ZEND_RETURN' --> <!-- opcode: 'ZEND_RETURN' -->
</foo> </foo>
<!-- init '%s%eobserver.inc' --> <!-- init '%s' -->
<!-- opcode: 'ZEND_INIT_FCALL' --> <!-- opcode: 'ZEND_INIT_FCALL' -->
<file '%s%eobserver.inc'> <file '%s'>
<!-- opcode: 'ZEND_INIT_FCALL' --> <!-- opcode: 'ZEND_INIT_FCALL' -->
<!-- init foo_observer_test() --> <!-- init foo_observer_test() -->
<!-- opcode: 'ZEND_ECHO' --> <!-- opcode: 'ZEND_ECHO' -->
@ -43,7 +43,13 @@ foo_observer_test
<!-- opcode: 'ZEND_RETURN' --> <!-- opcode: 'ZEND_RETURN' -->
</foo_observer_test> </foo_observer_test>
<!-- opcode: 'ZEND_RETURN' --> <!-- opcode: 'ZEND_RETURN' -->
</file '%s%eobserver.inc'> </file '%s'>
<!-- init array_sum() -->
<!-- opcode: 'ZEND_RETURN' -->
<array_sum>
<!-- opcode: 'ZEND_RETURN' -->
<!-- opcode: 'ZEND_RETURN' -->
</array_sum>
6 6
<foo> <foo>
<!-- opcode: 'ZEND_ECHO' --> <!-- opcode: 'ZEND_ECHO' -->
@ -51,4 +57,4 @@ Foo
<!-- opcode: 'ZEND_RETURN' --> <!-- opcode: 'ZEND_RETURN' -->
</foo> </foo>
<!-- opcode: 'ZEND_RETURN' --> <!-- opcode: 'ZEND_RETURN' -->
</file '%s%eobserver_opline_%d.php'> </file '%s'>

View file

@ -21,12 +21,20 @@ $gen->current();
echo 'Done' . PHP_EOL; echo 'Done' . PHP_EOL;
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_retval_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_retval_%d.php'> <file '%s'>
<!-- init foo() --> <!-- init Generator::current() -->
<foo> <Generator::current>
</foo:'I should be observable'> <!-- init foo() -->
<foo> <foo>
</foo:'Me too!'> </foo:'I should be observable'>
</Generator::current:'I should be observable'>
<!-- init Generator::next() -->
<Generator::next>
<foo>
</foo:'Me too!'>
</Generator::next:NULL>
<Generator::current>
</Generator::current:'Me too!'>
Done Done
</file '%s%eobserver_retval_%d.php'> </file '%s'>

View file

@ -24,13 +24,16 @@ function foo() {
echo 'Done: ' . bar(40) . PHP_EOL; echo 'Done: ' . bar(40) . PHP_EOL;
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_shutdown_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_shutdown_%d.php'> <file '%s'>
<!-- init register_shutdown_function() -->
<register_shutdown_function>
</register_shutdown_function:NULL>
<!-- init bar() --> <!-- init bar() -->
<bar> <bar>
</bar:40> </bar:40>
Done: 40 Done: 40
</file '%s%eobserver_shutdown_%d.php'> </file '%s'>
<!-- init {closure}() --> <!-- init {closure}() -->
<{closure}> <{closure}>
<!-- init foo() --> <!-- init foo() -->

View file

@ -12,14 +12,24 @@ function foo(array $a) { return 1; }
foo(42); foo(42);
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_types_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_types_%d.php'> <file '%s'>
<!-- init foo() --> <!-- init foo() -->
<foo> <foo>
<!-- Exception: TypeError --> <!-- Exception: TypeError -->
</foo:NULL> </foo:NULL>
<!-- Exception: TypeError --> <!-- Exception: TypeError -->
</file '%s%eobserver_types_%d.php'> </file '%s'>
<!-- init Error::__toString() -->
<Error::__toString>
<!-- init Error::getTraceAsString() -->
<Error::getTraceAsString>
</Error::getTraceAsString:'#0 %s(%d): foo(42)
#1 {main}'>
</Error::__toString:'TypeError: foo(): Argument #1 ($a) must be of type array, int given, called in %s:%d
Stack trace:
#0 %s%eobserver_types_%d.php(%d): foo(42)
#1 {main}'>
Fatal error: Uncaught TypeError: foo(): Argument #1 ($a) must be of type array, int given, called in %s:%d Fatal error: Uncaught TypeError: foo(): Argument #1 ($a) must be of type array, int given, called in %s:%d
Stack trace: Stack trace:

View file

@ -18,19 +18,25 @@ var_dump(array_reduce($a, 'sum'));
echo 'Done' . PHP_EOL; echo 'Done' . PHP_EOL;
?> ?>
--EXPECTF-- --EXPECTF--
<!-- init '%s%eobserver_zend_call_function_%d.php' --> <!-- init '%s' -->
<file '%s%eobserver_zend_call_function_%d.php'> <file '%s'>
<!-- init sum() --> <!-- init array_reduce() -->
<sum> <array_reduce>
</sum> <!-- init sum() -->
<sum> <sum>
</sum> </sum>
<sum> <sum>
</sum> </sum>
<sum> <sum>
</sum> </sum>
<sum> <sum>
</sum> </sum>
<sum>
</sum>
</array_reduce>
<!-- init var_dump() -->
<var_dump>
int(15) int(15)
</var_dump>
Done Done
</file '%s%eobserver_zend_call_function_%d.php'> </file '%s'>