diff options
Diffstat (limited to 'ext/zend_test')
46 files changed, 2370 insertions, 94 deletions
diff --git a/ext/zend_test/config.w32 b/ext/zend_test/config.w32 index 76a0f1ae5b..d66fd0b1ee 100644 --- a/ext/zend_test/config.w32 +++ b/ext/zend_test/config.w32 @@ -4,4 +4,5 @@ ARG_ENABLE("zend-test", "enable zend-test extension", "no"); if (PHP_ZEND_TEST != "no") { EXTENSION("zend_test", "test.c", PHP_ZEND_TEST_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); + ADD_FLAG("CFLAGS_ZEND_TEST", "/D PHP_ZEND_TEST_EXPORTS "); } diff --git a/ext/zend_test/php_test.h b/ext/zend_test/php_test.h index 03e6d836e2..2a1d7763da 100644 --- a/ext/zend_test/php_test.h +++ b/ext/zend_test/php_test.h @@ -1,7 +1,5 @@ /* +----------------------------------------------------------------------+ - | PHP Version 7 | - +----------------------------------------------------------------------+ | Copyright (c) The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 4f81adc6ac..4e7f55d1b1 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -1,7 +1,5 @@ /* +----------------------------------------------------------------------+ - | PHP Version 7 | - +----------------------------------------------------------------------+ | Copyright (c) The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | @@ -23,64 +21,81 @@ #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" +#include "ext/standard/php_var.h" #include "php_test.h" +#include "test_arginfo.h" +#include "zend_attributes.h" +#include "zend_observer.h" +#include "zend_smart_str.h" + +ZEND_BEGIN_MODULE_GLOBALS(zend_test) + int observer_enabled; + int observer_show_output; + int observer_observe_all; + int observer_observe_includes; + int observer_observe_functions; + int observer_show_return_type; + int observer_show_return_value; + int observer_show_init_backtrace; + int observer_show_opcode; + int observer_nesting_depth; + int replace_zend_execute_ex; +ZEND_END_MODULE_GLOBALS(zend_test) + +ZEND_DECLARE_MODULE_GLOBALS(zend_test) + +#define ZT_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(zend_test, v) static zend_class_entry *zend_test_interface; static zend_class_entry *zend_test_class; static zend_class_entry *zend_test_child_class; static zend_class_entry *zend_test_trait; +static zend_class_entry *zend_test_attribute; static zend_object_handlers zend_test_class_handlers; -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_zend_test_array_return, IS_ARRAY, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_zend_test_nullable_array_return, IS_ARRAY, 1) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(arginfo_zend_test_void_return, IS_VOID, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_terminate_string, 0, 0, 1) - ZEND_ARG_INFO(1, str) -ZEND_END_ARG_INFO() +static ZEND_FUNCTION(zend_test_func) +{ + RETVAL_STR_COPY(EX(func)->common.function_name); -ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_leak_variable, 0, 0, 1) - ZEND_ARG_INFO(0, variable) -ZEND_END_ARG_INFO() + /* Cleanup trampoline */ + ZEND_ASSERT(EX(func)->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE); + zend_string_release(EX(func)->common.function_name); + zend_free_trampoline(EX(func)); + EX(func) = NULL; +} -ZEND_FUNCTION(zend_test_func) +static ZEND_FUNCTION(zend_test_array_return) { - /* dummy */ + ZEND_PARSE_PARAMETERS_NONE(); } -ZEND_FUNCTION(zend_test_array_return) +static ZEND_FUNCTION(zend_test_nullable_array_return) { - zval *arg1, *arg2; - - zend_parse_parameters(ZEND_NUM_ARGS(), "|zz", &arg1, &arg2); + ZEND_PARSE_PARAMETERS_NONE(); } -ZEND_FUNCTION(zend_test_nullable_array_return) +static ZEND_FUNCTION(zend_test_void_return) { - zval *arg1, *arg2; - - zend_parse_parameters(ZEND_NUM_ARGS(), "|zz", &arg1, &arg2); + /* dummy */ + ZEND_PARSE_PARAMETERS_NONE(); } -ZEND_FUNCTION(zend_test_void_return) +static ZEND_FUNCTION(zend_test_deprecated) { - /* dummy */ + zval *arg1; + + zend_parse_parameters(ZEND_NUM_ARGS(), "|z", &arg1); } -/* Create a string without terminating null byte. Must be termined with +/* Create a string without terminating null byte. Must be terminated with * zend_terminate_string() before destruction, otherwise a warning is issued * in debug builds. */ -ZEND_FUNCTION(zend_create_unterminated_string) +static ZEND_FUNCTION(zend_create_unterminated_string) { zend_string *str, *res; if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) { - return; + RETURN_THROWS(); } res = zend_string_alloc(ZSTR_LEN(str), 0); @@ -91,39 +106,37 @@ ZEND_FUNCTION(zend_create_unterminated_string) } /* Enforce terminate null byte on string. This avoids a warning in debug builds. */ -ZEND_FUNCTION(zend_terminate_string) +static ZEND_FUNCTION(zend_terminate_string) { zend_string *str; if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) { - return; + RETURN_THROWS(); } ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0'; } -/* {{{ proto void zend_leak_bytes([int num_bytes]) - Cause an intentional memory leak, for testing/debugging purposes */ -ZEND_FUNCTION(zend_leak_bytes) +/* {{{ Cause an intentional memory leak, for testing/debugging purposes */ +static ZEND_FUNCTION(zend_leak_bytes) { zend_long leakbytes = 3; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &leakbytes) == FAILURE) { - return; + RETURN_THROWS(); } emalloc(leakbytes); } /* }}} */ -/* {{{ proto void zend_leak_variable(mixed variable) - Leak a refcounted variable */ -ZEND_FUNCTION(zend_leak_variable) +/* {{{ Leak a refcounted variable */ +static ZEND_FUNCTION(zend_leak_variable) { zval *zv; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zv) == FAILURE) { - return; + RETURN_THROWS(); } if (!Z_REFCOUNTED_P(zv)) { @@ -135,6 +148,100 @@ ZEND_FUNCTION(zend_leak_variable) } /* }}} */ +/* Tests Z_PARAM_OBJ_OR_STR */ +static ZEND_FUNCTION(zend_string_or_object) +{ + zend_string *str; + zend_object *object; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ_OR_STR(object, str) + ZEND_PARSE_PARAMETERS_END(); + + if (str) { + RETURN_STR_COPY(str); + } else { + RETURN_OBJ_COPY(object); + } +} +/* }}} */ + +/* Tests Z_PARAM_OBJ_OR_STR_OR_NULL */ +static ZEND_FUNCTION(zend_string_or_object_or_null) +{ + zend_string *str; + zend_object *object; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ_OR_STR_OR_NULL(object, str) + ZEND_PARSE_PARAMETERS_END(); + + if (str) { + RETURN_STR_COPY(str); + } else if (object) { + RETURN_OBJ_COPY(object); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +/* Tests Z_PARAM_OBJ_OF_CLASS_OR_STR */ +static ZEND_FUNCTION(zend_string_or_stdclass) +{ + zend_string *str; + zend_object *object; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ_OF_CLASS_OR_STR(object, zend_standard_class_def, str) + ZEND_PARSE_PARAMETERS_END(); + + if (str) { + RETURN_STR_COPY(str); + } else { + RETURN_OBJ_COPY(object); + } +} +/* }}} */ + +/* Tests Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL */ +static ZEND_FUNCTION(zend_string_or_stdclass_or_null) +{ + zend_string *str; + zend_object *object; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL(object, zend_standard_class_def, str) + ZEND_PARSE_PARAMETERS_END(); + + if (str) { + RETURN_STR_COPY(str); + } else if (object) { + RETURN_OBJ_COPY(object); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +/* TESTS Z_PARAM_ITERABLE and Z_PARAM_ITERABLE_OR_NULL */ +static ZEND_FUNCTION(zend_iterable) +{ + zval *arg1, *arg2; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ITERABLE(arg1) + Z_PARAM_OPTIONAL + Z_PARAM_ITERABLE_OR_NULL(arg2) + ZEND_PARSE_PARAMETERS_END(); +} + +static ZEND_FUNCTION(namespaced_func) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_TRUE; +} + static zend_object *zend_test_class_new(zend_class_entry *class_type) /* {{{ */ { zend_object *obj = zend_objects_new(class_type); object_properties_init(obj, class_type); @@ -144,15 +251,20 @@ static zend_object *zend_test_class_new(zend_class_entry *class_type) /* {{{ */ /* }}} */ static zend_function *zend_test_class_method_get(zend_object **object, zend_string *name, const zval *key) /* {{{ */ { - zend_internal_function *fptr = emalloc(sizeof(zend_internal_function)); - fptr->type = ZEND_OVERLOADED_FUNCTION_TEMPORARY; + zend_internal_function *fptr; + + if (EXPECTED(EG(trampoline).common.function_name == NULL)) { + fptr = (zend_internal_function *) &EG(trampoline); + } else { + fptr = emalloc(sizeof(zend_internal_function)); + } + memset(fptr, 0, sizeof(zend_internal_function)); + fptr->type = ZEND_INTERNAL_FUNCTION; fptr->num_args = 1; - fptr->arg_info = NULL; fptr->scope = (*object)->ce; fptr->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; fptr->function_name = zend_string_copy(name); fptr->handler = ZEND_FN(zend_test_func); - zend_set_function_arg_flags((zend_function*)fptr); return (zend_function*)fptr; } @@ -160,15 +272,20 @@ static zend_function *zend_test_class_method_get(zend_object **object, zend_stri static zend_function *zend_test_class_static_method_get(zend_class_entry *ce, zend_string *name) /* {{{ */ { if (zend_string_equals_literal_ci(name, "test")) { - zend_internal_function *fptr = emalloc(sizeof(zend_internal_function)); - fptr->type = ZEND_OVERLOADED_FUNCTION; + zend_internal_function *fptr; + + if (EXPECTED(EG(trampoline).common.function_name == NULL)) { + fptr = (zend_internal_function *) &EG(trampoline); + } else { + fptr = emalloc(sizeof(zend_internal_function)); + } + memset(fptr, 0, sizeof(zend_internal_function)); + fptr->type = ZEND_INTERNAL_FUNCTION; fptr->num_args = 1; - fptr->arg_info = NULL; fptr->scope = ce; fptr->fn_flags = ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_STATIC; - fptr->function_name = name; + fptr->function_name = zend_string_copy(name); fptr->handler = ZEND_FN(zend_test_func); - zend_set_function_arg_flags((zend_function*)fptr); return (zend_function*)fptr; } @@ -176,32 +293,62 @@ static zend_function *zend_test_class_static_method_get(zend_class_entry *ce, ze } /* }}} */ -static int zend_test_class_call_method(zend_string *method, zend_object *object, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ { - RETVAL_STR(zend_string_copy(method)); - return 0; +void zend_attribute_validate_zendtestattribute(zend_attribute *attr, uint32_t target, zend_class_entry *scope) +{ + if (target != ZEND_ATTRIBUTE_TARGET_CLASS) { + zend_error(E_COMPILE_ERROR, "Only classes can be marked with #[ZendTestAttribute]"); + } +} + +static ZEND_METHOD(_ZendTestClass, __toString) { + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_EMPTY_STRING(); } -/* }}} */ /* Internal function returns bool, we return int. */ -static ZEND_METHOD(_ZendTestClass, is_object) /* {{{ */ { +static ZEND_METHOD(_ZendTestClass, is_object) { + ZEND_PARSE_PARAMETERS_NONE(); RETURN_LONG(42); } -/* }}} */ -static ZEND_METHOD(_ZendTestTrait, testMethod) /* {{{ */ { +static ZEND_METHOD(_ZendTestClass, returnsStatic) { + ZEND_PARSE_PARAMETERS_NONE(); + object_init_ex(return_value, zend_get_called_scope(execute_data)); +} + +static ZEND_METHOD(_ZendTestTrait, testMethod) { + ZEND_PARSE_PARAMETERS_NONE(); RETURN_TRUE; } -/* }}} */ -static const zend_function_entry zend_test_class_methods[] = { - ZEND_ME(_ZendTestClass, is_object, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - ZEND_FE_END -}; +static ZEND_METHOD(ZendTestNS_Foo, method) { + ZEND_PARSE_PARAMETERS_NONE(); +} -static const zend_function_entry zend_test_trait_methods[] = { - ZEND_ME(_ZendTestTrait, testMethod, NULL, ZEND_ACC_PUBLIC) - ZEND_FE_END -}; +static ZEND_METHOD(ZendTestNS2_Foo, method) { + ZEND_PARSE_PARAMETERS_NONE(); +} + +PHP_INI_BEGIN() + STD_PHP_INI_BOOLEAN("zend_test.observer.enabled", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_enabled, zend_zend_test_globals, zend_test_globals) + STD_PHP_INI_BOOLEAN("zend_test.observer.show_output", "1", PHP_INI_SYSTEM, OnUpdateBool, observer_show_output, zend_zend_test_globals, zend_test_globals) + STD_PHP_INI_BOOLEAN("zend_test.observer.observe_all", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_all, zend_zend_test_globals, zend_test_globals) + STD_PHP_INI_BOOLEAN("zend_test.observer.observe_includes", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_includes, zend_zend_test_globals, zend_test_globals) + 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) + STD_PHP_INI_BOOLEAN("zend_test.observer.show_opcode", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_opcode, zend_zend_test_globals, zend_test_globals) + STD_PHP_INI_BOOLEAN("zend_test.replace_zend_execute_ex", "0", PHP_INI_SYSTEM, OnUpdateBool, replace_zend_execute_ex, zend_zend_test_globals, zend_test_globals) +PHP_INI_END() + +static zend_observer_fcall_handlers observer_fcall_init(zend_execute_data *execute_data); + +void (*old_zend_execute_ex)(zend_execute_data *execute_data); +static void custom_zend_execute_ex(zend_execute_data *execute_data) +{ + old_zend_execute_ex(execute_data); +} PHP_MINIT_FUNCTION(zend_test) { @@ -210,8 +357,8 @@ PHP_MINIT_FUNCTION(zend_test) INIT_CLASS_ENTRY(class_entry, "_ZendTestInterface", NULL); zend_test_interface = zend_register_internal_interface(&class_entry); zend_declare_class_constant_long(zend_test_interface, ZEND_STRL("DUMMY"), 0); - INIT_CLASS_ENTRY(class_entry, "_ZendTestClass", zend_test_class_methods); - zend_test_class = zend_register_internal_class_ex(&class_entry, NULL); + INIT_CLASS_ENTRY(class_entry, "_ZendTestClass", class__ZendTestClass_methods); + zend_test_class = zend_register_internal_class(&class_entry); zend_class_implements(zend_test_class, 1, zend_test_interface); zend_test_class->create_object = zend_test_class_new; zend_test_class->get_static_method = zend_test_class_static_method_get; @@ -223,7 +370,8 @@ PHP_MINIT_FUNCTION(zend_test) zval val; ZVAL_LONG(&val, 123); zend_declare_typed_property( - zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, ZEND_TYPE_ENCODE(IS_LONG, 0)); + zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, + (zend_type) ZEND_TYPE_INIT_CODE(IS_LONG, 0, 0)); zend_string_release(name); } @@ -234,7 +382,22 @@ PHP_MINIT_FUNCTION(zend_test) ZVAL_NULL(&val); zend_declare_typed_property( zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, - ZEND_TYPE_ENCODE_CLASS(class_name, 1)); + (zend_type) ZEND_TYPE_INIT_CLASS(class_name, 1, 0)); + zend_string_release(name); + } + + { + zend_string *name = zend_string_init("classUnionProp", sizeof("classUnionProp") - 1, 1); + zend_string *class_name1 = zend_string_init("stdClass", sizeof("stdClass") - 1, 1); + zend_string *class_name2 = zend_string_init("Iterator", sizeof("Iterator") - 1, 1); + zend_type_list *type_list = malloc(ZEND_TYPE_LIST_SIZE(2)); + type_list->num_types = 2; + type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(class_name1, 0, 0); + type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(class_name2, 0, 0); + zend_type type = ZEND_TYPE_INIT_PTR(type_list, _ZEND_TYPE_LIST_BIT, 1, 0); + zval val; + ZVAL_NULL(&val); + zend_declare_typed_property(zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, type); zend_string_release(name); } @@ -244,7 +407,7 @@ PHP_MINIT_FUNCTION(zend_test) ZVAL_LONG(&val, 123); zend_declare_typed_property( zend_test_class, name, &val, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, NULL, - ZEND_TYPE_ENCODE(IS_LONG, 0)); + (zend_type) ZEND_TYPE_INIT_CODE(IS_LONG, 0, 0)); zend_string_release(name); } @@ -253,27 +416,184 @@ PHP_MINIT_FUNCTION(zend_test) memcpy(&zend_test_class_handlers, &std_object_handlers, sizeof(zend_object_handlers)); zend_test_class_handlers.get_method = zend_test_class_method_get; - zend_test_class_handlers.call_method = zend_test_class_call_method; - INIT_CLASS_ENTRY(class_entry, "_ZendTestTrait", zend_test_trait_methods); + INIT_CLASS_ENTRY(class_entry, "_ZendTestTrait", class__ZendTestTrait_methods); zend_test_trait = zend_register_internal_class(&class_entry); zend_test_trait->ce_flags |= ZEND_ACC_TRAIT; zend_declare_property_null(zend_test_trait, "testProp", sizeof("testProp")-1, ZEND_ACC_PUBLIC); zend_register_class_alias("_ZendTestClassAlias", zend_test_class); + + REGISTER_LONG_CONSTANT("ZEND_TEST_DEPRECATED", 42, CONST_PERSISTENT | CONST_DEPRECATED); + + INIT_CLASS_ENTRY(class_entry, "ZendTestAttribute", NULL); + zend_test_attribute = zend_register_internal_class(&class_entry); + zend_test_attribute->ce_flags |= ZEND_ACC_FINAL; + + { + zend_internal_attribute *attr = zend_internal_attribute_register(zend_test_attribute, ZEND_ATTRIBUTE_TARGET_ALL); + attr->validator = zend_attribute_validate_zendtestattribute; + } + + // Loading via dl() not supported with the observer API + if (type != MODULE_TEMPORARY) { + REGISTER_INI_ENTRIES(); + if (ZT_G(observer_enabled)) { + zend_observer_fcall_register(observer_fcall_init); + } + } else { + (void)ini_entries; + } + + if (ZT_G(replace_zend_execute_ex)) { + old_zend_execute_ex = zend_execute_ex; + zend_execute_ex = custom_zend_execute_ex; + } + return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(zend_test) { + if (type != MODULE_TEMPORARY) { + UNREGISTER_INI_ENTRIES(); + } + return SUCCESS; } +static void observer_show_opcode(zend_execute_data *execute_data) +{ + if (!ZT_G(observer_show_opcode)) { + return; + } + php_printf("%*s<!-- opcode: '%s' -->\n", 2 * ZT_G(observer_nesting_depth), "", zend_get_opcode_name(EX(opline)->opcode)); +} + +static void observer_begin(zend_execute_data *execute_data) +{ + if (!ZT_G(observer_show_output)) { + return; + } + + if (execute_data->func && execute_data->func->common.function_name) { + if (execute_data->func->common.scope) { + php_printf("%*s<%s::%s>\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(execute_data->func->common.scope->name), ZSTR_VAL(execute_data->func->common.function_name)); + } else { + php_printf("%*s<%s>\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(execute_data->func->common.function_name)); + } + } else { + php_printf("%*s<file '%s'>\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(execute_data->func->op_array.filename)); + } + ZT_G(observer_nesting_depth)++; + observer_show_opcode(execute_data); +} + +static void get_retval_info(zval *retval, smart_str *buf) +{ + if (!ZT_G(observer_show_return_type) && !ZT_G(observer_show_return_value)) { + return; + } + + smart_str_appendc(buf, ':'); + if (retval == NULL) { + smart_str_appendl(buf, "NULL", 4); + } else if (ZT_G(observer_show_return_value)) { + if (Z_TYPE_P(retval) == IS_OBJECT) { + smart_str_appendl(buf, "object(", 7); + smart_str_append(buf, Z_OBJCE_P(retval)->name); + smart_str_appendl(buf, ")#", 2); + smart_str_append_long(buf, Z_OBJ_HANDLE_P(retval)); + } else { + php_var_export_ex(retval, 2 * ZT_G(observer_nesting_depth) + 3, buf); + } + } else if (ZT_G(observer_show_return_type)) { + smart_str_appends(buf, zend_zval_type_name(retval)); + } + smart_str_0(buf); +} + +static void observer_end(zend_execute_data *execute_data, zval *retval) +{ + if (!ZT_G(observer_show_output)) { + return; + } + + if (EG(exception)) { + php_printf("%*s<!-- Exception: %s -->\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(EG(exception)->ce->name)); + } + observer_show_opcode(execute_data); + ZT_G(observer_nesting_depth)--; + if (execute_data->func && execute_data->func->common.function_name) { + smart_str retval_info = {0}; + get_retval_info(retval, &retval_info); + if (execute_data->func->common.scope) { + php_printf("%*s</%s::%s%s>\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(execute_data->func->common.scope->name), ZSTR_VAL(execute_data->func->common.function_name), retval_info.s ? ZSTR_VAL(retval_info.s) : ""); + } else { + php_printf("%*s</%s%s>\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(execute_data->func->common.function_name), retval_info.s ? ZSTR_VAL(retval_info.s) : ""); + } + smart_str_free(&retval_info); + } else { + php_printf("%*s</file '%s'>\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(execute_data->func->op_array.filename)); + } +} + +static void observer_show_init(zend_function *fbc) +{ + if (fbc->common.function_name) { + if (fbc->common.scope) { + php_printf("%*s<!-- init %s::%s() -->\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name)); + } else { + php_printf("%*s<!-- init %s() -->\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(fbc->common.function_name)); + } + } else { + php_printf("%*s<!-- init '%s' -->\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(fbc->op_array.filename)); + } +} + +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); + } + observer_show_opcode(execute_data); + } + + if (ZT_G(observer_observe_all)) { + return (zend_observer_fcall_handlers){observer_begin, observer_end}; + } else if (ZT_G(observer_observe_includes) && !fbc->common.function_name) { + return (zend_observer_fcall_handlers){observer_begin, observer_end}; + } else if (ZT_G(observer_observe_functions) && fbc->common.function_name) { + return (zend_observer_fcall_handlers){observer_begin, observer_end}; + } + return (zend_observer_fcall_handlers){NULL, NULL}; +} + PHP_RINIT_FUNCTION(zend_test) { -#if defined(COMPILE_DL_ZEND_TEST) && defined(ZTS) - ZEND_TSRMLS_CACHE_UPDATE(); -#endif return SUCCESS; } @@ -282,35 +602,38 @@ PHP_RSHUTDOWN_FUNCTION(zend_test) return SUCCESS; } +static PHP_GINIT_FUNCTION(zend_test) +{ +#if defined(COMPILE_DL_ZEND_TEST) && defined(ZTS) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + memset(zend_test_globals, 0, sizeof(*zend_test_globals)); +} + PHP_MINFO_FUNCTION(zend_test) { php_info_print_table_start(); php_info_print_table_header(2, "zend-test extension", "enabled"); php_info_print_table_end(); -} -static const zend_function_entry zend_test_functions[] = { - ZEND_FE(zend_test_array_return, arginfo_zend_test_array_return) - ZEND_FE(zend_test_nullable_array_return, arginfo_zend_test_nullable_array_return) - ZEND_FE(zend_test_void_return, arginfo_zend_test_void_return) - ZEND_FE(zend_create_unterminated_string, NULL) - ZEND_FE(zend_terminate_string, arginfo_zend_terminate_string) - ZEND_FE(zend_leak_bytes, NULL) - ZEND_FE(zend_leak_variable, arginfo_zend_leak_variable) - ZEND_FE_END -}; + DISPLAY_INI_ENTRIES(); +} zend_module_entry zend_test_module_entry = { STANDARD_MODULE_HEADER, "zend-test", - zend_test_functions, + ext_functions, PHP_MINIT(zend_test), PHP_MSHUTDOWN(zend_test), PHP_RINIT(zend_test), PHP_RSHUTDOWN(zend_test), PHP_MINFO(zend_test), PHP_ZEND_TEST_VERSION, - STANDARD_MODULE_PROPERTIES + PHP_MODULE_GLOBALS(zend_test), + PHP_GINIT(zend_test), + NULL, + NULL, + STANDARD_MODULE_PROPERTIES_EX }; #ifdef COMPILE_DL_ZEND_TEST @@ -320,7 +643,7 @@ ZEND_TSRMLS_CACHE_DEFINE() ZEND_GET_MODULE(zend_test) #endif -struct bug79096 bug79096(void) +PHP_ZEND_TEST_API struct bug79096 bug79096(void) { struct bug79096 b; @@ -329,7 +652,7 @@ struct bug79096 bug79096(void) return b; } -void bug79532(off_t *array, size_t elems) +PHP_ZEND_TEST_API void bug79532(off_t *array, size_t elems) { int i; for (i = 0; i < elems; i++) { @@ -342,3 +665,17 @@ void bug79177(void) { bug79177_cb(); } + +typedef struct bug80847_01 { + uint64_t b; + double c; +} bug80847_01; +typedef struct bug80847_02 { + bug80847_01 a; +} bug80847_02; + +PHP_ZEND_TEST_API bug80847_02 ffi_bug80847(bug80847_02 s) { + s.a.b += 10; + s.a.c -= 10.0; + return s; +} diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php new file mode 100644 index 0000000000..d9db786cee --- /dev/null +++ b/ext/zend_test/test.stub.php @@ -0,0 +1,84 @@ +<?php + +/** @generate-function-entries static */ + +namespace { + +interface _ZendTestInterface +{ +} + +/** @alias _ZendTestClassAlias */ +class _ZendTestClass { + public static function is_object(): int {} + + /** @deprecated */ + public function __toString(): string {} + + public function returnsStatic(): static {} +} + +class _ZendTestChildClass extends _ZendTestClass +{ +} + +trait _ZendTestTrait { + public function testMethod(): bool {} +} + +final class ZendTestAttribute { + +} + +function zend_test_array_return(): array {} + +function zend_test_nullable_array_return(): ?array {} + +function zend_test_void_return(): void {} + +/** @deprecated */ +function zend_test_deprecated(mixed $arg = null): void {} + +function zend_create_unterminated_string(string $str): string {} + +function zend_terminate_string(string &$str): void {} + +function zend_leak_variable(mixed $variable): void {} + +function zend_leak_bytes(int $bytes = 3): void {} + +function zend_string_or_object(object|string $param): object|string {} + +function zend_string_or_object_or_null(object|string|null $param): object|string|null {} + +/** @param stdClass|string $param */ +function zend_string_or_stdclass($param): stdClass|string {} + +/** @param stdClass|string|null $param */ +function zend_string_or_stdclass_or_null($param): stdClass|string|null {} + +function zend_iterable(iterable $arg1, ?iterable $arg2 = null): void {} + +} + +namespace ZendTestNS { + +class Foo { + public function method(): void {} +} + +} + +namespace ZendTestNS2 { + +class Foo { + public function method(): void {} +} + +} + +namespace ZendTestNS2\ZendSubNS { + +function namespaced_func(): bool {} + +} diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h new file mode 100644 index 0000000000..c58d53b457 --- /dev/null +++ b/ext/zend_test/test_arginfo.h @@ -0,0 +1,152 @@ +/* This is a generated file, edit the .stub.php file instead. + * Stub hash: 3240b7fa3461b40a211371250c4975802f44185b */ + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_nullable_array_return, 0, 0, IS_ARRAY, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_void_return, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_deprecated, 0, 0, IS_VOID, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_MIXED, 0, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_create_unterminated_string, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_terminate_string, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(1, str, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_leak_variable, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, variable, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_leak_bytes, 0, 0, IS_VOID, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bytes, IS_LONG, 0, "3") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_zend_string_or_object, 0, 1, MAY_BE_OBJECT|MAY_BE_STRING) + ZEND_ARG_TYPE_MASK(0, param, MAY_BE_OBJECT|MAY_BE_STRING, NULL) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_zend_string_or_object_or_null, 0, 1, MAY_BE_OBJECT|MAY_BE_STRING|MAY_BE_NULL) + ZEND_ARG_TYPE_MASK(0, param, MAY_BE_OBJECT|MAY_BE_STRING|MAY_BE_NULL, NULL) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_zend_string_or_stdclass, 0, 1, stdClass, MAY_BE_STRING) + ZEND_ARG_INFO(0, param) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_zend_string_or_stdclass_or_null, 0, 1, stdClass, MAY_BE_STRING|MAY_BE_NULL) + ZEND_ARG_INFO(0, param) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_iterable, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, arg1, IS_ITERABLE, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_ITERABLE, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ZendTestNS2_ZendSubNS_namespaced_func, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class__ZendTestClass_is_object, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class__ZendTestClass___toString, 0, 0, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class__ZendTestClass_returnsStatic, 0, 0, IS_STATIC, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class__ZendTestTrait_testMethod arginfo_ZendTestNS2_ZendSubNS_namespaced_func + +#define arginfo_class_ZendTestNS_Foo_method arginfo_zend_test_void_return + +#define arginfo_class_ZendTestNS2_Foo_method arginfo_zend_test_void_return + + +static ZEND_FUNCTION(zend_test_array_return); +static ZEND_FUNCTION(zend_test_nullable_array_return); +static ZEND_FUNCTION(zend_test_void_return); +static ZEND_FUNCTION(zend_test_deprecated); +static ZEND_FUNCTION(zend_create_unterminated_string); +static ZEND_FUNCTION(zend_terminate_string); +static ZEND_FUNCTION(zend_leak_variable); +static ZEND_FUNCTION(zend_leak_bytes); +static ZEND_FUNCTION(zend_string_or_object); +static ZEND_FUNCTION(zend_string_or_object_or_null); +static ZEND_FUNCTION(zend_string_or_stdclass); +static ZEND_FUNCTION(zend_string_or_stdclass_or_null); +static ZEND_FUNCTION(zend_iterable); +static ZEND_FUNCTION(namespaced_func); +static ZEND_METHOD(_ZendTestClass, is_object); +static ZEND_METHOD(_ZendTestClass, __toString); +static ZEND_METHOD(_ZendTestClass, returnsStatic); +static ZEND_METHOD(_ZendTestTrait, testMethod); +static ZEND_METHOD(ZendTestNS_Foo, method); +static ZEND_METHOD(ZendTestNS2_Foo, method); + + +static const zend_function_entry ext_functions[] = { + ZEND_FE(zend_test_array_return, arginfo_zend_test_array_return) + ZEND_FE(zend_test_nullable_array_return, arginfo_zend_test_nullable_array_return) + ZEND_FE(zend_test_void_return, arginfo_zend_test_void_return) + ZEND_DEP_FE(zend_test_deprecated, arginfo_zend_test_deprecated) + ZEND_FE(zend_create_unterminated_string, arginfo_zend_create_unterminated_string) + ZEND_FE(zend_terminate_string, arginfo_zend_terminate_string) + ZEND_FE(zend_leak_variable, arginfo_zend_leak_variable) + ZEND_FE(zend_leak_bytes, arginfo_zend_leak_bytes) + ZEND_FE(zend_string_or_object, arginfo_zend_string_or_object) + ZEND_FE(zend_string_or_object_or_null, arginfo_zend_string_or_object_or_null) + ZEND_FE(zend_string_or_stdclass, arginfo_zend_string_or_stdclass) + ZEND_FE(zend_string_or_stdclass_or_null, arginfo_zend_string_or_stdclass_or_null) + ZEND_FE(zend_iterable, arginfo_zend_iterable) + ZEND_NS_FE("ZendTestNS2\\ZendSubNS", namespaced_func, arginfo_ZendTestNS2_ZendSubNS_namespaced_func) + ZEND_FE_END +}; + + +static const zend_function_entry class__ZendTestInterface_methods[] = { + ZEND_FE_END +}; + + +static const zend_function_entry class__ZendTestClass_methods[] = { + ZEND_ME(_ZendTestClass, is_object, arginfo_class__ZendTestClass_is_object, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + ZEND_ME(_ZendTestClass, __toString, arginfo_class__ZendTestClass___toString, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) + ZEND_ME(_ZendTestClass, returnsStatic, arginfo_class__ZendTestClass_returnsStatic, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + + +static const zend_function_entry class__ZendTestChildClass_methods[] = { + ZEND_FE_END +}; + + +static const zend_function_entry class__ZendTestTrait_methods[] = { + ZEND_ME(_ZendTestTrait, testMethod, arginfo_class__ZendTestTrait_testMethod, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + + +static const zend_function_entry class_ZendTestAttribute_methods[] = { + ZEND_FE_END +}; + + +static const zend_function_entry class_ZendTestNS_Foo_methods[] = { + ZEND_ME(ZendTestNS_Foo, method, arginfo_class_ZendTestNS_Foo_method, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + + +static const zend_function_entry class_ZendTestNS2_Foo_methods[] = { + ZEND_ME(ZendTestNS2_Foo, method, arginfo_class_ZendTestNS2_Foo_method, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; diff --git a/ext/zend_test/tests/observer.inc b/ext/zend_test/tests/observer.inc new file mode 100644 index 0000000000..611e9fa0e6 --- /dev/null +++ b/ext/zend_test/tests/observer.inc @@ -0,0 +1,8 @@ +<?php + +function foo_observer_test() +{ + echo __FUNCTION__ . PHP_EOL; +} + +foo_observer_test(); diff --git a/ext/zend_test/tests/observer_backtrace_01.phpt b/ext/zend_test/tests/observer_backtrace_01.phpt new file mode 100644 index 0000000000..ece481cbba --- /dev/null +++ b/ext/zend_test/tests/observer_backtrace_01.phpt @@ -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%eobserver_backtrace_%d.php' --> +<!-- + {main} %s%eobserver_backtrace_%d.php +--> +<file '%s%eobserver_backtrace_%d.php'> + <!-- init foo() --> + <!-- + foo() + {main} %s%eobserver_backtrace_%d.php + --> + <foo> + <!-- init gen() --> + <!-- + gen() + Generator::current() + foo() + {main} %s%eobserver_backtrace_%d.php + --> + <gen> + <!-- init TestClass::foo() --> + <!-- + TestClass::foo() + gen() + Generator::current() + foo() + {main} %s%eobserver_backtrace_%d.php + --> + <TestClass::foo> + <!-- init TestClass::{closure}() --> + <!-- + TestClass::{closure}() + array_map() + TestClass::foo() + gen() + Generator::current() + 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}> + </TestClass::foo> + </gen> + </foo> +array(2) { + [0]=> + int(42) + [1]=> + int(1337) +} +</file '%s%eobserver_backtrace_%d.php'> diff --git a/ext/zend_test/tests/observer_basic_01.phpt b/ext/zend_test/tests/observer_basic_01.phpt new file mode 100644 index 0000000000..064ed99a29 --- /dev/null +++ b/ext/zend_test/tests/observer_basic_01.phpt @@ -0,0 +1,55 @@ +--TEST-- +Observer: Basic observability of userland functions +--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 +--FILE-- +<?php +function bar() +{ + echo 'Bar' . PHP_EOL; + var_dump(array_sum([1,2,3])); +} + +function foo() +{ + echo 'Foo' . PHP_EOL; + bar(); +} + +foo(); +foo(); +foo(); + +echo 'DONE' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_basic_01.php' --> +<file '%s%eobserver_basic_01.php'> + <!-- init foo() --> + <foo> +Foo + <!-- init bar() --> + <bar> +Bar +int(6) + </bar> + </foo> + <foo> +Foo + <bar> +Bar +int(6) + </bar> + </foo> + <foo> +Foo + <bar> +Bar +int(6) + </bar> + </foo> +DONE +</file '%s%eobserver_basic_01.php'> diff --git a/ext/zend_test/tests/observer_basic_02.phpt b/ext/zend_test/tests/observer_basic_02.phpt new file mode 100644 index 0000000000..2b4d632d69 --- /dev/null +++ b/ext/zend_test/tests/observer_basic_02.phpt @@ -0,0 +1,59 @@ +--TEST-- +Observer: Basic observability of userland methods +--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 +--FILE-- +<?php +class TestClass +{ + private function bar() + { + echo 'Bar' . PHP_EOL; + var_dump(array_sum([1,2,3])); + } + + public function foo() + { + echo 'Foo' . PHP_EOL; + $this->bar(); + } +} + +$test = new TestClass(); +$test->foo(); +$test->foo(); +$test->foo(); + +echo 'DONE' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_basic_02.php' --> +<file '%s%eobserver_basic_02.php'> + <!-- init TestClass::foo() --> + <TestClass::foo> +Foo + <!-- init TestClass::bar() --> + <TestClass::bar> +Bar +int(6) + </TestClass::bar> + </TestClass::foo> + <TestClass::foo> +Foo + <TestClass::bar> +Bar +int(6) + </TestClass::bar> + </TestClass::foo> + <TestClass::foo> +Foo + <TestClass::bar> +Bar +int(6) + </TestClass::bar> + </TestClass::foo> +DONE +</file '%s%eobserver_basic_02.php'> diff --git a/ext/zend_test/tests/observer_basic_03.phpt b/ext/zend_test/tests/observer_basic_03.phpt new file mode 100644 index 0000000000..8675a0fdc0 --- /dev/null +++ b/ext/zend_test/tests/observer_basic_03.phpt @@ -0,0 +1,36 @@ +--TEST-- +Observer: Basic observability of includes +--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 +--FILE-- +<?php +function foo() +{ + echo 'Foo' . PHP_EOL; +} + +foo(); +include __DIR__ . '/observer.inc'; +foo(); +?> +--EXPECTF-- +<!-- init '%s%eobserver_basic_03.php' --> +<file '%s%eobserver_basic_03.php'> + <!-- init foo() --> + <foo> +Foo + </foo> + <!-- init '%s%eobserver.inc' --> + <file '%s%eobserver.inc'> + <!-- init foo_observer_test() --> + <foo_observer_test> +foo_observer_test + </foo_observer_test> + </file '%s%eobserver.inc'> + <foo> +Foo + </foo> +</file '%s%eobserver_basic_03.php'> diff --git a/ext/zend_test/tests/observer_basic_04.phpt b/ext/zend_test/tests/observer_basic_04.phpt new file mode 100644 index 0000000000..6fc1fae1c0 --- /dev/null +++ b/ext/zend_test/tests/observer_basic_04.phpt @@ -0,0 +1,30 @@ +--TEST-- +Observer: Basic observability of includes only (no functions) +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_includes=1 +--FILE-- +<?php +function foo() +{ + echo 'Foo' . PHP_EOL; +} + +foo(); +include __DIR__ . '/observer.inc'; +foo(); +?> +--EXPECTF-- +<!-- init '%s%eobserver_basic_04.php' --> +<file '%s%eobserver_basic_04.php'> + <!-- init foo() --> +Foo + <!-- init '%s%eobserver.inc' --> + <file '%s%eobserver.inc'> + <!-- init foo_observer_test() --> +foo_observer_test + </file '%s%eobserver.inc'> +Foo +</file '%s%eobserver_basic_04.php'> diff --git a/ext/zend_test/tests/observer_basic_05.phpt b/ext/zend_test/tests/observer_basic_05.phpt new file mode 100644 index 0000000000..de14ebc1fd --- /dev/null +++ b/ext/zend_test/tests/observer_basic_05.phpt @@ -0,0 +1,32 @@ +--TEST-- +Observer: Basic observability of functions only (no includes) +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_functions=1 +--FILE-- +<?php +function foo() +{ + echo 'Foo' . PHP_EOL; +} + +foo(); +include __DIR__ . '/observer.inc'; +foo(); +?> +--EXPECTF-- +<!-- init '%s%eobserver_basic_05.php' --> +<!-- init foo() --> +<foo> +Foo +</foo> +<!-- init '%s%eobserver.inc' --> +<!-- init foo_observer_test() --> +<foo_observer_test> +foo_observer_test +</foo_observer_test> +<foo> +Foo +</foo> diff --git a/ext/zend_test/tests/observer_call_user_func_01.phpt b/ext/zend_test/tests/observer_call_user_func_01.phpt new file mode 100644 index 0000000000..802db43e33 --- /dev/null +++ b/ext/zend_test/tests/observer_call_user_func_01.phpt @@ -0,0 +1,40 @@ +--TEST-- +Observer: call_user_func() from root namespace +--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 +--FILE-- +<?php +namespace Test { + final class MyClass + { + public static function myMethod() + { + echo 'MyClass::myMethod called' . PHP_EOL; + } + } + + function my_function() + { + echo 'my_function called' . PHP_EOL; + } +} +namespace { + call_user_func('Test\\MyClass::myMethod'); + call_user_func('Test\\my_function'); +} +?> +--EXPECTF-- +<!-- init '%s%eobserver_call_user_func_%d.php' --> +<file '%s%eobserver_call_user_func_%d.php'> + <!-- init Test\MyClass::myMethod() --> + <Test\MyClass::myMethod> +MyClass::myMethod called + </Test\MyClass::myMethod> + <!-- init Test\my_function() --> + <Test\my_function> +my_function called + </Test\my_function> +</file '%s%eobserver_call_user_func_%d.php'> diff --git a/ext/zend_test/tests/observer_call_user_func_02.phpt b/ext/zend_test/tests/observer_call_user_func_02.phpt new file mode 100644 index 0000000000..25de267c06 --- /dev/null +++ b/ext/zend_test/tests/observer_call_user_func_02.phpt @@ -0,0 +1,40 @@ +--TEST-- +Observer: call_user_func_array() from root namespace +--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 +--FILE-- +<?php +namespace Test { + final class MyClass + { + public static function myMethod(string $msg) + { + echo 'MyClass::myMethod ' . $msg . PHP_EOL; + } + } + + function my_function(string $msg) + { + echo 'my_function ' . $msg . PHP_EOL; + } +} +namespace { + call_user_func_array('Test\\MyClass::myMethod', ['called']); + call_user_func_array('Test\\my_function', ['called']); +} +?> +--EXPECTF-- +<!-- init '%s%eobserver_call_user_func_%d.php' --> +<file '%s%eobserver_call_user_func_%d.php'> + <!-- init Test\MyClass::myMethod() --> + <Test\MyClass::myMethod> +MyClass::myMethod called + </Test\MyClass::myMethod> + <!-- init Test\my_function() --> + <Test\my_function> +my_function called + </Test\my_function> +</file '%s%eobserver_call_user_func_%d.php'> diff --git a/ext/zend_test/tests/observer_call_user_func_03.phpt b/ext/zend_test/tests/observer_call_user_func_03.phpt new file mode 100644 index 0000000000..7c041cbc70 --- /dev/null +++ b/ext/zend_test/tests/observer_call_user_func_03.phpt @@ -0,0 +1,39 @@ +--TEST-- +Observer: call_user_func() from namespace +--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 +--FILE-- +<?php +namespace Test { + final class MyClass + { + public static function myMethod() + { + echo 'MyClass::myMethod called' . PHP_EOL; + } + } + + function my_function() + { + echo 'my_function called' . PHP_EOL; + } + + call_user_func('Test\\MyClass::myMethod'); + call_user_func('Test\\my_function'); +} +?> +--EXPECTF-- +<!-- init '%s%eobserver_call_user_func_%d.php' --> +<file '%s%eobserver_call_user_func_%d.php'> + <!-- init Test\MyClass::myMethod() --> + <Test\MyClass::myMethod> +MyClass::myMethod called + </Test\MyClass::myMethod> + <!-- init Test\my_function() --> + <Test\my_function> +my_function called + </Test\my_function> +</file '%s%eobserver_call_user_func_%d.php'> diff --git a/ext/zend_test/tests/observer_call_user_func_04.phpt b/ext/zend_test/tests/observer_call_user_func_04.phpt new file mode 100644 index 0000000000..ebcf06082b --- /dev/null +++ b/ext/zend_test/tests/observer_call_user_func_04.phpt @@ -0,0 +1,39 @@ +--TEST-- +Observer: call_user_func_array() from namespace +--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 +--FILE-- +<?php +namespace Test { + final class MyClass + { + public static function myMethod(string $msg) + { + echo 'MyClass::myMethod ' . $msg . PHP_EOL; + } + } + + function my_function(string $msg) + { + echo 'my_function ' . $msg . PHP_EOL; + } + + call_user_func_array('Test\\MyClass::myMethod', ['called']); + call_user_func_array('Test\\my_function', ['called']); +} +?> +--EXPECTF-- +<!-- init '%s%eobserver_call_user_func_%d.php' --> +<file '%s%eobserver_call_user_func_%d.php'> + <!-- init Test\MyClass::myMethod() --> + <Test\MyClass::myMethod> +MyClass::myMethod called + </Test\MyClass::myMethod> + <!-- init Test\my_function() --> + <Test\my_function> +my_function called + </Test\my_function> +</file '%s%eobserver_call_user_func_%d.php'> diff --git a/ext/zend_test/tests/observer_closure_01.phpt b/ext/zend_test/tests/observer_closure_01.phpt new file mode 100644 index 0000000000..85312b32ef --- /dev/null +++ b/ext/zend_test/tests/observer_closure_01.phpt @@ -0,0 +1,49 @@ +--TEST-- +Observer: Basic observability of closures +--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 +--FILE-- +<?php +$bar = function() { + var_dump(array_sum([40,2])); +}; + +$foo = function($bar) { + echo 'Answer' . PHP_EOL; + $bar(); +}; + +$foo($bar); +$foo($bar); +$foo($bar); + +echo 'DONE' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_closure_%d.php' --> +<file '%s%eobserver_closure_%d.php'> + <!-- init {closure}() --> + <{closure}> +Answer + <!-- init {closure}() --> + <{closure}> +int(42) + </{closure}> + </{closure}> + <{closure}> +Answer + <{closure}> +int(42) + </{closure}> + </{closure}> + <{closure}> +Answer + <{closure}> +int(42) + </{closure}> + </{closure}> +DONE +</file '%s%eobserver_closure_%d.php'> diff --git a/ext/zend_test/tests/observer_closure_02.phpt b/ext/zend_test/tests/observer_closure_02.phpt new file mode 100644 index 0000000000..9d6cc900e3 --- /dev/null +++ b/ext/zend_test/tests/observer_closure_02.phpt @@ -0,0 +1,32 @@ +--TEST-- +Observer: Observability of fake closures +--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 +--FILE-- +<?php +class Foo +{ + public function bar() + { + echo 'Called as fake closure.' . PHP_EOL; + } +} + +$callable = [new Foo(), 'bar']; +$closure = \Closure::fromCallable($callable); +$closure(); + +echo 'DONE' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_closure_%d.php' --> +<file '%s%eobserver_closure_%d.php'> + <!-- init Foo::bar() --> + <Foo::bar> +Called as fake closure. + </Foo::bar> +DONE +</file '%s%eobserver_closure_%d.php'> diff --git a/ext/zend_test/tests/observer_error_01.phpt b/ext/zend_test/tests/observer_error_01.phpt new file mode 100644 index 0000000000..e80a9ec3c7 --- /dev/null +++ b/ext/zend_test/tests/observer_error_01.phpt @@ -0,0 +1,29 @@ +--TEST-- +Observer: End handlers fire after a fatal error +--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_return_value=1 +memory_limit=1M +--FILE-- +<?php +function foo() +{ + str_repeat('.', 1024 * 1024 * 2); // 2MB +} + +foo(); + +echo 'You should not see this.'; +?> +--EXPECTF-- +<!-- init '%s%eobserver_error_%d.php' --> +<file '%s%eobserver_error_%d.php'> + <!-- init foo() --> + <foo> + +Fatal error: Allowed memory size of 2097152 bytes exhausted%s(tried to allocate %d bytes) in %s on line %d + </foo:NULL> +</file '%s%eobserver_error_%d.php'> diff --git a/ext/zend_test/tests/observer_error_02.phpt b/ext/zend_test/tests/observer_error_02.phpt new file mode 100644 index 0000000000..9dfa71d182 --- /dev/null +++ b/ext/zend_test/tests/observer_error_02.phpt @@ -0,0 +1,28 @@ +--TEST-- +Observer: End handlers fire after a userland fatal error +--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_return_value=1 +--FILE-- +<?php +function foo() +{ + trigger_error('Foo error', E_USER_ERROR); +} + +foo(); + +echo 'You should not see this.'; +?> +--EXPECTF-- +<!-- init '%s%eobserver_error_%d.php' --> +<file '%s%eobserver_error_%d.php'> + <!-- init foo() --> + <foo> + +Fatal error: Foo error in %s on line %d + </foo:NULL> +</file '%s%eobserver_error_%d.php'> diff --git a/ext/zend_test/tests/observer_error_03.phpt b/ext/zend_test/tests/observer_error_03.phpt new file mode 100644 index 0000000000..606b827bb2 --- /dev/null +++ b/ext/zend_test/tests/observer_error_03.phpt @@ -0,0 +1,39 @@ +--TEST-- +Observer: non-fatal errors do not fire end handlers prematurely +--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_return_value=1 +--FILE-- +<?php +function foo() +{ + return $this_does_not_exit; // E_WARNING +} + +function main() +{ + foo(); + echo 'After error.' . PHP_EOL; +} + +main(); + +echo 'Done.' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_error_%d.php' --> +<file '%s%eobserver_error_%d.php'> + <!-- init main() --> + <main> + <!-- init foo() --> + <foo> + +Warning: Undefined variable $this_does_not_exit in %s on line %d + </foo:NULL> +After error. + </main:NULL> +Done. +</file '%s%eobserver_error_%d.php'> diff --git a/ext/zend_test/tests/observer_error_04.phpt b/ext/zend_test/tests/observer_error_04.phpt new file mode 100644 index 0000000000..49c56c17ab --- /dev/null +++ b/ext/zend_test/tests/observer_error_04.phpt @@ -0,0 +1,46 @@ +--TEST-- +Observer: fatal errors caught with zend_try will not fire end handlers prematurely +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +<?php if (!extension_loaded('soap')) die('skip: soap extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +<?php +function foo() +{ + // ext/soap catches a zend_bailout and then throws an exception + $client = new SoapClient('foo'); +} + +function main() +{ + foo(); +} + +// try/catch is on main() to ensure ZEND_HANDLE_EXCEPTION does not fire the end handlers again +try { + main(); +} catch (SoapFault $e) { + echo $e->getMessage() . PHP_EOL; +} + +echo 'Done.' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_error_%d.php' --> +<file '%s%eobserver_error_%d.php'> + <!-- init main() --> + <main> + <!-- init foo() --> + <foo> + <!-- Exception: SoapFault --> + </foo:NULL> + <!-- Exception: SoapFault --> + </main:NULL> +SOAP-ERROR: Parsing WSDL: Couldn't load from 'foo' : failed to load external entity "foo" + +Done. +</file '%s%eobserver_error_%d.php'> diff --git a/ext/zend_test/tests/observer_error_05.phpt b/ext/zend_test/tests/observer_error_05.phpt new file mode 100644 index 0000000000..df05cdd8ed --- /dev/null +++ b/ext/zend_test/tests/observer_error_05.phpt @@ -0,0 +1,37 @@ +--TEST-- +Observer: End handlers fire after a userland fatal error +--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_return_value=1 +--XFAIL-- +This is unsafe and fails on macos +--FILE-- +<?php +set_error_handler(function ($errno, $errstr, $errfile, $errline) { + trigger_error('Foo error', E_USER_ERROR); +}); + +function foo() +{ + return $x; // warning +} + +foo(); + +echo 'You should not see this.'; +?> +--EXPECTF-- +<!-- init '%s%eobserver_error_%d.php' --> +<file '%s%eobserver_error_%d.php'> + <!-- init foo() --> + <foo> + <!-- init {closure}() --> + <{closure}> + +Fatal error: Foo error in %s on line %d + </{closure}:NULL> + </foo:NULL> +</file '%s%eobserver_error_%d.php'> diff --git a/ext/zend_test/tests/observer_eval_01.phpt b/ext/zend_test/tests/observer_eval_01.phpt new file mode 100644 index 0000000000..addce2dba5 --- /dev/null +++ b/ext/zend_test/tests/observer_eval_01.phpt @@ -0,0 +1,21 @@ +--TEST-- +Observer: Basic eval observability +--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 +--FILE-- +<?php +echo eval("return 'Foo eval' . PHP_EOL;"); +echo 'DONE' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_eval_%d.php' --> +<file '%s%eobserver_eval_%d.php'> + <!-- init '%s%eobserver_eval_%d.php(%d) : eval()'d code' --> + <file '%s%eobserver_eval_%d.php(%d) : eval()'d code'> + </file '%s%eobserver_eval_%d.php(%d) : eval()'d code'> +Foo eval +DONE +</file '%s%eobserver_eval_%d.php'> diff --git a/ext/zend_test/tests/observer_exception_01.phpt b/ext/zend_test/tests/observer_exception_01.phpt new file mode 100644 index 0000000000..6ded60c970 --- /dev/null +++ b/ext/zend_test/tests/observer_exception_01.phpt @@ -0,0 +1,46 @@ +--TEST-- +Observer: Basic observability of userland functions with uncaught exceptions +--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 +--FILE-- +<?php +function foo() +{ + static $callCount = 0; + echo 'Call #' . $callCount . PHP_EOL; + if (++$callCount == 3) { + throw new RuntimeException('Third time is a charm'); + } +} + +foo(); +foo(); +foo(); + +echo 'You should not see this' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_exception_%d.php' --> +<file '%s%eobserver_exception_%d.php'> + <!-- init foo() --> + <foo> +Call #0 + </foo> + <foo> +Call #1 + </foo> + <foo> +Call #2 + <!-- Exception: RuntimeException --> + </foo> + <!-- Exception: RuntimeException --> +</file '%s%eobserver_exception_%d.php'> + +Fatal error: Uncaught RuntimeException: Third time is a charm in %s%eobserver_exception_%d.php:%d +Stack trace: +#0 %s%eobserver_exception_%d.php(%d): foo() +#1 {main} + thrown in %s%eobserver_exception_%d.php on line %d diff --git a/ext/zend_test/tests/observer_generator_01.phpt b/ext/zend_test/tests/observer_generator_01.phpt new file mode 100644 index 0000000000..34314cbf36 --- /dev/null +++ b/ext/zend_test/tests/observer_generator_01.phpt @@ -0,0 +1,47 @@ +--TEST-- +Observer: Basic generator observability +--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_return_value=1 +--FILE-- +<?php +function getResults() { + for ($i = 10; $i < 13; $i++) { + yield $i; + } +} + +function doSomething() { + $generator = getResults(); + foreach ($generator as $value) { + echo $value . PHP_EOL; + } + + return 'Done'; +} + +echo doSomething() . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_generator_%d.php' --> +<file '%s%eobserver_generator_%d.php'> + <!-- init doSomething() --> + <doSomething> + <!-- init getResults() --> + <getResults> + </getResults:10> +10 + <getResults> + </getResults:11> +11 + <getResults> + </getResults:12> +12 + <getResults> + </getResults:NULL> + </doSomething:'Done'> +Done +</file '%s%eobserver_generator_%d.php'> diff --git a/ext/zend_test/tests/observer_generator_02.phpt b/ext/zend_test/tests/observer_generator_02.phpt new file mode 100644 index 0000000000..9d29b9e8e5 --- /dev/null +++ b/ext/zend_test/tests/observer_generator_02.phpt @@ -0,0 +1,50 @@ +--TEST-- +Observer: Generator with explicit return +--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_return_value=1 +--FILE-- +<?php +function getResults() { + for ($i = 10; $i < 13; $i++) { + yield $i; + } + return 1337; +} + +function doSomething() { + $generator = getResults(); + foreach ($generator as $value) { + echo $value . PHP_EOL; + } + echo $generator->getReturn() . PHP_EOL; + + return 'Done'; +} + +echo doSomething() . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_generator_%d.php' --> +<file '%s%eobserver_generator_%d.php'> + <!-- init doSomething() --> + <doSomething> + <!-- init getResults() --> + <getResults> + </getResults:10> +10 + <getResults> + </getResults:11> +11 + <getResults> + </getResults:12> +12 + <getResults> + </getResults:1337> +1337 + </doSomething:'Done'> +Done +</file '%s%eobserver_generator_%d.php'> diff --git a/ext/zend_test/tests/observer_generator_03.phpt b/ext/zend_test/tests/observer_generator_03.phpt new file mode 100644 index 0000000000..cad6e3bb96 --- /dev/null +++ b/ext/zend_test/tests/observer_generator_03.phpt @@ -0,0 +1,68 @@ +--TEST-- +Observer: Generator with 'yield from' +--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_return_value=1 +--FILE-- +<?php +function barResults() { + for ($i = 10; $i < 13; $i++) { + yield $i; + } +} + +function fooResults() { + yield 0; + yield 1; + yield from barResults(); + yield 42; +} + +function doSomething() { + $generator = fooResults(); + foreach ($generator as $value) { + echo $value . PHP_EOL; + } + + return 'Done'; +} + +echo doSomething() . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_generator_%d.php' --> +<file '%s%eobserver_generator_%d.php'> + <!-- init doSomething() --> + <doSomething> + <!-- init fooResults() --> + <fooResults> + </fooResults:0> +0 + <fooResults> + </fooResults:1> +1 + <fooResults> + </fooResults:1> + <!-- init barResults() --> + <barResults> + </barResults:10> +10 + <barResults> + </barResults:11> +11 + <barResults> + </barResults:12> +12 + <barResults> + </barResults:NULL> + <fooResults> + </fooResults:42> +42 + <fooResults> + </fooResults:NULL> + </doSomething:'Done'> +Done +</file '%s%eobserver_generator_%d.php'> diff --git a/ext/zend_test/tests/observer_generator_04.phpt b/ext/zend_test/tests/observer_generator_04.phpt new file mode 100644 index 0000000000..dd941dd78f --- /dev/null +++ b/ext/zend_test/tests/observer_generator_04.phpt @@ -0,0 +1,64 @@ +--TEST-- +Observer: Generator with manual traversal +--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_return_value=1 +--FILE-- +<?php +function fooResults() { + echo 'Starting generator' . PHP_EOL; + $i = 0; + while (true) { + if (yield $i++) break; + } +} + +function doSomething() { + $generator = fooResults(); + + while($generator->current() !== NULL) { + echo $generator->current() . PHP_EOL; + if ($generator->current() === 5) { + $generator->send('Boop'); + } + $generator->next(); + } + + return 'Done'; +} + +echo doSomething() . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_generator_%d.php' --> +<file '%s%eobserver_generator_%d.php'> + <!-- init doSomething() --> + <doSomething> + <!-- init fooResults() --> + <fooResults> +Starting generator + </fooResults:0> +0 + <fooResults> + </fooResults:1> +1 + <fooResults> + </fooResults:2> +2 + <fooResults> + </fooResults:3> +3 + <fooResults> + </fooResults:4> +4 + <fooResults> + </fooResults:5> +5 + <fooResults> + </fooResults:NULL> + </doSomething:'Done'> +Done +</file '%s%eobserver_generator_%d.php'> diff --git a/ext/zend_test/tests/observer_generator_05.phpt b/ext/zend_test/tests/observer_generator_05.phpt new file mode 100644 index 0000000000..8809511361 --- /dev/null +++ b/ext/zend_test/tests/observer_generator_05.phpt @@ -0,0 +1,53 @@ +--TEST-- +Observer: Generator with uncaught exception +--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_return_value=1 +--FILE-- +<?php +function fooResults() { + yield 0; + yield 1; + throw new RuntimeException('Oops!'); +} + +function doSomething() { + $generator = fooResults(); + foreach ($generator as $value) { + echo $value . PHP_EOL; + } + + return 'You should not see this'; +} + +echo doSomething() . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_generator_%d.php' --> +<file '%s%eobserver_generator_%d.php'> + <!-- init doSomething() --> + <doSomething> + <!-- init fooResults() --> + <fooResults> + </fooResults:0> +0 + <fooResults> + </fooResults:1> +1 + <fooResults> + <!-- Exception: RuntimeException --> + </fooResults:NULL> + <!-- Exception: RuntimeException --> + </doSomething:NULL> + <!-- Exception: RuntimeException --> +</file '%s%eobserver_generator_%d.php'> + +Fatal error: Uncaught 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} + thrown in %s%eobserver_generator_%d.php on line %d diff --git a/ext/zend_test/tests/observer_magic_01.phpt b/ext/zend_test/tests/observer_magic_01.phpt new file mode 100644 index 0000000000..bae01e3b17 --- /dev/null +++ b/ext/zend_test/tests/observer_magic_01.phpt @@ -0,0 +1,45 @@ +--TEST-- +Observer: Basic magic method observability +--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 +--FILE-- +<?php +class MagicTest +{ + public function __call($name, $args) + { + echo '__call()' . PHP_EOL; + $this->foo($name); + } + + public function foo($name) + { + echo $name . PHP_EOL; + } +} + +$test = new MagicTest(); +$test->foo('test'); +$test->bar(); + +echo 'DONE' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_magic_01.php' --> +<file '%s%eobserver_magic_01.php'> + <!-- init MagicTest::foo() --> + <MagicTest::foo> +test + </MagicTest::foo> + <!-- init MagicTest::__call() --> + <MagicTest::__call> +__call() + <MagicTest::foo> +bar + </MagicTest::foo> + </MagicTest::__call> +DONE +</file '%s%eobserver_magic_01.php'> diff --git a/ext/zend_test/tests/observer_opline_01.phpt b/ext/zend_test/tests/observer_opline_01.phpt new file mode 100644 index 0000000000..924c22b3a0 --- /dev/null +++ b/ext/zend_test/tests/observer_opline_01.phpt @@ -0,0 +1,53 @@ +--TEST-- +Observer: Ensure opline exists on the execute_data +--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_opcode=1 +--FILE-- +<?php +function foo() +{ + echo 'Foo' . PHP_EOL; +} + +foo(); +include __DIR__ . '/observer.inc'; +echo array_sum([1,2,3]) . PHP_EOL; +foo(); +?> +--EXPECTF-- +<!-- init '%s%eobserver_opline_%d.php' --> +<!-- opcode: 'ZEND_INIT_FCALL' --> +<file '%s%eobserver_opline_%d.php'> + <!-- opcode: 'ZEND_INIT_FCALL' --> + <!-- init foo() --> + <!-- opcode: 'ZEND_ECHO' --> + <foo> + <!-- opcode: 'ZEND_ECHO' --> +Foo + <!-- opcode: 'ZEND_RETURN' --> + </foo> + <!-- init '%s%eobserver.inc' --> + <!-- opcode: 'ZEND_INIT_FCALL' --> + <file '%s%eobserver.inc'> + <!-- opcode: 'ZEND_INIT_FCALL' --> + <!-- init foo_observer_test() --> + <!-- opcode: 'ZEND_ECHO' --> + <foo_observer_test> + <!-- opcode: 'ZEND_ECHO' --> +foo_observer_test + <!-- opcode: 'ZEND_RETURN' --> + </foo_observer_test> + <!-- opcode: 'ZEND_RETURN' --> + </file '%s%eobserver.inc'> +6 + <foo> + <!-- opcode: 'ZEND_ECHO' --> +Foo + <!-- opcode: 'ZEND_RETURN' --> + </foo> + <!-- opcode: 'ZEND_RETURN' --> +</file '%s%eobserver_opline_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_01.phpt b/ext/zend_test/tests/observer_retval_01.phpt new file mode 100644 index 0000000000..1359a00d40 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_01.phpt @@ -0,0 +1,30 @@ +--TEST-- +Observer: Retvals are observable that are: IS_CONST +--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_return_value=1 +opcache.optimization_level=0 +--FILE-- +<?php +function foo() { + return 'I should be observable'; // IS_CONST +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_retval_%d.php' --> +<file '%s%eobserver_retval_%d.php'> + <!-- init foo() --> + <foo> + </foo:'I should be observable'> + <foo> + </foo:'I should be observable'> +Done +</file '%s%eobserver_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_02.phpt b/ext/zend_test/tests/observer_retval_02.phpt new file mode 100644 index 0000000000..b48d8d6b7e --- /dev/null +++ b/ext/zend_test/tests/observer_retval_02.phpt @@ -0,0 +1,32 @@ +--TEST-- +Observer: Unused retvals from generators are still observable +--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_return_value=1 +--FILE-- +<?php +function foo() { + yield 'I should be observable'; + yield 'Me too!'; +} + +$gen = foo(); +$gen->current(); +$gen->next(); +$gen->current(); + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_retval_%d.php' --> +<file '%s%eobserver_retval_%d.php'> + <!-- init foo() --> + <foo> + </foo:'I should be observable'> + <foo> + </foo:'Me too!'> +Done +</file '%s%eobserver_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_03.phpt b/ext/zend_test/tests/observer_retval_03.phpt new file mode 100644 index 0000000000..227fe5670a --- /dev/null +++ b/ext/zend_test/tests/observer_retval_03.phpt @@ -0,0 +1,32 @@ +--TEST-- +Observer: Retvals are observable that are: refcounted, IS_CV +--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_return_value=1 +--FILE-- +<?php +class MyRetval {} + +function foo() { + $retval = new MyRetval(); // Refcounted + return $retval; // IS_CV +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_retval_%d.php' --> +<file '%s%eobserver_retval_%d.php'> + <!-- init foo() --> + <foo> + </foo:object(MyRetval)#%d> + <foo> + </foo:object(MyRetval)#%d> +Done +</file '%s%eobserver_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_04.phpt b/ext/zend_test/tests/observer_retval_04.phpt new file mode 100644 index 0000000000..f6aea0d1e2 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_04.phpt @@ -0,0 +1,52 @@ +--TEST-- +Observer: Retvals are observable that are: refcounted, IS_VAR +--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_return_value=1 +--FILE-- +<?php +class MyRetval {} + +function getObj() { + return new MyRetval(); // Refcounted +} + +function foo() { + return getObj(); // IS_VAR +} + +$res = foo(); // Retval used +foo(); // Retval unused + +function bar($what) { + return 'This gets ' . $what . ' in the return handler when unused'; // Refcounted + IS_VAR +} + +$res = bar('freed'); // Retval used +bar('freed'); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_retval_%d.php' --> +<file '%s%eobserver_retval_%d.php'> + <!-- init foo() --> + <foo> + <!-- init getObj() --> + <getObj> + </getObj:object(MyRetval)#%d> + </foo:object(MyRetval)#%d> + <foo> + <getObj> + </getObj:object(MyRetval)#%d> + </foo:object(MyRetval)#%d> + <!-- init bar() --> + <bar> + </bar:'This gets freed in the return handler when unused'> + <bar> + </bar:'This gets freed in the return handler when unused'> +Done +</file '%s%eobserver_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_05.phpt b/ext/zend_test/tests/observer_retval_05.phpt new file mode 100644 index 0000000000..e987a06d7a --- /dev/null +++ b/ext/zend_test/tests/observer_retval_05.phpt @@ -0,0 +1,33 @@ +--TEST-- +Observer: Retvals are observable that are: IS_CV, IS_UNDEF +--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_return_value=1 +--FILE-- +<?php +function foo() { + return $i_do_not_exist; // IS_CV && IS_UNDEF +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_retval_%d.php' --> +<file '%s%eobserver_retval_%d.php'> + <!-- init foo() --> + <foo> + +Warning: Undefined variable $i_do_not_exist in %s on line %d + </foo:NULL> + <foo> + +Warning: Undefined variable $i_do_not_exist in %s on line %d + </foo:NULL> +Done +</file '%s%eobserver_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_06.phpt b/ext/zend_test/tests/observer_retval_06.phpt new file mode 100644 index 0000000000..b3a64b3ee8 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_06.phpt @@ -0,0 +1,30 @@ +--TEST-- +Observer: Retvals are observable that are: IS_CV +--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_return_value=1 +--FILE-- +<?php +function foo() { + $retval = 'I should be observable'; + return $retval; // IS_CV +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_retval_%d.php' --> +<file '%s%eobserver_retval_%d.php'> + <!-- init foo() --> + <foo> + </foo:'I should be observable'> + <foo> + </foo:'I should be observable'> +Done +</file '%s%eobserver_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_07.phpt b/ext/zend_test/tests/observer_retval_07.phpt new file mode 100644 index 0000000000..678027a01a --- /dev/null +++ b/ext/zend_test/tests/observer_retval_07.phpt @@ -0,0 +1,39 @@ +--TEST-- +Observer: Retvals are observable that are: IS_REFERENCE, IS_VAR +--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_return_value=1 +--FILE-- +<?php +function &getMessage() { + $retval = 'I should be observable'; + return $retval; +} + +function foo() { + return getMessage(); // IS_REFERENCE + IS_VAR +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_retval_%d.php' --> +<file '%s%eobserver_retval_%d.php'> + <!-- init foo() --> + <foo> + <!-- init getMessage() --> + <getMessage> + </getMessage:'I should be observable'> + </foo:'I should be observable'> + <foo> + <getMessage> + </getMessage:'I should be observable'> + </foo:'I should be observable'> +Done +</file '%s%eobserver_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_by_ref_01.phpt b/ext/zend_test/tests/observer_retval_by_ref_01.phpt new file mode 100644 index 0000000000..2e7d910aac --- /dev/null +++ b/ext/zend_test/tests/observer_retval_by_ref_01.phpt @@ -0,0 +1,30 @@ +--TEST-- +Observer: Retvals by reference are observable that are: IS_CV +--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_return_value=1 +--FILE-- +<?php +function &foo() { + $retval = 'I should be observable'; + return $retval; // IS_CV +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_retval_by_ref_%d.php' --> +<file '%s%eobserver_retval_by_ref_%d.php'> + <!-- init foo() --> + <foo> + </foo:'I should be observable'> + <foo> + </foo:'I should be observable'> +Done +</file '%s%eobserver_retval_by_ref_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_by_ref_02.phpt b/ext/zend_test/tests/observer_retval_by_ref_02.phpt new file mode 100644 index 0000000000..7283a8d967 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_by_ref_02.phpt @@ -0,0 +1,34 @@ +--TEST-- +Observer: Retvals by reference are observable that are: IS_TMP_VAR +--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_return_value=1 +--FILE-- +<?php +function &foo() { + $retval = 'I should be '; + return $retval . 'observable'; // IS_TMP_VAR +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_retval_by_ref_%d.php' --> +<file '%s%eobserver_retval_by_ref_%d.php'> + <!-- init foo() --> + <foo> + +Notice: Only variable references should be returned by reference in %s on line %d + </foo:'I should be observable'> + <foo> + +Notice: Only variable references should be returned by reference in %s on line %d + </foo:'I should be observable'> +Done +</file '%s%eobserver_retval_by_ref_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_by_ref_03.phpt b/ext/zend_test/tests/observer_retval_by_ref_03.phpt new file mode 100644 index 0000000000..b75e589095 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_by_ref_03.phpt @@ -0,0 +1,43 @@ +--TEST-- +Observer: Retvals by reference are observable that are: IS_VAR, ZEND_RETURNS_FUNCTION +--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_return_value=1 +opcache.optimization_level=0 +--FILE-- +<?php +function getMessage() { + return 'I should be observable'; +} + +function &foo() { + return getMessage(); // IS_VAR + ZEND_RETURNS_FUNCTION +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_retval_by_ref_%d.php' --> +<file '%s%eobserver_retval_by_ref_%d.php'> + <!-- init foo() --> + <foo> + <!-- init getMessage() --> + <getMessage> + </getMessage:'I should be observable'> + +Notice: Only variable references should be returned by reference in %s on line %d + </foo:'I should be observable'> + <foo> + <getMessage> + </getMessage:'I should be observable'> + +Notice: Only variable references should be returned by reference in %s on line %d + </foo:'I should be observable'> +Done +</file '%s%eobserver_retval_by_ref_%d.php'> diff --git a/ext/zend_test/tests/observer_shutdown_01.phpt b/ext/zend_test/tests/observer_shutdown_01.phpt new file mode 100644 index 0000000000..5fcc7aeb81 --- /dev/null +++ b/ext/zend_test/tests/observer_shutdown_01.phpt @@ -0,0 +1,44 @@ +--TEST-- +Observer: Function calls from a shutdown handler are observable +--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_return_value=1 +--FILE-- +<?php +register_shutdown_function(function () { + echo 'Shutdown: ' . foo() . PHP_EOL; +}); + +function bar($arg) { + return $arg; +} + +function foo() { + bar(41); + return bar(42); +} + +echo 'Done: ' . bar(40) . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_shutdown_%d.php' --> +<file '%s%eobserver_shutdown_%d.php'> + <!-- init bar() --> + <bar> + </bar:40> +Done: 40 +</file '%s%eobserver_shutdown_%d.php'> +<!-- init {closure}() --> +<{closure}> + <!-- init foo() --> + <foo> + <bar> + </bar:41> + <bar> + </bar:42> + </foo:42> +Shutdown: 42 +</{closure}:NULL> diff --git a/ext/zend_test/tests/observer_shutdown_02.phpt b/ext/zend_test/tests/observer_shutdown_02.phpt new file mode 100644 index 0000000000..410403df1a --- /dev/null +++ b/ext/zend_test/tests/observer_shutdown_02.phpt @@ -0,0 +1,50 @@ +--TEST-- +Observer: Function calls from a __destruct during shutdown are observable +--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_return_value=1 +--FILE-- +<?php +class MyClass +{ + public function __destruct() + { + echo 'Shutdown: ' . foo() . PHP_EOL; + } +} + +function bar($arg) { + return $arg; +} + +function foo() { + bar(41); + return bar(42); +} + +$mc = new MyClass(); + +echo 'Done: ' . bar(40) . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_shutdown_%d.php' --> +<file '%s%eobserver_shutdown_%d.php'> + <!-- init bar() --> + <bar> + </bar:40> +Done: 40 +</file '%s%eobserver_shutdown_%d.php'> +<!-- init MyClass::__destruct() --> +<MyClass::__destruct> + <!-- init foo() --> + <foo> + <bar> + </bar:41> + <bar> + </bar:42> + </foo:42> +Shutdown: 42 +</MyClass::__destruct:NULL> diff --git a/ext/zend_test/tests/observer_types_01.phpt b/ext/zend_test/tests/observer_types_01.phpt new file mode 100644 index 0000000000..7b5408a456 --- /dev/null +++ b/ext/zend_test/tests/observer_types_01.phpt @@ -0,0 +1,28 @@ +--TEST-- +Observer: Observe basic TypeError +--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_return_value=1 +--FILE-- +<?php +function foo(array $a) { return 1; } +foo(42); +?> +--EXPECTF-- +<!-- init '%s%eobserver_types_%d.php' --> +<file '%s%eobserver_types_%d.php'> + <!-- init foo() --> + <foo> + <!-- Exception: TypeError --> + </foo:NULL> + <!-- Exception: TypeError --> +</file '%s%eobserver_types_%d.php'> + +Fatal error: Uncaught 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} + thrown in %s%eobserver_types_%d.php on line %d diff --git a/ext/zend_test/tests/observer_zend_call_function_01.phpt b/ext/zend_test/tests/observer_zend_call_function_01.phpt new file mode 100644 index 0000000000..73b0f92cce --- /dev/null +++ b/ext/zend_test/tests/observer_zend_call_function_01.phpt @@ -0,0 +1,36 @@ +--TEST-- +Observer: Calls that go through zend_call_function are observed +--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 +--FILE-- +<?php +function sum($carry, $item) { + $carry += $item; + return $carry; +} + +$a = [1, 2, 3, 4, 5]; +// array_reduce() calls zend_call_function() under the hood +var_dump(array_reduce($a, 'sum')); +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s%eobserver_zend_call_function_%d.php' --> +<file '%s%eobserver_zend_call_function_%d.php'> + <!-- init sum() --> + <sum> + </sum> + <sum> + </sum> + <sum> + </sum> + <sum> + </sum> + <sum> + </sum> +int(15) +Done +</file '%s%eobserver_zend_call_function_%d.php'> |