package.xml0000664000175000017500000003314414133502033011646 0ustar remiremi uopz pecl.php.net User Operations for Zend The uopz extension is focused on providing utilities to aid with unit testing PHP code. It supports the following activities: * Intercepting function execution * Intercepting object creation * Hooking into function execution * Manipulation of function statics * Manipulation of function flags * Redefinition of constants * Deletion of constants * Runtime creation of functions and methods Joe Watkins krakjoe pthreads@pthreads.org yes Remi Collet remi remi@php.net yes 2021-10-19 7.1.1 7.1.0 stable stable PHP License - fix PHP 8.1 compatibility 8.0.0 1.10 xdebug 2.9.3 uopz 2021-08-06 7.1.0 7.1.0 stable stable PHP License - uopz_implement and uopz_extend have been dropped - internal rewrite to improve stability and simplify 2021-07-28 7.0.0 6.0.0 stable stable PHP License - Fix #145 uopz_find_hook segfault with PHP 8.0 - Fix #140 segfault with PHPUnit 9.5 caused by uopz_del_function - Don't remove methods of immutable classes - PHP 8 support - Drop PHP 7 support 2020-06-30 6.1.2 6.0.0 stable stable PHP License - make UOPZ compatible with Xdebug again (2.9.4+ required) 2019-09-18 6.1.1 6.0.0 stable stable PHP License - Fix compatibility with 7.4.0RC1 - Fix gh#110 uopz_set_mock function does not work if xdebug is loaded 2019-05-27 6.1.0 6.0.0 stable stable PHP License - Fix #109 uopz_set_hook closure receive extra arguments when function call through call_user_func and call_user_func_array - 7.4 support 2019-02-06 6.0.1 6.0.0 stable stable PHP License - Add "uopz.exit" configuration option, to allow the execution of exit opcodes or not (default=0 to keep current behavior) - Improve opcache optimizer compatibility - Display ini settings in module info 2019-01-30 6.0.0 6.0.0 stable stable PHP License - Breaking change to uopz_set_mock: now behaves like the old test-helpers new overload - Fixed redefine/undefine namespaced constants - Fix flags being ignored when adding functions - Remove executor hook for maximum compatibility 2019-01-02 5.1.0 5.1.0 stable stable PHP License - Fix #89 uopz_flags bugs on 64bit windows - Fix #87 segfault when hook throws fatal error - Fix #86 interface support for uopz_set_return - Fix #85 cuf/cufa bugs - Fix #76 uopz_extend changing linkage - Fix #73 cuf/cufa bugs - Fix #68 hang when using anon class as mock - Fix #64 segfault after uopz_set_static - Fix #63 class constant redefinition depends on opcache - Fix #61 mocking not working with xdebug - Fix #51 hooks and returns bug - Fix #42 uopz_set_mock not working in some cases - Fix PHP 7.3 compatibility 2017-08-03 5.0.2 5.0.0 stable stable PHP License - add uopz.disable ini switch (default 0) - fix gh#43: setting hook on __invoke method doesn't work on call_user_func - fix gh#48: segmentation fault (uopz_set_return) - add 4 new functions: - uopz_call_user_func(callable function, ... args) - uopz_get_exit_status() - uopz_allow_exit(bool allow) - uopz_call_user_func_array(callable function, array args) - fix PHP 7.1 compatibility - fix PHP 7.2 compatibility 2016-04-13 5.0.1 5.0.0 stable stable PHP License - PHP 7 compatibility - new API, see documentation 2015-03-04 2.0.7 2.0.7 stable stable PHP License fix bug in overriding methods in classes loaded prior to override add ability to send object from ZEND_NEW handler send exit parameter to overload function fix bug in handling exit status code when overloading disabled set exit status code when overloading enabled from return value uopz-7.1.1/src/class.c0000664000175000017500000001331214133502033013223 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_CLASS #define UOPZ_CLASS #include "php.h" #include "uopz.h" #include "util.h" #include "class.h" ZEND_EXTERN_MODULE_GLOBALS(uopz); #define uopz_get_scope(e) ((e) ? zend_get_executed_scope() : EG(fake_scope)) #define uopz_set_scope(s) EG(fake_scope) = (s) void uopz_set_mock(zend_string *clazz, zval *mock) { /* {{{ */ zend_string *key = zend_string_tolower(clazz); if (zend_hash_update(&UOPZ(mocks), key, mock)) { zval_copy_ctor(mock); } zend_string_release(key); } /* }}} */ void uopz_unset_mock(zend_string *clazz) { /* {{{ */ zend_string *key = zend_string_tolower(clazz); if (!zend_hash_exists(&UOPZ(mocks), key)) { uopz_exception( "the class provided (%s) has no mock set", ZSTR_VAL(clazz)); zend_string_release(key); return; } zend_hash_del(&UOPZ(mocks), key); zend_string_release(key); } /* }}} */ int uopz_get_mock(zend_string *clazz, zval *return_value) { /* {{{ */ zval *mock = NULL; zend_string *key = zend_string_tolower(clazz); if (!(mock = zend_hash_find(&UOPZ(mocks), key))) { zend_string_release(key); return FAILURE; } ZVAL_COPY(return_value, mock); zend_string_release(key); return SUCCESS; } /* }}} */ int uopz_find_mock(zend_string *clazz, zend_object **object, zend_class_entry **mock) { /* {{{ */ zend_string *key = zend_string_tolower(clazz); zval *found = zend_hash_find(&UOPZ(mocks), key); zend_string_release(key); if (!found) { return FAILURE; } if (Z_TYPE_P(found) == IS_STRING) { *mock = zend_fetch_class_by_name(Z_STR_P(found), NULL, ZEND_FETCH_CLASS_EXCEPTION); return *mock ? SUCCESS : FAILURE; } else { *mock = Z_OBJCE_P(found); if (object) { *object = Z_OBJ_P(found); } return SUCCESS; } } /* }}} */ void uopz_set_property(zval *object, zend_string *member, zval *value) { /* {{{ */ zend_class_entry *scope = uopz_get_scope(0); zend_class_entry *ce = Z_OBJCE_P(object); zend_property_info *info; do { uopz_set_scope(ce); info = zend_get_property_info(ce, member, 1); if (info && info != ZEND_WRONG_PROPERTY_INFO) { break; } ce = ce->parent; } while (ce); if (info && info != ZEND_WRONG_PROPERTY_INFO) { uopz_set_scope(info->ce); } else { uopz_set_scope(Z_OBJCE_P(object)); } Z_OBJ_HT_P(object) ->write_property(Z_OBJ_P(object), member, value, NULL); uopz_set_scope(scope); } /* }}} */ void uopz_get_property(zval *object, zend_string *member, zval *value) { /* {{{ */ zend_class_entry *scope = uopz_get_scope(0); zend_class_entry *ce = Z_OBJCE_P(object); zend_property_info *info; zval *prop, rv; do { uopz_set_scope(ce); info = zend_get_property_info(ce, member, 1); if (info && info != ZEND_WRONG_PROPERTY_INFO) { break; } ce = ce->parent; } while (ce); if (info && info != ZEND_WRONG_PROPERTY_INFO) { uopz_set_scope(info->ce); } else { uopz_set_scope(Z_OBJCE_P(object)); } prop = Z_OBJ_HT_P(object)->read_property( Z_OBJ_P(object), member, BP_VAR_R, NULL, &rv); uopz_set_scope(scope); ZVAL_COPY(value, prop); } /* }}} */ void uopz_set_static_property(zend_class_entry *ce, zend_string *property, zval *value) { /* {{{ */ zend_class_entry *scope = uopz_get_scope(0); zend_class_entry *seek = ce; zend_property_info *info; zval *prop; do { uopz_set_scope(seek); info = zend_get_property_info(seek, property, 1); if (info && info != ZEND_WRONG_PROPERTY_INFO) { break; } seek = seek->parent; } while (seek); if (info && info != ZEND_WRONG_PROPERTY_INFO) { uopz_set_scope(info->ce); } else { uopz_set_scope(ce); } prop = zend_std_get_static_property(uopz_get_scope(0), property, 1); uopz_set_scope(scope); if (!prop) { uopz_exception( "cannot set non-existent static property %s::%s", ZSTR_VAL(ce->name), ZSTR_VAL(property)); return; } zval_ptr_dtor(prop); ZVAL_COPY(prop, value); } /* }}} */ void uopz_get_static_property(zend_class_entry *ce, zend_string *property, zval *value) { /* {{{ */ zend_class_entry *scope = uopz_get_scope(0); zend_class_entry *seek = ce; zend_property_info *info; zval *prop; do { uopz_set_scope(seek); info = zend_get_property_info(seek, property, 1); if (info && info != ZEND_WRONG_PROPERTY_INFO) { break; } seek = seek->parent; } while (seek); if (info && info != ZEND_WRONG_PROPERTY_INFO) { uopz_set_scope(info->ce); } else { uopz_set_scope(ce); } prop = zend_std_get_static_property(uopz_get_scope(0), property, 1); uopz_set_scope(scope); if (!prop) { return; } ZVAL_COPY(value, prop); } /* }}} */ #endif /* UOPZ_CLASS */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/class.h0000664000175000017500000000401314133502033013226 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_CLASS_H #define UOPZ_CLASS_H void uopz_set_mock(zend_string *clazz, zval *mock); void uopz_unset_mock(zend_string *clazz); zend_bool uopz_extend(zend_class_entry *clazz, zend_class_entry *parent); zend_bool uopz_implement(zend_class_entry *clazz, zend_class_entry *interface); int uopz_get_mock(zend_string *clazz, zval *return_value); int uopz_find_mock(zend_string *clazz, zend_object **object, zend_class_entry **mock); void uopz_set_property(zval *object, zend_string *member, zval *value); void uopz_get_property(zval *object, zend_string *member, zval *value); void uopz_set_static_property(zend_class_entry *ce, zend_string *property, zval *value); void uopz_get_static_property(zend_class_entry *ce, zend_string *property, zval *value); #endif /* UOPZ_CLASS_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/constant.c0000664000175000017500000001022114133502033013743 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_CONSTANT #define UOPZ_CONSTANT #include "php.h" #include "uopz.h" #include "util.h" #include "constant.h" /* {{{ */ zend_bool uopz_constant_redefine(zend_class_entry *clazz, zend_string *name, zval *variable) { HashTable *table = clazz ? &clazz->constants_table : EG(zend_constants); zend_string *key = zend_string_copy(name); zend_constant *zconstant = zend_hash_find_ptr(table, key); if (!zconstant && !clazz) { const char *ns = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name)); size_t nss; if (ns) { zend_string *heap = zend_string_tolower(key); ns++; nss = (ZSTR_VAL(name) + ZSTR_LEN(name)) - ns; memcpy(&ZSTR_VAL(heap)[ZSTR_LEN(heap) - nss], ns, nss); zconstant = zend_hash_find_ptr(table, heap); zend_string_release(key); key = heap; } } if (!zconstant) { if (!clazz) { zend_constant create; ZVAL_COPY(&create.value, variable); ZEND_CONSTANT_SET_FLAGS(&create, CONST_CS, PHP_USER_CONSTANT); create.name = zend_string_copy(key); zend_register_constant(&create); } else { zend_declare_class_constant(clazz, ZSTR_VAL(name), ZSTR_LEN(name), variable); Z_TRY_ADDREF_P(variable); } zend_string_release(key); return 1; } if (!clazz) { if (ZEND_CONSTANT_MODULE_NUMBER(zconstant) == PHP_USER_CONSTANT) { zval_dtor(&zconstant->value); ZVAL_COPY(&zconstant->value, variable); } else { uopz_exception( "failed to redefine the internal %s, not allowed", ZSTR_VAL(name)); zend_string_release(key); return 0; } } else { zend_hash_del(table, key); zend_declare_class_constant(clazz, ZSTR_VAL(name), ZSTR_LEN(name), variable); Z_TRY_ADDREF_P(variable); } zend_string_release(key); return 1; } /* }}} */ /* {{{ */ zend_bool uopz_constant_undefine(zend_class_entry *clazz, zend_string *name) { zend_constant *zconstant; HashTable *table = clazz ? &clazz->constants_table : EG(zend_constants); zend_string *heap = NULL; if (!(zconstant = zend_hash_find_ptr(table, name))) { if (!clazz) { const char *ns = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name)); size_t nss; if (ns) { heap = zend_string_tolower(name); ns++; nss = (ZSTR_VAL(name) + ZSTR_LEN(name)) - ns; memcpy(&ZSTR_VAL(heap)[ZSTR_LEN(heap) - nss], ns, nss); zconstant = zend_hash_find_ptr(table, heap); if (!zconstant) { zend_string_release(heap); return 0; } name = heap; goto _uopz_constant_undefine; } } return 0; } _uopz_constant_undefine: if (!clazz) { if (ZEND_CONSTANT_MODULE_NUMBER(zconstant) != PHP_USER_CONSTANT) { uopz_exception( "failed to undefine the internal constant %s, not allowed", ZSTR_VAL(name)); if (heap) { zend_string_release(heap); } return 0; } zend_hash_del(table, name); if (heap) { zend_string_release(heap); } return 1; } zend_hash_del(table, name); return 1; } /* }}} */ #endif /* UOPZ_CONSTANT */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/constant.h0000664000175000017500000000276114133502033013762 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_CONSTANT_H #define UOPZ_CONSTANT_H zend_bool uopz_constant_redefine(zend_class_entry *clazz, zend_string *name, zval *variable); zend_bool uopz_constant_undefine(zend_class_entry *clazz, zend_string *name); #endif /* UOPZ_CONSTANT_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/executors.c0000664000175000017500000000441714133502033014145 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_EXECUTORS #define UOPZ_EXECUTORS #include "php.h" #include "uopz.h" #include "executors.h" ZEND_EXTERN_MODULE_GLOBALS(uopz); typedef void (*zend_execute_internal_f) (zend_execute_data *, zval *); void php_uopz_execute_internal(zend_execute_data *execute_data, zval *return_value); zend_execute_internal_f zend_execute_internal_function; void php_uopz_execute_internal(zend_execute_data *execute_data, zval *return_value); void uopz_executors_init(void) { /* {{{ */ zend_execute_internal_function = zend_execute_internal; zend_execute_internal = php_uopz_execute_internal; } /* }}} */ void uopz_executors_shutdown(void) { /* {{{ */ zend_execute_internal = zend_execute_internal_function; } /* }}} */ void php_uopz_execute_internal(zend_execute_data *execute_data, zval *return_value) { /* {{{ LCOV_EXCL_START */ if (zend_execute_internal_function) { zend_execute_internal_function(execute_data, return_value); } else execute_internal(execute_data, return_value); } /* LCOV_EXCL_STOP }}} */ #endif /* UOPZ_HANDLERS_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/executors.h0000664000175000017500000000261414133502033014147 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_EXECUTORS_H #define UOPZ_EXECUTORS_H void uopz_executors_init(void); void uopz_executors_shutdown(void); #endif /* UOPZ_EXECUTORS_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/function.c0000664000175000017500000002550414133502033013751 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_FUNCTION #define UOPZ_FUNCTION #include "php.h" #include "uopz.h" #include "util.h" #include "function.h" #include ZEND_EXTERN_MODULE_GLOBALS(uopz); static zend_function* uopz_copy_function(zend_class_entry *scope, zend_string *name, zend_object *closure, zend_long flags) { /* {{{ */ zend_function *copy = (zend_function*) zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); memcpy(copy, zend_get_closure_method_def(closure), sizeof(zend_op_array)); copy->op_array.fn_flags &= ~ZEND_ACC_CLOSURE|ZEND_ACC_IMMUTABLE|ZEND_ACC_ARENA_ALLOCATED; copy->op_array.function_name = zend_string_copy(name); copy->op_array.scope = scope; if (flags & ZEND_ACC_PPP_MASK) { copy->op_array.fn_flags &= ~ZEND_ACC_PPP_MASK; copy->op_array.fn_flags |= flags & ZEND_ACC_PPP_MASK; } else { copy->op_array.fn_flags |= ZEND_ACC_PUBLIC; } if (flags & ZEND_ACC_STATIC) { copy->op_array.fn_flags |= ZEND_ACC_STATIC; } if (copy->op_array.static_variables) { copy->op_array.static_variables = zend_array_dup(copy->op_array.static_variables); #if PHP_VERSION_ID > 80100 ZEND_MAP_PTR_INIT( copy->op_array.static_variables_ptr, copy->op_array.static_variables); #else ZEND_MAP_PTR_INIT( copy->op_array.static_variables_ptr, ©->op_array.static_variables); #endif } else { ZEND_MAP_PTR_INIT(copy->op_array.static_variables_ptr, NULL); } if (copy->op_array.refcount) { (*copy->op_array.refcount)++; } copy->op_array.fn_flags |= ZEND_ACC_UOPZ; return copy; } /* }}} */ zend_bool uopz_add_function(zend_class_entry *clazz, zend_string *name, zval *closure, zend_long flags, zend_bool all) { /* {{{ */ HashTable *table = clazz ? &clazz->function_table : CG(function_table); zend_string *key; zend_function *function = NULL; if (clazz && clazz->ce_flags & ZEND_ACC_IMMUTABLE) { uopz_exception( "cannot add method %s::%s to immutable class, use uopz_set_return instead", ZSTR_VAL(clazz->name), ZSTR_VAL(name)); return 0; } key = zend_string_tolower(name); key = zend_new_interned_string(key); if (zend_hash_exists(table, key)) { if (clazz) { uopz_exception( "will not replace existing method %s::%s, use uopz_set_return instead", ZSTR_VAL(clazz->name), ZSTR_VAL(name)); } else { uopz_exception( "will not replace existing function %s, use uopz_set_return instead", ZSTR_VAL(name)); } zend_string_release(key); return 0; } function = uopz_copy_function(clazz, name, Z_OBJ_P(closure), flags); zend_hash_update_ptr(table, key, (void*) function); if (clazz) { if (all) { zend_class_entry *next; ZEND_HASH_FOREACH_PTR(CG(class_table), next) { if (next->parent == clazz) { if (zend_hash_exists(&next->function_table, key)) { continue; } uopz_add_function(next, name, closure, flags, all); } } ZEND_HASH_FOREACH_END(); } uopz_handle_magic(clazz, name, function); } zend_string_release(key); return 1; } /* }}} */ zend_bool uopz_del_function(zend_class_entry *clazz, zend_string *name, zend_bool all) { /* {{{ */ HashTable *table = clazz ? &clazz->function_table : CG(function_table); zend_string *key = zend_string_tolower(name); zend_function *function = zend_hash_find_ptr(table, key); if (!function) { if (clazz) { uopz_exception( "cannot delete method %s::%s, it does not exist", ZSTR_VAL(clazz->name), ZSTR_VAL(name)); } else { uopz_exception( "cannot delete function %s, it does not exist", ZSTR_VAL(name)); } zend_string_release(key); return 0; } if (!(function->common.fn_flags & ZEND_ACC_UOPZ)) { if (clazz) { uopz_exception( "cannot delete method %s::%s, it was not added by uopz", ZSTR_VAL(clazz->name), ZSTR_VAL(name)); } else { uopz_exception( "cannot delete function %s, it was not added by uopz", ZSTR_VAL(name)); } zend_string_release(key); return 0; } if (clazz && all) { zend_class_entry *next; ZEND_HASH_FOREACH_PTR(CG(class_table), next) { if (next->parent == clazz) { if (!zend_hash_exists(&next->function_table, key)) { continue; } uopz_del_function(next, name, all); } } ZEND_HASH_FOREACH_END(); } zend_hash_del(table, key); zend_string_release(key); return 1; } /* }}} */ /* {{{ */ void uopz_flags(zend_class_entry *clazz, zend_string *name, zend_long flags, zval *return_value) { HashTable *table = clazz ? &clazz->function_table : CG(function_table); zend_long current = 0; if (clazz && (!name || !ZSTR_LEN(name))) { if (flags == ZEND_LONG_MAX) { RETURN_LONG(clazz->ce_flags); } if (flags & ZEND_ACC_PPP_MASK) { uopz_exception( "attempt to set public, private or protected on class entry %s, not allowed", ZSTR_VAL(clazz->name)); return; } if (flags & ZEND_ACC_STATIC) { uopz_exception( "attempt to set static on class entry %s, not allowed", ZSTR_VAL(clazz->name)); return; } if (clazz->ce_flags & ZEND_ACC_IMMUTABLE) { uopz_exception( "attempt to set flags of immutable class entry %s, not allowed", ZSTR_VAL(clazz->name)); return; } current = clazz->ce_flags; clazz->ce_flags = flags; if (current & ZEND_ACC_LINKED) { clazz->ce_flags |= ZEND_ACC_LINKED; } RETURN_LONG(current); } zend_function *function = uopz_find_function(table, name); if (!function) { if (clazz) { uopz_exception( "failed to set or get flags of method %s::%s, it does not exist", ZSTR_VAL(clazz->name), ZSTR_VAL(name)); } else { uopz_exception( "failed to set or get flags of function %s, it does not exist", ZSTR_VAL(name)); } return; } if (flags == ZEND_LONG_MAX) { RETURN_LONG(function->common.fn_flags); } current = function->common.fn_flags; if (flags) { if (function->common.fn_flags & ZEND_ACC_IMMUTABLE) { uopz_exception( "attempt to set flags of immutable function entry %s, not allowed", ZSTR_VAL(name)); return; } /* Only allow changing a whitelist of flags, don't allow modifying internal flags. */ uint32_t allowed_flags = ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC | ZEND_ACC_FINAL; function->common.fn_flags = (function->common.fn_flags & ~allowed_flags) | (flags & allowed_flags); } RETURN_LONG(current); } /* }}} */ zend_bool uopz_set_static(zend_class_entry *clazz, zend_string *function, zval *statics) { /* {{{ */ zend_function *entry; zend_string *k = NULL; zval *v = NULL; if (clazz) { entry = uopz_find_function(&clazz->function_table, function); if (!entry) { uopz_exception( "failed to set statics in method %s::%s, it does not exist", ZSTR_VAL(clazz->name), ZSTR_VAL(function)); return 0; } } else { entry = uopz_find_function(CG(function_table), function); if (!entry) { uopz_exception( "failed to set statics in function %s, it does not exist", ZSTR_VAL(function)); return 0; } } if (entry->type != ZEND_USER_FUNCTION) { if (clazz) { uopz_exception( "failed to set statics in internal method %s::%s", ZSTR_VAL(clazz->name), ZSTR_VAL(function)); } else { uopz_exception( "failed to set statics in internal function %s", ZSTR_VAL(function)); } return 0; } if (!entry->op_array.static_variables) { if (clazz) { uopz_exception( "failed to set statics in method %s::%s, no statics declared", ZSTR_VAL(clazz->name), ZSTR_VAL(function)); } else { uopz_exception( "failed to set statics in function %s, no statics declared", ZSTR_VAL(function)); } return 0; } HashTable *variables = ZEND_MAP_PTR_GET(entry->op_array.static_variables_ptr); if (!variables) { variables = zend_array_dup(entry->op_array.static_variables); ZEND_MAP_PTR_SET(entry->op_array.static_variables_ptr, variables); } ZEND_HASH_FOREACH_STR_KEY_VAL(variables, k, v) { zval *y; if (Z_REFCOUNTED_P(v)) { zval_ptr_dtor(v); } if (!(y = zend_hash_find(Z_ARRVAL_P(statics), k))) { ZVAL_NULL(v); continue; } ZVAL_COPY(v, y); } ZEND_HASH_FOREACH_END(); return 1; } /* }}} */ zend_bool uopz_get_static(zend_class_entry *clazz, zend_string *function, zval *return_value) { /* {{{ */ zend_function *entry; if (clazz) { entry = uopz_find_function(&clazz->function_table, function); if (!entry) { uopz_exception( "failed to get statics from method %s::%s, it does not exist", ZSTR_VAL(clazz->name), ZSTR_VAL(function)); return 0; } } else { entry = uopz_find_function(CG(function_table), function); if (!entry) { uopz_exception( "failed to get statics from function %s, it does not exist", ZSTR_VAL(function)); return 0; } } if (entry->type != ZEND_USER_FUNCTION) { if (clazz) { uopz_exception( "failed to get statics from internal method %s::%s", ZSTR_VAL(clazz->name), ZSTR_VAL(function)); } else { uopz_exception( "failed to get statics from internal function %s", ZSTR_VAL(function)); } return 0; } if (!entry->op_array.static_variables) { if (clazz) { uopz_exception( "failed to set statics in method %s::%s, no statics declared", ZSTR_VAL(clazz->name), ZSTR_VAL(function)); } else { uopz_exception( "failed to set statics in function %s, no statics declared", ZSTR_VAL(function)); } return 0; } HashTable *variables = ZEND_MAP_PTR_GET(entry->op_array.static_variables_ptr); if (!variables) { variables = zend_array_dup(entry->op_array.static_variables); ZEND_MAP_PTR_SET(entry->op_array.static_variables_ptr, variables); } zval *val; ZEND_HASH_FOREACH_VAL(variables, val) { if (zval_update_constant_ex(val, entry->common.scope) != SUCCESS) { return false; } } ZEND_HASH_FOREACH_END(); ZVAL_ARR(return_value, zend_array_dup(variables)); return 1; } /* }}} */ #endif /* UOPZ_FUNCTION */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/function.h0000664000175000017500000000352014133502033013750 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_FUNCTION_H #define UOPZ_FUNCTION_H #define ZEND_ACC_UOPZ (1<<30) zend_bool uopz_add_function(zend_class_entry *clazz, zend_string *name, zval *closure, zend_long flags, zend_bool all); zend_bool uopz_del_function(zend_class_entry *clazz, zend_string *name, zend_bool all); void uopz_flags(zend_class_entry *clazz, zend_string *name, zend_long flags, zval *return_value); zend_bool uopz_set_static(zend_class_entry *clazz, zend_string *function, zval *statics); zend_bool uopz_get_static(zend_class_entry *clazz, zend_string *function, zval *return_value); #endif /* UOPZ_FUNCTION_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/handlers.c0000664000175000017500000003376714133502033013736 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_HANDLERS #define UOPZ_HANDLERS #include "php.h" #include "uopz.h" #include "class.h" #include "return.h" #include "hook.h" #include "util.h" ZEND_EXTERN_MODULE_GLOBALS(uopz); #define UOPZ_HANDLERS_COUNT 12 #ifdef ZEND_VM_FP_GLOBAL_REG # define UOPZ_OPCODE_HANDLER_ARGS # define UOPZ_OPCODE_HANDLER_ARGS_PASSTHRU # define UOPZ_OPCODE_HANDLER_ARGS_CC # define UOPZ_OPCODE_HANDLER_ARGS_DC #else # define UOPZ_OPCODE_HANDLER_ARGS zend_execute_data *execute_data # define UOPZ_OPCODE_HANDLER_ARGS_DC , zend_execute_data *execute_data # define UOPZ_OPCODE_HANDLER_ARGS_PASSTHRU execute_data # define UOPZ_OPCODE_HANDLER_ARGS_CC , execute_data #endif #ifdef ZEND_VM_IP_GLOBAL_REG # define UOPZ_OPLINE opline # define UOPZ_USE_OPLINE # define UOPZ_LOAD_OPLINE() opline = EX(opline) # define UOPZ_LOAD_NEXT_OPLINE() opline = EX(opline) + 1 # define UOPZ_SAVE_OPLINE() EX(opline) = opline #else # define UOPZ_OPLINE EX(opline) # define UOPZ_USE_OPLINE const zend_op *opline = EX(opline) # define UOPZ_LOAD_OPLINE() # define UOPZ_LOAD_NEXT_OPLINE() UOPZ_OPLINE++ # define UOPZ_SAVE_OPLINE() #endif #define UOPZ_HANDLE_EXCEPTION() UOPZ_LOAD_OPLINE() return ZEND_USER_OPCODE_CONTINUE #define UOPZ_VM_DISPATCH() return _uopz_vm_dispatch(UOPZ_OPCODE_HANDLER_ARGS_PASSTHRU) #define UOPZ_VM_RETURN() return ZEND_USER_OPCODE_RETURN #define UOPZ_VM_CONTINUE() return ZEND_USER_OPCODE_CONTINUE #define UOPZ_VM_NEXT(e, n) do { \ if (e) { \ UOPZ_OPLINE = EX(opline) + (n); \ } else { \ UOPZ_OPLINE = opline + (n); \ } \ \ UOPZ_VM_CONTINUE(); \ } while(0) #define UOPZ_VM_LEAVE() return ZEND_USER_OPCODE_LEAVE #define RETURN_VALUE_USED(opline) ((opline)->result_type != IS_UNUSED) #define EX_CONSTANT(e) RT_CONSTANT(EX(opline), e) #define UOPZ_HANDLERS_DECL_BEGIN() uopz_vm_handler_t uopz_vm_handlers[UOPZ_HANDLERS_COUNT] = { #define UOPZ_HANDLER_DECL(o, n) {o, &zend_vm_##n, uopz_vm_##n}, #define UOPZ_HANDLERS_DECL_END() {0}}; #define UOPZ_HANDLER_OVERLOAD(h) do { \ *(h)->zend = zend_get_user_opcode_handler((h)->opcode); \ zend_set_user_opcode_handler((h)->opcode, (h)->uopz); \ } while (0) #define UOPZ_HANDLER_RESTORE(h) do { \ zend_set_user_opcode_handler((h)->opcode, *(h)->zend); \ } while (0) typedef int (*zend_vm_handler_t) (UOPZ_OPCODE_HANDLER_ARGS); typedef struct _uopz_vm_handler_t { zend_uchar opcode; zend_vm_handler_t *zend; zend_vm_handler_t uopz; } uopz_vm_handler_t; zend_vm_handler_t zend_vm_exit; zend_vm_handler_t zend_vm_new; zend_vm_handler_t zend_vm_fetch_constant; zend_vm_handler_t zend_vm_do_fcall; zend_vm_handler_t zend_vm_do_ucall; zend_vm_handler_t zend_vm_fetch_class; zend_vm_handler_t zend_vm_fetch_class_constant; zend_vm_handler_t zend_vm_init_fcall; zend_vm_handler_t zend_vm_init_fcall_by_name; zend_vm_handler_t zend_vm_init_ns_fcall_by_name; zend_vm_handler_t zend_vm_init_method_call; zend_vm_handler_t zend_vm_init_static_method_call; int uopz_vm_exit(UOPZ_OPCODE_HANDLER_ARGS); int uopz_vm_new(UOPZ_OPCODE_HANDLER_ARGS); int uopz_vm_fetch_constant(UOPZ_OPCODE_HANDLER_ARGS); int uopz_vm_do_fcall(UOPZ_OPCODE_HANDLER_ARGS); int uopz_vm_do_ucall(UOPZ_OPCODE_HANDLER_ARGS); int uopz_vm_fetch_class_constant(UOPZ_OPCODE_HANDLER_ARGS); int uopz_vm_init_fcall(UOPZ_OPCODE_HANDLER_ARGS); int uopz_vm_init_fcall_by_name(UOPZ_OPCODE_HANDLER_ARGS); int uopz_vm_init_ns_fcall_by_name(UOPZ_OPCODE_HANDLER_ARGS); int uopz_vm_init_method_call(UOPZ_OPCODE_HANDLER_ARGS); int uopz_vm_init_static_method_call(UOPZ_OPCODE_HANDLER_ARGS); UOPZ_HANDLERS_DECL_BEGIN() UOPZ_HANDLER_DECL(ZEND_EXIT, exit) UOPZ_HANDLER_DECL(ZEND_NEW, new) UOPZ_HANDLER_DECL(ZEND_FETCH_CONSTANT, fetch_constant) UOPZ_HANDLER_DECL(ZEND_FETCH_CLASS_CONSTANT, fetch_class_constant) UOPZ_HANDLER_DECL(ZEND_DO_FCALL, do_fcall) UOPZ_HANDLER_DECL(ZEND_DO_UCALL, do_ucall) UOPZ_HANDLER_DECL(ZEND_INIT_FCALL, init_fcall) UOPZ_HANDLER_DECL(ZEND_INIT_FCALL_BY_NAME, init_fcall_by_name) UOPZ_HANDLER_DECL(ZEND_INIT_NS_FCALL_BY_NAME, init_ns_fcall_by_name) UOPZ_HANDLER_DECL(ZEND_INIT_METHOD_CALL, init_method_call) UOPZ_HANDLER_DECL(ZEND_INIT_STATIC_METHOD_CALL, init_static_method_call) UOPZ_HANDLERS_DECL_END() static zend_always_inline zval* uopz_get_zval(const zend_op *opline, int op_type, const znode_op *node, const zend_execute_data *execute_data) { return zend_get_zval_ptr(opline, op_type, node, execute_data); } void uopz_handlers_init(void) { uopz_vm_handler_t *handler = uopz_vm_handlers; while (handler) { if (!handler->opcode) { break; } UOPZ_HANDLER_OVERLOAD(handler); handler++; } } void uopz_handlers_shutdown(void) { uopz_vm_handler_t *handler = uopz_vm_handlers; while (handler) { if (!handler->opcode) { break; } UOPZ_HANDLER_RESTORE(handler); handler++; } } static zend_always_inline int _uopz_vm_dispatch(UOPZ_OPCODE_HANDLER_ARGS) { zend_vm_handler_t zend = NULL; switch (EX(opline)->opcode) { case ZEND_EXIT: zend = zend_vm_exit; break; case ZEND_NEW: zend = zend_vm_new; break; case ZEND_INIT_FCALL_BY_NAME: zend = zend_vm_init_fcall_by_name; break; case ZEND_INIT_FCALL: zend = zend_vm_init_fcall; break; case ZEND_INIT_NS_FCALL_BY_NAME: zend = zend_vm_init_ns_fcall_by_name; break; case ZEND_INIT_METHOD_CALL: zend = zend_vm_init_method_call; break; case ZEND_INIT_STATIC_METHOD_CALL: zend = zend_vm_init_static_method_call; break; case ZEND_FETCH_CONSTANT: zend = zend_vm_fetch_constant; break; case ZEND_FETCH_CLASS_CONSTANT: zend = zend_vm_fetch_class_constant; break; case ZEND_DO_FCALL: zend = zend_vm_do_fcall; break; case ZEND_DO_UCALL: zend = zend_vm_do_ucall; break; } if (zend) { return zend(UOPZ_OPCODE_HANDLER_ARGS_PASSTHRU); } return ZEND_USER_OPCODE_DISPATCH; } int uopz_vm_exit(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ UOPZ_USE_OPLINE; zval *estatus; if (UOPZ(exit)) { UOPZ_VM_DISPATCH(); } if (opline->op1_type != IS_UNUSED) { estatus = uopz_get_zval( opline, opline->op1_type, &opline->op1, execute_data); do { if (Z_TYPE_P(estatus) == IS_LONG) { EG(exit_status) = Z_LVAL_P(estatus); } else { if (opline->op1_type & (IS_VAR|IS_CV) && Z_ISREF_P(estatus)) { estatus = Z_REFVAL_P(estatus); if (Z_TYPE_P(estatus) == IS_LONG) { EG(exit_status) = Z_LVAL_P(estatus); break; } } } } while (0); ZVAL_COPY(&UOPZ(estatus), estatus); } if (opline < &EX(func)->op_array.opcodes[EX(func)->op_array.last - 1]) { UOPZ_LOAD_NEXT_OPLINE(); while (opline->opcode == ZEND_EXT_STMT) { UOPZ_LOAD_NEXT_OPLINE(); } UOPZ_VM_CONTINUE(); } else { UOPZ_VM_RETURN(); } } /* }}} */ int uopz_vm_new(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ UOPZ_USE_OPLINE; zval *result; zend_function *constructor; zend_class_entry *ce; zend_execute_data *call; zend_object *obj = NULL; UOPZ_SAVE_OPLINE(); if (opline->op1_type == IS_CONST) { if (uopz_find_mock(Z_STR_P(EX_CONSTANT(opline->op1)), &obj, &ce) != SUCCESS) { if (!EG(exception)) { ce = zend_fetch_class_by_name( Z_STR_P(EX_CONSTANT(opline->op1)), Z_STR_P(EX_CONSTANT(opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); } } } else if (opline->op1_type == IS_UNUSED) { ce = zend_fetch_class(NULL, opline->op1.num); if (!ce) { ZVAL_UNDEF(EX_VAR(opline->result.var)); UOPZ_VM_DISPATCH(); } uopz_find_mock(ce->name, &obj, &ce); } else { ce = Z_CE_P( EX_VAR(opline->op1.var)); uopz_find_mock(ce->name, &obj, &ce); } if (ce == NULL) { ZVAL_UNDEF(EX_VAR(opline->result.var)); UOPZ_VM_DISPATCH(); } if (obj != NULL) { ZVAL_OBJ( EX_VAR(opline->result.var), obj); Z_ADDREF_P(EX_VAR(opline->result.var)); if (opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL) { UOPZ_VM_NEXT(0, 2); } call = zend_vm_stack_push_call_frame( ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, opline->extended_value, NULL ); call->prev_execute_data = EX(call); EX(call) = call; UOPZ_VM_NEXT(0, 1); } result = EX_VAR(opline->result.var); if (object_init_ex(result, ce) != SUCCESS) { ZVAL_UNDEF(result); UOPZ_HANDLE_EXCEPTION(); } constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); if (!constructor) { if (EG(exception)) { UOPZ_HANDLE_EXCEPTION(); } if (opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL) { UOPZ_VM_NEXT(0, 2); } call = zend_vm_stack_push_call_frame( ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, opline->extended_value, NULL); } else { if (constructor->type == ZEND_USER_FUNCTION && !RUN_TIME_CACHE(&constructor->op_array)) { void **run_time_cache = zend_arena_alloc(&CG(arena), constructor->op_array.cache_size); memset(run_time_cache, 0, constructor->op_array.cache_size); ZEND_MAP_PTR_SET(constructor->op_array.run_time_cache, run_time_cache); } call = zend_vm_stack_push_call_frame( ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_THIS, constructor, opline->extended_value, Z_OBJ_P(result) ); Z_ADDREF_P(result); } call->prev_execute_data = EX(call); EX(call) = call; UOPZ_VM_NEXT(0, 1); } /* }}} */ static zend_always_inline bool uopz_run_hook(zend_function *function, zend_execute_data *execute_data) { /* {{{ */ uopz_hook_t *uhook = uopz_find_hook(function); if (uhook && !uhook->busy) { uopz_execute_hook(uhook, execute_data, 0, 0); if (UNEXPECTED(EG(exception))) { return false; } } return true; } /* }}} */ /* {{{ */ static zend_always_inline int php_uopz_leave_helper(zend_execute_data *execute_data) { zend_execute_data *call = EX(call); if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { OBJ_RELEASE(Z_OBJ(call->This)); } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); } EX(call) = call->prev_execute_data; EX(opline) = EX(opline) + 1; zend_vm_stack_free_args(call); zend_vm_stack_free_call_frame(call); UOPZ_VM_LEAVE(); } /* }}} */ int uopz_vm_do_call_common(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ zend_execute_data *call = EX(call); if (call) { const zend_op *opline = EX(opline); if (!uopz_run_hook(call->func, call)) { if (RETURN_VALUE_USED(opline)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); } return php_uopz_leave_helper(UOPZ_OPCODE_HANDLER_ARGS_PASSTHRU); } uopz_return_t *ureturn = uopz_find_return(call->func); if (ureturn) { zval rv, *return_value = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &rv; if (UOPZ_RETURN_IS_EXECUTABLE(ureturn)) { if (UOPZ_RETURN_IS_BUSY(ureturn)) { goto _uopz_vm_do_fcall_dispatch; } uopz_execute_return(ureturn, call, return_value); if (!RETURN_VALUE_USED(opline)) { zval_ptr_dtor(&rv); } return php_uopz_leave_helper(UOPZ_OPCODE_HANDLER_ARGS_PASSTHRU); } if (RETURN_VALUE_USED(opline)) { ZVAL_COPY(return_value, &ureturn->value); } return php_uopz_leave_helper(UOPZ_OPCODE_HANDLER_ARGS_PASSTHRU); } } _uopz_vm_do_fcall_dispatch: UOPZ_VM_DISPATCH(); } /* }}} */ int uopz_vm_do_ucall(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ return uopz_vm_do_call_common(UOPZ_OPCODE_HANDLER_ARGS_PASSTHRU); } /* }}} */ int uopz_vm_do_fcall(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ return uopz_vm_do_call_common(UOPZ_OPCODE_HANDLER_ARGS_PASSTHRU); } /* }}} */ int uopz_vm_call_common(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ CACHE_PTR(EX(opline)->result.num, NULL); UOPZ_VM_DISPATCH(); } /* }}} */ int uopz_vm_init_fcall(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ return uopz_vm_call_common(UOPZ_OPCODE_HANDLER_ARGS_PASSTHRU); } /* }}} */ int uopz_vm_init_fcall_by_name(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ return uopz_vm_call_common(UOPZ_OPCODE_HANDLER_ARGS_PASSTHRU); } /* }}} */ int uopz_vm_init_ns_fcall_by_name(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ return uopz_vm_call_common(UOPZ_OPCODE_HANDLER_ARGS_PASSTHRU); } /* }}} */ int uopz_vm_init_method_call(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ if (EX(opline)->op2_type == IS_CONST) { CACHE_PTR(EX(opline)->result.num, NULL); CACHE_PTR(EX(opline)->result.num + sizeof(void*), NULL); } UOPZ_VM_DISPATCH(); } /* }}} */ int uopz_vm_init_static_method_call(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ if (EX(opline)->op2_type == IS_CONST) { if (EX(opline)->op1_type == IS_CONST) { CACHE_PTR(EX(opline)->result.num + sizeof(void*), NULL); } else { CACHE_PTR(EX(opline)->result.num, NULL); CACHE_PTR(EX(opline)->result.num + sizeof(void*), NULL); } } UOPZ_VM_DISPATCH(); } /* }}} */ int uopz_vm_fetch_constant(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ CACHE_PTR(EX(opline)->extended_value, NULL); UOPZ_VM_DISPATCH(); } /* }}} */ int uopz_vm_fetch_class_constant(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ CACHE_PTR(EX(opline)->extended_value + sizeof(void*), NULL); if (EX(opline)->op1_type != IS_CONST) { CACHE_PTR(EX(opline)->extended_value, NULL); } UOPZ_VM_DISPATCH(); } /* }}} */ #endif /* UOPZ_HANDLERS_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/handlers.h0000664000175000017500000000260714133502033013730 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_HANDLERS_H #define UOPZ_HANDLERS_H void uopz_handlers_init(void); void uopz_handlers_shutdown(void); #endif /* UOPZ_HANDLERS_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/hook.c0000664000175000017500000001347614133502033013071 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_HOOK #define UOPZ_HOOK #include "php.h" #include "uopz.h" #include "util.h" #include "hook.h" #include ZEND_EXTERN_MODULE_GLOBALS(uopz); zend_bool uopz_set_hook(zend_class_entry *clazz, zend_string *name, zval *closure) { /* {{{ */ HashTable *hooks; uopz_hook_t hook; zend_string *key = zend_string_tolower(name); if (clazz) { zend_function *function = uopz_find_method(clazz, key); if (!function) { uopz_exception( "failed to set hook for %s::%s, the method does not exist", ZSTR_VAL(clazz->name), ZSTR_VAL(name)); zend_string_release(key); return 0; } if (function->common.scope != clazz) { uopz_exception( "failed to set hook for %s::%s, the method is defined in %s", ZSTR_VAL(clazz->name), ZSTR_VAL(name), ZSTR_VAL(function->common.scope->name)); zend_string_release(key); return 0; } } if (clazz) { hooks = zend_hash_find_ptr(&UOPZ(hooks), clazz->name); } else hooks = zend_hash_index_find_ptr(&UOPZ(hooks), 0); if (!hooks) { ALLOC_HASHTABLE(hooks); zend_hash_init(hooks, 8, NULL, uopz_hook_free, 0); if (clazz) { zend_hash_update_ptr(&UOPZ(hooks), clazz->name, hooks); } else zend_hash_index_update_ptr(&UOPZ(hooks), 0, hooks); } memset(&hook, 0, sizeof(uopz_hook_t)); hook.clazz = clazz; hook.function = zend_string_copy(name); ZVAL_COPY(&hook.closure, closure); zend_hash_update_mem( hooks, key, &hook, sizeof(uopz_hook_t)); zend_string_release(key); return 1; } /* }}} */ zend_bool uopz_unset_hook(zend_class_entry *clazz, zend_string *function) { /* {{{ */ HashTable *hooks; zend_string *key = zend_string_tolower(function); if (clazz) { hooks = zend_hash_find_ptr(&UOPZ(hooks), clazz->name); } else hooks = zend_hash_index_find_ptr(&UOPZ(hooks), 0); if (!hooks || !zend_hash_exists(hooks, key)) { zend_string_release(key); return 0; } zend_hash_del(hooks, key); zend_string_release(key); return 1; } /* }}} */ void uopz_get_hook(zend_class_entry *clazz, zend_string *function, zval *return_value) { /* {{{ */ HashTable *hooks; uopz_hook_t *uhook; zend_string *key = zend_string_tolower(function); if (clazz) { hooks = zend_hash_find_ptr(&UOPZ(hooks), clazz->name); } else hooks = zend_hash_index_find_ptr(&UOPZ(hooks), 0); if (!hooks || !zend_hash_exists(hooks, key)) { zend_string_release(key); return; } uhook = zend_hash_find_ptr(hooks, key); ZVAL_COPY(return_value, &uhook->closure); zend_string_release(key); } /* }}} */ uopz_hook_t* uopz_find_hook(zend_function *function) { /* {{{ */ zend_string *key; uopz_hook_t *uhook; HashTable *hooks; if ((function == NULL) || (function->common.function_name == NULL)) { return NULL; } if (EG(flags) & EG_FLAGS_IN_SHUTDOWN) { return NULL; } if (function->common.scope) { hooks = zend_hash_find_ptr(&UOPZ(hooks), function->common.scope->name); } else { hooks = zend_hash_index_find_ptr(&UOPZ(hooks), 0); } if (!hooks) { if (function->common.prototype && function->common.prototype->common.scope && function->common.prototype->common.scope->ce_flags & ZEND_ACC_INTERFACE) { return uopz_find_hook( function->common.prototype); } return NULL; } key = zend_string_tolower(function->common.function_name); uhook = zend_hash_find_ptr(hooks, key); zend_string_release(key); return uhook; } /* }}} */ void uopz_execute_hook(uopz_hook_t *uhook, zend_execute_data *execute_data, zend_bool skip, zend_bool variadic) { /* {{{ */ zend_fcall_info fci; zend_fcall_info_cache fcc; char *error = NULL; zval closure, rv; ZVAL_UNDEF(&rv); uhook->busy = 1; zend_create_closure(&closure, (zend_function*) zend_get_closure_method_def(Z_OBJ(uhook->closure)), uhook->clazz, uhook->clazz, Z_OBJ(EX(This)) ? &EX(This) : NULL); zend_fcall_info_init(&closure, 0, &fci, &fcc, NULL, &error); if (!skip) { fci.param_count = ZEND_CALL_NUM_ARGS(execute_data); fci.params = ZEND_CALL_ARG(execute_data, 1); } else { if (variadic) { zend_fcall_info_args_ex( &fci, fcc.function_handler, ZEND_CALL_ARG(execute_data, 2)); } else { fci.param_count = ZEND_CALL_NUM_ARGS(execute_data) - 1; fci.params = ZEND_CALL_ARG(execute_data, 2); } } fci.retval= &rv; if (zend_call_function(&fci, &fcc) == SUCCESS) { if (!Z_ISUNDEF(rv)) { zval_ptr_dtor(&rv); } } if (variadic) { zend_fcall_info_args_clear(&fci, 1); } zval_ptr_dtor(&closure); uhook->busy = 0; } /* }}} */ void uopz_hook_free(zval *zv) { /* {{{ */ uopz_hook_t *uhook = Z_PTR_P(zv); zend_string_release(uhook->function); zval_ptr_dtor(&uhook->closure); efree(uhook); } /* }}} */ #endif /* UOPZ_HOOK */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/hook.h0000664000175000017500000000356714133502033013076 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_HOOK_H #define UOPZ_HOOK_H typedef struct _uopz_hook_t { zval closure; zend_class_entry *clazz; zend_string *function; zend_bool busy; } uopz_hook_t; zend_bool uopz_set_hook(zend_class_entry *clazz, zend_string *name, zval *closure); zend_bool uopz_unset_hook(zend_class_entry *clazz, zend_string *function); void uopz_get_hook(zend_class_entry *clazz, zend_string *function, zval *return_value); uopz_hook_t* uopz_find_hook(zend_function *function); void uopz_execute_hook(uopz_hook_t *uhook, zend_execute_data *execute_data, zend_bool skip, zend_bool variadic); void uopz_hook_free(zval *zv); #endif /* UOPZ_HOOK_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/return.c0000664000175000017500000001415214133502033013440 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_RETURN #define UOPZ_RETURN #include "php.h" #include "uopz.h" #include "util.h" #include "return.h" #include ZEND_EXTERN_MODULE_GLOBALS(uopz); zend_bool uopz_set_return(zend_class_entry *clazz, zend_string *name, zval *value, zend_bool execute) { /* {{{ */ HashTable *returns; uopz_return_t ret; zend_string *key = zend_string_tolower(name); if (clazz) { zend_function *function = uopz_find_method(clazz, key); if (!function) { uopz_exception( "failed to set return for %s::%s, the method does not exist", ZSTR_VAL(clazz->name), ZSTR_VAL(name)); zend_string_release(key); return 0; } if (function->common.scope != clazz) { uopz_exception( "failed to set return for %s::%s, the method is defined in %s", ZSTR_VAL(clazz->name), ZSTR_VAL(name), ZSTR_VAL(function->common.scope->name)); zend_string_release(key); return 0; } } if (clazz) { returns = zend_hash_find_ptr(&UOPZ(returns), clazz->name); } else returns = zend_hash_index_find_ptr(&UOPZ(returns), 0); if (!returns) { ALLOC_HASHTABLE(returns); zend_hash_init(returns, 8, NULL, uopz_return_free, 0); if (clazz) { zend_hash_update_ptr(&UOPZ(returns), clazz->name, returns); } else zend_hash_index_update_ptr(&UOPZ(returns), 0, returns); } memset(&ret, 0, sizeof(uopz_return_t)); ret.clazz = clazz; ret.function = zend_string_copy(name); ZVAL_COPY(&ret.value, value); ret.flags = execute ? UOPZ_RETURN_EXECUTE : 0; zend_hash_update_mem(returns, key, &ret, sizeof(uopz_return_t)); zend_string_release(key); return 1; } /* }}} */ zend_bool uopz_unset_return(zend_class_entry *clazz, zend_string *function) { /* {{{ */ HashTable *returns; zend_string *key = zend_string_tolower(function); if (clazz) { returns = zend_hash_find_ptr(&UOPZ(returns), clazz->name); } else returns = zend_hash_index_find_ptr(&UOPZ(returns), 0); if (!returns || !zend_hash_exists(returns, key)) { zend_string_release(key); return 0; } zend_hash_del(returns, key); zend_string_release(key); return 1; } /* }}} */ void uopz_get_return(zend_class_entry *clazz, zend_string *function, zval *return_value) { /* {{{ */ HashTable *returns; uopz_return_t *ureturn; if (clazz) { returns = zend_hash_find_ptr(&UOPZ(returns), clazz->name); } else returns = zend_hash_index_find_ptr(&UOPZ(returns), 0); if (!returns) { return; } ureturn = zend_hash_find_ptr(returns, function); if (!ureturn) { return; } ZVAL_COPY(return_value, &ureturn->value); } /* }}} */ uopz_return_t* uopz_find_return(zend_function *function) { /* {{{ */ zend_string *key; uopz_return_t *ureturn; HashTable *returns; if ((function == NULL) || (function->common.function_name == NULL) || (function->common.fn_flags & ZEND_ACC_CLOSURE)) { return NULL; } if (EG(flags) & EG_FLAGS_IN_SHUTDOWN) { return NULL; } if (function->common.scope) { returns = zend_hash_find_ptr(&UOPZ(returns), function->common.scope->name); } else { returns = zend_hash_index_find_ptr(&UOPZ(returns), 0); } if (!returns) { if (function->common.prototype && function->common.prototype->common.scope && function->common.prototype->common.scope->ce_flags & ZEND_ACC_INTERFACE) { return uopz_find_return( function->common.prototype); } return NULL; } key = zend_string_tolower(function->common.function_name); ureturn = zend_hash_find_ptr(returns, key); zend_string_release(key); return ureturn; } /* }}} */ void uopz_execute_return(uopz_return_t *ureturn, zend_execute_data *execute_data, zval *return_value) { /* {{{ */ zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fcc = empty_fcall_info_cache; char *error = NULL; zval closure, rv, *result = return_value ? return_value : &rv; ZVAL_UNDEF(&rv); ureturn->flags ^= UOPZ_RETURN_BUSY; zend_create_closure(&closure, (zend_function*) zend_get_closure_method_def(Z_OBJ(ureturn->value)), ureturn->clazz, ureturn->clazz, Z_OBJ(EX(This)) ? &EX(This) : NULL); zend_fcall_info_init(&closure, 0, &fci, &fcc, NULL, &error); if (uopz_is_cuf(execute_data)) { fci.params = ZEND_CALL_ARG(execute_data, 2); fci.param_count = ZEND_CALL_NUM_ARGS(execute_data) - 1; } else if (uopz_is_cufa(execute_data)) { zend_fcall_info_args(&fci, ZEND_CALL_ARG(execute_data, 2)); } else { fci.params = ZEND_CALL_ARG(execute_data, 1); fci.param_count = ZEND_CALL_NUM_ARGS(execute_data); } fci.retval = result; if (zend_call_function(&fci, &fcc) == SUCCESS) { if (!return_value) { if (!Z_ISUNDEF(rv)) { zval_ptr_dtor(&rv); } } } zval_ptr_dtor(&closure); if (uopz_is_cufa(execute_data)) { zend_fcall_info_args_clear(&fci, 1); } ureturn->flags ^= UOPZ_RETURN_BUSY; } /* }}} */ void uopz_return_free(zval *zv) { /* {{{ */ uopz_return_t *ureturn = Z_PTR_P(zv); zend_string_release(ureturn->function); zval_ptr_dtor(&ureturn->value); efree(ureturn); } /* }}} */ #endif /* UOPZ_RETURN */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/return.h0000664000175000017500000000423014133502033013441 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_RETURN_H #define UOPZ_RETURN_H typedef struct _uopz_return_t { zval value; zend_uchar flags; zend_class_entry *clazz; zend_string *function; } uopz_return_t; #define UOPZ_RETURN_EXECUTE 0x00000001 #define UOPZ_RETURN_BUSY 0x00000010 #define UOPZ_RETURN_IS_EXECUTABLE(u) (((u)->flags & UOPZ_RETURN_EXECUTE) == UOPZ_RETURN_EXECUTE) #define UOPZ_RETURN_IS_BUSY(u) (((u)->flags & UOPZ_RETURN_BUSY) == UOPZ_RETURN_BUSY) zend_bool uopz_set_return(zend_class_entry *clazz, zend_string *name, zval *value, zend_bool execute); zend_bool uopz_unset_return(zend_class_entry *clazz, zend_string *function); void uopz_get_return(zend_class_entry *clazz, zend_string *function, zval *return_value); uopz_return_t* uopz_find_return(zend_function *function); void uopz_execute_return(uopz_return_t *ureturn, zend_execute_data *execute_data, zval *return_value); void uopz_return_free(zval *zv); #endif /* UOPZ_RETURN_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/util.c0000664000175000017500000002262514133502033013102 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_UTIL #define UOPZ_UTIL #include "php.h" #include "uopz.h" #include "class.h" #include "function.h" #include "hook.h" #include "return.h" #include "util.h" #include ZEND_EXTERN_MODULE_GLOBALS(uopz); static zend_internal_function *zend_call_user_func_ptr; static zend_internal_function *zend_call_user_func_array_ptr; static zend_internal_function *uopz_call_user_func_ptr; static zend_internal_function *uopz_call_user_func_array_ptr; static inline void uopz_table_dtor(zval *zv) { /* {{{ */ zend_hash_destroy(Z_PTR_P(zv)); efree(Z_PTR_P(zv)); } /* }}} */ /* {{{ */ typedef struct _uopz_magic_t { const char *name; size_t length; int id; } uopz_magic_t; #define UOPZ_MAGIC(name, id) {name, sizeof(name)-1, id} #define UOPZ_MAGIC_END {NULL, 0, 0L} static const uopz_magic_t umagic[] = { UOPZ_MAGIC(ZEND_CONSTRUCTOR_FUNC_NAME, 0), UOPZ_MAGIC(ZEND_DESTRUCTOR_FUNC_NAME, 1), UOPZ_MAGIC(ZEND_CLONE_FUNC_NAME, 2), UOPZ_MAGIC(ZEND_GET_FUNC_NAME, 3), UOPZ_MAGIC(ZEND_SET_FUNC_NAME, 4), UOPZ_MAGIC(ZEND_UNSET_FUNC_NAME, 5), UOPZ_MAGIC(ZEND_ISSET_FUNC_NAME, 6), UOPZ_MAGIC(ZEND_CALL_FUNC_NAME, 7), UOPZ_MAGIC(ZEND_CALLSTATIC_FUNC_NAME, 8), UOPZ_MAGIC(ZEND_TOSTRING_FUNC_NAME, 9), UOPZ_MAGIC("__serialize", 10), UOPZ_MAGIC("__unserialize", 11), UOPZ_MAGIC(ZEND_DEBUGINFO_FUNC_NAME, 12), UOPZ_MAGIC_END }; void uopz_handle_magic(zend_class_entry *clazz, zend_string *name, zend_function *function) { /* {{{ */ uopz_magic_t *magic; for (magic = (uopz_magic_t*) umagic; magic->name; magic++) { if (ZSTR_LEN(name) == magic->length && strncasecmp(ZSTR_VAL(name), magic->name, magic->length) == SUCCESS) { switch (magic->id) { case 0: clazz->constructor = function; break; case 1: clazz->destructor = function; break; case 2: clazz->clone = function; break; case 3: clazz->__get = function; break; case 4: clazz->__set = function; break; case 5: clazz->__unset = function; break; case 6: clazz->__isset = function; break; case 7: clazz->__call = function; break; case 8: clazz->__callstatic = function; break; case 9: clazz->__tostring = function; break; case 10: clazz->__serialize = function; break; case 11: clazz->__unserialize = function; break; case 12: clazz->__debugInfo = function; break; } return; } } } /* }}} */ zend_function *uopz_find_method(zend_class_entry *ce, zend_string *name) { /* {{{ */ return uopz_find_function(&ce->function_table, name); } /* }}} */ zend_function *uopz_find_function(HashTable *table, zend_string *name) { /* {{{ */ zend_string *key = zend_string_tolower(name); zend_function *ptr = zend_hash_find_ptr(table, key); zend_string_release(key); return ptr; } /* }}} */ zend_bool uopz_is_magic_method(zend_class_entry *clazz, zend_string *function) /* {{{ */ { if (!clazz) { return 0; } if (zend_string_equals_literal_ci(function, "__construct") || zend_string_equals_literal_ci(function, "__destruct") || zend_string_equals_literal_ci(function, "__clone") || zend_string_equals_literal_ci(function, "__get") || zend_string_equals_literal_ci(function, "__set") || zend_string_equals_literal_ci(function, "__unset") || zend_string_equals_literal_ci(function, "__isset") || zend_string_equals_literal_ci(function, "__call") || zend_string_equals_literal_ci(function, "__callstatic") || zend_string_equals_literal_ci(function, "__tostring") || zend_string_equals_literal_ci(function, "__debuginfo") || zend_string_equals_literal_ci(function, "__serialize") || zend_string_equals_literal_ci(function, "__unserialize") || zend_string_equals_literal_ci(function, "__sleep") || zend_string_equals_literal_ci(function, "__wakeup")) { return 1; } return 0; } /* }}} */ int uopz_clean_function(zval *zv) { /* {{{ */ zend_function *fp = Z_PTR_P(zv); if (fp->type == ZEND_USER_FUNCTION && fp->common.fn_flags & ZEND_ACC_UOPZ) { return ZEND_HASH_APPLY_REMOVE; } return ZEND_HASH_APPLY_KEEP; } /* }}} */ int uopz_clean_class(zval *zv) { /* {{{ */ zend_class_entry *ce = Z_PTR_P(zv); if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { return ZEND_HASH_APPLY_KEEP; } zend_hash_apply( &ce->function_table, uopz_clean_function); return ZEND_HASH_APPLY_KEEP; } /* }}} */ static inline void uopz_caller_switch(zif_handler *old, zif_handler *new) { zif_handler *current = old; *old = *new; *new = *current; } static void uopz_callers_init(void) { /* {{{ */ uopz_call_user_func_ptr = zend_hash_str_find_ptr( CG(function_table), "uopz_call_user_func", sizeof("uopz_call_user_func")-1); uopz_call_user_func_array_ptr = zend_hash_str_find_ptr( CG(function_table), "uopz_call_user_func_array", sizeof("uopz_call_user_func_array")-1); zend_call_user_func_ptr = zend_hash_str_find_ptr( CG(function_table), "call_user_func", sizeof("call_user_func")-1); zend_call_user_func_array_ptr = zend_hash_str_find_ptr( CG(function_table), "call_user_func_array", sizeof("call_user_func_array")-1); uopz_caller_switch(&zend_call_user_func_ptr->handler, &uopz_call_user_func_ptr->handler); uopz_caller_switch(&zend_call_user_func_array_ptr->handler, &uopz_call_user_func_array_ptr->handler); } /* }}} */ static void uopz_callers_shutdown(void) { /* {{{ */ uopz_caller_switch(&uopz_call_user_func_ptr->handler, &zend_call_user_func_ptr->handler); uopz_caller_switch(&uopz_call_user_func_array_ptr->handler, &zend_call_user_func_array_ptr->handler); } /* }}} */ #define UOPZ_CALL_HOOKS(variadic) do { \ if (!fcc.function_handler) { \ break; \ } \ \ { \ uopz_hook_t *uhook = uopz_find_hook(fcc.function_handler); \ \ if (uhook && !uhook->busy) { \ uopz_execute_hook(uhook, execute_data, 1, variadic); \ } \ } \ \ do { \ uopz_return_t *ureturn = uopz_find_return(fcc.function_handler); \ \ if (ureturn) { \ if (UOPZ_RETURN_IS_EXECUTABLE(ureturn)) { \ if (UOPZ_RETURN_IS_BUSY(ureturn)) { \ break; \ } \ \ uopz_execute_return(ureturn, execute_data, return_value); \ return; \ } \ \ ZVAL_COPY(return_value, &ureturn->value); \ return; \ } \ } while (0); \ } while (0) /* {{{ proto mixed uopz_call_user_func(callable function, ... args) */ PHP_FUNCTION(uopz_call_user_func) { zval retval; zend_fcall_info fci; zend_fcall_info_cache fcc; ZEND_PARSE_PARAMETERS_START(1, -1) Z_PARAM_FUNC(fci, fcc) Z_PARAM_VARIADIC_WITH_NAMED(fci.params, fci.param_count, fci.named_params) ZEND_PARSE_PARAMETERS_END(); fci.retval = &retval; UOPZ_CALL_HOOKS(0); if (zend_call_function(&fci, &fcc) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { if (Z_ISREF(retval)) { zend_unwrap_reference(&retval); } ZVAL_COPY_VALUE(return_value, &retval); } } /* }}} */ /* {{{ proto mixed uopz_call_user_func_array(callable function, array args) */ PHP_FUNCTION(uopz_call_user_func_array) { HashTable *params; zval retval; zend_fcall_info fci; zend_fcall_info_cache fcc; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_FUNC(fci, fcc) Z_PARAM_ARRAY_HT(params) ZEND_PARSE_PARAMETERS_END(); fci.named_params = params; fci.retval = &retval; UOPZ_CALL_HOOKS(1); if (zend_call_function(&fci, &fcc) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { if (Z_ISREF(retval)) { zend_unwrap_reference(&retval); } ZVAL_COPY_VALUE(return_value, &retval); } } /* }}} */ void uopz_request_init(void) { /* {{{ */ UOPZ(copts) = CG(compiler_options); CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION | ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION | ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS | ZEND_COMPILE_IGNORE_USER_FUNCTIONS | ZEND_COMPILE_GUARDS; zend_hash_init(&UOPZ(returns), 8, NULL, uopz_table_dtor, 0); zend_hash_init(&UOPZ(mocks), 8, NULL, uopz_zval_dtor, 0); zend_hash_init(&UOPZ(hooks), 8, NULL, uopz_table_dtor, 0); { char *report = getenv("UOPZ_REPORT_MEMLEAKS"); PG(report_memleaks) = (report && report[0] == '1'); } uopz_callers_init(); } /* }}} */ void uopz_request_shutdown(void) { /* {{{ */ CG(compiler_options) = UOPZ(copts); zend_hash_apply(CG(class_table), uopz_clean_class); zend_hash_apply(CG(function_table), uopz_clean_function); zend_hash_destroy(&UOPZ(mocks)); zend_hash_destroy(&UOPZ(returns)); zend_hash_destroy(&UOPZ(hooks)); uopz_callers_shutdown(); } /* }}} */ #endif /* UOPZ_UTIL */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/src/util.h0000664000175000017500000000457614133502033013114 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_UTIL_H #define UOPZ_UTIL_H extern PHP_FUNCTION(uopz_call_user_func); extern PHP_FUNCTION(uopz_call_user_func_array); void uopz_handle_magic(zend_class_entry *clazz, zend_string *name, zend_function *function); zend_function *uopz_find_function(HashTable *table, zend_string *name); zend_function *uopz_find_method(zend_class_entry *ce, zend_string *name); zend_bool uopz_is_magic_method(zend_class_entry *clazz, zend_string *function); int uopz_clean_function(zval *zv); int uopz_clean_class(zval *zv); void uopz_request_init(void); void uopz_request_shutdown(void); static inline void uopz_zval_dtor(zval *zv) { /* {{{ */ zval_ptr_dtor(zv); } /* }}} */ static inline zend_bool uopz_is_cuf(zend_execute_data *execute_data) { if (EX(func)->type == ZEND_INTERNAL_FUNCTION) { if (EX(func)->internal_function.handler == zif_uopz_call_user_func) { return 1; } } return 0; } static inline zend_bool uopz_is_cufa(zend_execute_data *execute_data) { if (EX(func)->type == ZEND_INTERNAL_FUNCTION) { if (EX(func)->internal_function.handler == zif_uopz_call_user_func_array) { return 1; } } return 0; } #endif /* UOPZ_UTIL_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/tests/bugs/0001-uopz_set_static.phpt0000664000175000017500000000053014133502033017755 0ustar remiremi--TEST-- uopz_set_static with array --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- 1]); var_dump(Foo::bar()); ?> --EXPECTF-- int(%d) int(1) uopz-7.1.1/tests/bugs/0002-uopz_set_static_clear.phpt0000664000175000017500000000052514133502033021130 0ustar remiremi--TEST-- uopz_set_static with empty array --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- 1]); var_dump(Foo::bar()); uopz_set_static ('Foo', 'bar', []); var_dump(Foo::bar()); ?> --EXPECT-- int(1) NULL uopz-7.1.1/tests/bugs/0003-uopz_get_property.phpt0000664000175000017500000000064514133502033020347 0ustar remiremi--TEST-- call uopz_get_property in a class scope --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- name; } } class UM { public function getProp($obj, $name) { uopz_get_property($obj, $name); } } $f = new Foo(); $um = New UM(); $um->getProp($f, 'name'); echo $f->getName(); ?> --EXPECT-- hello uopz-7.1.1/tests/bugs/0004-uopz_set_property.phpt0000664000175000017500000000067614133502033020370 0ustar remiremi--TEST-- call uopz_set_property in a class scope --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- name; } } class UM { public function setProp($obj, $name, $value) { uopz_set_property($obj, $name, $value); } } $f = new Foo(); $um = New UM(); $um->setProp($f, 'name', 'world'); echo $f->getName(); ?> --EXPECT-- world uopz-7.1.1/tests/bugs/0005-uopz_set_return_prototype.phpt0000664000175000017500000000072314133502033022142 0ustar remiremi--TEST-- prototype mixup setting return --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- bar = $value; } } uopz_set_return (Foo::class, 'setBar', function ($value) { echo "unused\n"; }, true); class Foo2 extends Foo { public function setBar ($value) { $this->bar = 'expected'; } } $foo2 = new Foo2; $foo2->setBar ('meh'); var_dump($foo2->bar); --EXPECT-- string(8) "expected" uopz-7.1.1/tests/bugs/0006-uopz-opcache-const-subst.phpt0000664000175000017500000000160514133502033021426 0ustar remiremi--TEST-- opcache constant substitution disable --EXTENSIONS-- uopz --SKIPIF-- --INI-- uopz.disable=0 opcache.enabled=1 opcache.enable_cli=1 opcache.optimization_level=0x7FFFBFFF --FILE-- callB () : $this->callA (); } private function callA () { echo "A!" . PHP_EOL; } private function callB () { echo "B!" . PHP_EOL; } } uopz_set_return (Foo::class, 'callA', function () { echo "uopz A!" . PHP_EOL; }, true); $x = new Foo; var_dump ($x->__call (null, null)); uopz_redefine (Foo::class, 'USE_B', true); var_dump ($x->__call (null, null)); ?> --EXPECT-- uopz A! NULL B! NULL uopz-7.1.1/tests/bugs/gh43.phpt0000664000175000017500000000116414133502033014731 0ustar remiremi--TEST-- github #43 --DESCRIPTION-- Setting hook on __invoke method doesn't work on call_user_func --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- __invoke(2)); var_dump(call_user_func($h, 3)); uopz_set_return(Handler::class, '__invoke', 42); var_dump($h(1)); var_dump($h->__invoke(2)); var_dump(call_user_func($h, 3)); --EXPECT-- hi!int(1) hi!int(2) hi!int(3) hi!int(42) hi!int(42) hi!int(42) uopz-7.1.1/tests/bugs/gh53.phpt0000664000175000017500000000056714133502033014740 0ustar remiremi--TEST-- github #53 --DESCRIPTION-- uopz_redefine() refuses to redefine a constant as array --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- ===DONE=== --EXPECT-- array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(4) } ===DONE=== uopz-7.1.1/tests/bugs/gh64.phpt0000664000175000017500000000060514133502033014733 0ustar remiremi--TEST-- Segfault after uopz_set_static --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- 3, 'b'=>7]); $a = new A; $a->fn(); print_r(uopz_get_static(A::class, "fn")); --EXPECT-- Array ( [a] => 1 ) Array ( [a] => 4 ) uopz-7.1.1/tests/bugs/gh68.phpt0000664000175000017500000000105614133502033014740 0ustar remiremi--TEST-- github #68: uopz_set_mock should not hang when mock with no-arg constructor is called with args --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- foo(); uopz_unset_mock(X::class); uopz_set_mock(X::class, new Y); $x = new X(0); $x->foo(); ?> --EXPECT-- anonymous Y uopz-7.1.1/tests/bugs/gh73.phpt0000664000175000017500000000123714133502033014735 0ustar remiremi--TEST-- bugs in cuf(a) --EXTENSIONS-- uopz --FILE-- emitEvent(1); ?> --EXPECTF-- bool(true) int(%d) uopz-7.1.1/tests/bugs/gh99.phpt0000664000175000017500000000056014133502033014743 0ustar remiremi--TEST-- get hook not case insensitive --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- --EXPECT-- string(11) "hook called" int(123) object(Closure)#1 (0) { } uopz-7.1.1/tests/bugs/gh100.phpt0000664000175000017500000000061414133502033015002 0ustar remiremi--TEST-- get hook not case insensitive --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- --EXPECT-- 1234 uopz-7.1.1/tests/bugs/gh102.phpt0000664000175000017500000000052714133502033015007 0ustar remiremi--TEST-- uopz_undefine with namespaced constants does not delete constant --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- --EXPECTF-- Fatal error: Uncaught Error: Undefined constant %s in %s:7 Stack trace: #0 {main} thrown in %s on line 7 uopz-7.1.1/tests/bugs/gh106.phpt0000664000175000017500000000022214133502033015003 0ustar remiremi--TEST-- uopz.exit enabled --EXTENSIONS-- uopz --INI-- uopz.disable=0 uopz.exit=1 --FILE-- --EXPECT-- OK uopz-7.1.1/tests/bugs/gh106a.phpt0000664000175000017500000000022614133502033015150 0ustar remiremi--TEST-- uopz.exit disabled --EXTENSIONS-- uopz --INI-- uopz.disable=0 uopz.exit=0 --FILE-- --EXPECT-- OK OK uopz-7.1.1/tests/bugs/gh109.phpt0000664000175000017500000000104014133502033015005 0ustar remiremi--TEST-- hook closure call inconsistency --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- some(1, 2); ?> --EXPECT-- array(2) { [0]=> int(1) [1]=> int(2) } array(2) { [0]=> int(1) [1]=> int(2) } array(2) { [0]=> int(1) [1]=> int(2) } uopz-7.1.1/tests/001.phpt0000664000175000017500000000177514133502033013534 0ustar remiremi--TEST-- uopz_set_return --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- bar(1)); uopz_set_return(Foo::class, "bar", function(int $arg) : int { return $arg * 2; }, true); var_dump($foo->bar(2)); try { uopz_set_return(Foo::class, "nope", 1); } catch(Throwable $t) { var_dump($t->getMessage()); } class Bar extends Foo {} try { uopz_set_return(Bar::class, "bar", null); } catch (Throwable $t) { var_dump($t->getMessage()); } var_dump($foo::qux(10)); uopz_set_Return(Foo::class, "qux", function(int $arg) : int { return $arg * 2; }, true); var_dump($foo::qux(20)); ?> --EXPECT-- bool(true) bool(true) int(4) string(61) "failed to set return for Foo::nope, the method does not exist" string(63) "failed to set return for Bar::bar, the method is defined in Foo" int(10) int(40) uopz-7.1.1/tests/002.phpt0000664000175000017500000000073414133502033013527 0ustar remiremi--TEST-- uopz_get_return --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- --EXPECT-- bool(true) bool(true) bool(true) object(Closure)#1 (0) { } NULL uopz-7.1.1/tests/003.phpt0000664000175000017500000000136014133502033013524 0ustar remiremi--TEST-- uopz_unset_return --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- bar()); var_dump(uopz_unset_return(Foo::class, "bar")); var_dump($foo->bar()); var_dump(uopz_unset_return(Foo::class, "nope")); var_dump(uopz_set_return("bar", function(){ return true; }, true)); var_dump(uopz_get_return("bar")); var_dump(bar()); var_dump(uopz_unset_return("bar")); var_dump(uopz_get_return(DateTime::class, "__construct")); ?> --EXPECT-- bool(true) bool(true) bool(true) bool(false) bool(false) bool(true) object(Closure)#2 (0) { } bool(true) bool(true) NULL uopz-7.1.1/tests/004.phpt0000664000175000017500000000146014133502033013526 0ustar remiremi--TEST-- uopz_set_mock --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- getMessage(), "\n"; } try { Foo::newSelf(); } catch (Error $e) { echo $e->getMessage(), "\n"; } ?> --EXPECTF-- object(Bar)#%d (0) { } int(1) int(-1) object(Foo)#%d (0) { } Class "DoesntExist" not found Class "DoesntExist" not found uopz-7.1.1/tests/005.phpt0000664000175000017500000000045314133502033013530 0ustar remiremi--TEST-- uopz_get_mock --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- --EXPECT-- string(3) "Bar" object(Bar)#1 (0) { } uopz-7.1.1/tests/006.phpt0000664000175000017500000000055614133502033013535 0ustar remiremi--TEST-- uopz_unset_mock --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- --EXPECT-- string(3) "Bar" NULL OK uopz-7.1.1/tests/007.phpt0000664000175000017500000000350414133502033013532 0ustar remiremi--TEST-- uopz_get_static --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- method(); var_dump(uopz_get_static(Foo::class, "method")); try { uopz_get_static(Foo::class, "none"); } catch (RuntimeException $ex) { var_dump($ex->getMessage()); } try { uopz_get_static("none"); } catch (RuntimeException $ex) { var_dump($ex->getMessage()); } try { uopz_get_static(DateTime::class, "__construct"); } catch(RuntimeException $ex) { var_dump($ex->getMessage()); } try { uopz_get_static("phpversion"); } catch(RuntimeException $ex) { var_dump($ex->getMessage()); } try { uopz_get_static(Foo::class, "nostatics"); } catch(RuntimeException $ex) { var_dump($ex->getMessage()); } try { uopz_get_static("nostatics"); } catch(RuntimeException $ex) { var_dump($ex->getMessage()); } ?> --EXPECTF-- array(2) { ["vars"]=> array(5) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> int(4) [4]=> int(5) } ["bar"]=> string(3) "bar" } array(2) { ["vars"]=> array(6) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> int(4) [4]=> int(5) [5]=> int(6) } ["bar"]=> string(3) "bar" } string(%d) "failed to get statics from method %s::%s, it does not exist" string(%d) "failed to get statics from function %s, it does not exist" string(%d) "failed to get statics from internal method %s::%s" string(%d) "failed to get statics from internal function %s" string(%d) "failed to set statics in method %s::%s, no statics declared" string(%d) "failed to set statics in function %s, no statics declared" uopz-7.1.1/tests/008.phpt0000664000175000017500000000335214133502033013534 0ustar remiremi--TEST-- uopz_set_static --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- method(); var_dump(uopz_get_static(Foo::class, "method")); uopz_set_static(Foo::class, "method", [ "vars" => [] ]); $foo->method(); var_dump(uopz_get_static(Foo::class, "method")); try { uopz_set_static(Foo::class, "none", []); } catch(RuntimeException $ex) { var_dump($ex->getMessage()); } try { uopz_set_static("none", []); } catch(RuntimeException $ex) { var_dump($ex->getMessage()); } try { uopz_set_static("phpversion", []); } catch(RuntimeException $ex) { var_dump($ex->getMessage()); } try { uopz_set_static(DateTime::class, "__construct", []); } catch(RuntimeException $ex) { var_dump($ex->getMessage()); } try { uopz_set_static(Foo::class, "nostatics", []); } catch(RuntimeException $ex) { var_dump($ex->getMessage()); } try { uopz_set_static("nostatics", []); } catch(RuntimeException $ex) { var_dump($ex->getMessage()); } ?> --EXPECTF-- array(1) { ["vars"]=> array(6) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> int(4) [4]=> int(5) [5]=> int(6) } } array(1) { ["vars"]=> array(1) { [0]=> int(6) } } string(%d) "failed to set statics in method %s::%s, it does not exist" string(%d) "failed to set statics in function %s, it does not exist" string(%d) "failed to set statics in internal function %s" string(%d) "failed to set statics in internal method %s::%s" string(%d) "failed to set statics in method %s::%s, no statics declared" string(%d) "failed to set statics in function %s, no statics declared" uopz-7.1.1/tests/009.phpt0000664000175000017500000000214114133502033013530 0ustar remiremi--TEST-- uopz_set_hook --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- method(true); class Bar extends Foo {} try { uopz_set_hook(Bar::class, "method", function(){}); } catch (Throwable $t) { var_dump($t->getMessage()); } try { uopz_set_hook(Bar::class, "none", function(){}); } catch (Throwable $t) { var_dump($t->getMessage()); } var_dump(uopz_set_hook("bar", function(){ var_dump("hook"); })); bar(); var_dump(uopz_unset_hook("bar")); bar(); var_dump(uopz_unset_hook("none")); uopz_set_hook("bar", function() { throw new Exception("Ooops"); }); try { var_dump(bar()); } catch (Exception $e) { echo $e->getMessage(), "\n"; } ?> --EXPECTF-- bool(true) bool(true) object(Foo)#2 (0) { } string(%d) "failed to set hook for %s::%s, the method is defined in %s" string(%d) "failed to set hook for %s::%s, the method does not exist" bool(true) string(4) "hook" bool(true) bool(false) Ooops uopz-7.1.1/tests/010.phpt0000664000175000017500000000116514133502033013525 0ustar remiremi--TEST-- uopz_get_hook --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- --EXPECT-- bool(true) object(Closure)#1 (1) { ["parameter"]=> array(1) { ["$arg"]=> string(10) "" } } object(Closure)#2 (0) { } NULL NULL uopz-7.1.1/tests/011.phpt0000664000175000017500000000061514133502033013525 0ustar remiremi--TEST-- uopz_unset_hook --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- method()); uopz_unset_hook(Foo::class, "method"); var_dump($foo->method()); ?> --EXPECT-- bool(true) bool(true) bool(false) bool(false) uopz-7.1.1/tests/012.phpt0000664000175000017500000000463714133502033013536 0ustar remiremi--TEST-- uopz_add_function --EXTENSIONS-- uopz --INI-- uopz.disable=0 opcache.enable_cli=0 --FILE-- priv(true); }); uopz_add_function(Foo::class, "PRIV", /** doc **/ function(bool $arg){ return $arg; }, ZEND_ACC_PRIVATE); uopz_add_function(Foo::class, "STATICFUNCTION", /** doc **/ function(){ static $a = [1,2,3]; try { $a[] = 4; } catch (Throwable $t) { } finally { return count($a) == 4; } }, ZEND_ACC_STATIC); uopz_add_function(Foo::class, "__construct", function() { var_dump("__construct"); }); uopz_add_function(Foo::class, "__destruct", function(){ var_dump("__destruct"); }); uopz_add_function(Foo::class, "__clone", function() { var_dump("clone"); return $this; }); uopz_add_function(Foo::class, "__tostring", function(){ return "string"; }); uopz_add_function(Foo::class, "__set", function($property, $value){ var_dump($property, $value); }); uopz_add_function(Foo::class, "__get", function($property) { return $property; }); uopz_add_function(Foo::class, "__isset", function($property) { return false; }); uopz_add_function(Foo::class, "__unset", function($property) { throw new Error(); }); $foo = new Foo(); var_dump((string) $foo); $x = clone $foo; var_dump($foo->property); $foo->property = "value"; var_dump(isset($foo->property)); try { unset($foo->property); } catch (Error $ex) { echo "unset\n"; } var_dump($foo->method()); var_dump(Foo::staticFunction()); try { var_dump($foo->priv(false)); } catch(Error $e) { var_dump($e->getMessage()); } try { uopz_add_function(Foo::class, "exists", function() {}); } catch(Exception $e) { var_dump($e->getMessage()); } try { uopz_add_function("uopz_add_function", function(){}); } catch (Exception $e) { var_dump($e->getMessage()); } $foo = "foo"; uopz_add_function($foo . "bar", function() {}); foobar(); ?> --EXPECTF-- string(%d) "__construct" string(%d) "string" string(%d) "clone" string(%d) "property" string(%d) "property" string(%d) "value" bool(false) unset bool(true) bool(true) string(%d) "Call to private method Foo::priv() from %s" string(%d) "will not replace existing method %s::%s, use uopz_set_return instead" string(%d) "will not replace existing function %s, use uopz_set_return instead" string(%d) "__destruct" string(%d) "__destruct" uopz-7.1.1/tests/013.phpt0000664000175000017500000000221114133502033013521 0ustar remiremi--TEST-- uopz_del_function --EXTENSIONS-- uopz --INI-- uopz.disable=0 opcache.enable_cli=0 --FILE-- method()); var_dump(uopz_del_function(Foo::class, "method")); try { $foo->method(); } catch(Throwable $e) { var_dump($e->getMessage()); } try { uopz_del_function(Foo::class, "exists"); } catch (Throwable $t) { var_dump($t->getMessage()); } try { uopz_del_function("phpversion"); } catch (Throwable $t) { var_dump($t->getMessage()); } try { uopz_del_function(stdClass::class, "none"); } catch (Throwable $t) { var_dump($t->getMessage()); } try { uopz_del_function("none42"); } catch (Throwable $t) { var_dump($t->getMessage()); } ?> --EXPECTF-- bool(true) bool(true) bool(true) string(%d) "Call to undefined method Foo::method()" string(%d) "cannot delete method %s::%s, it was not added by uopz" string(%d) "cannot delete function %s, it was not added by uopz" string(%d) "cannot delete method %s::%s, it does not exist" string(%d) "cannot delete function %s, it does not exist" uopz-7.1.1/tests/016.phpt0000664000175000017500000000272214133502033013533 0ustar remiremi--TEST-- uopz_flags --EXTENSIONS-- uopz --INI-- uopz.disable=0 opcache.enable_cli=0 --FILE-- getMessage()); } try { uopz_flags(Foo::class, '', ZEND_ACC_STATIC); } catch (Exception $ex) { var_dump($ex->getMessage()); } uopz_flags(Foo::class, '', ZEND_ACC_FINAL); $reflector = new ReflectionClass(Foo::class); var_dump($reflector->isFinal()); try { uopz_flags(Foo::class, "none", ZEND_ACC_PUBLIC); } catch (Exception $ex) { var_dump($ex->getMessage()); } try { uopz_flags("none", ZEND_ACC_STATIC); } catch (Exception $ex) { var_dump($ex->getMessage()); } try { uopz_flags("", ZEND_ACC_STATIC); } catch (Exception $ex) { var_dump($ex->getMessage()); } ?> --EXPECTF-- bool(false) bool(true) bool(false) string(%d) "attempt to set public, private or protected on class entry %s, not allowed" string(%d) "attempt to set static on class entry %s, not allowed" bool(true) string(%d) "failed to set or get flags of method %s::%s, it does not exist" string(%d) "failed to set or get flags of function %s, it does not exist" string(%d) "failed to set or get flags of function , it does not exist" uopz-7.1.1/tests/017.phpt0000664000175000017500000000101014133502033013521 0ustar remiremi--TEST-- uopz_redefine --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- --EXPECT-- int(1) int(2) int(3) int(4) int(5) OK uopz-7.1.1/tests/018.phpt0000664000175000017500000000124214133502033013531 0ustar remiremi--TEST-- uopz_undefine --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- getConstants())); var_dump(uopz_undefine(Foo::class, "NONE")); uopz_undefine(Foo::class, "QUX"); var_dump(count($reflector->getConstants())); try { uopz_undefine("PHP_VERSION"); } catch (RuntimeException $ex) { echo "OK\n"; } ?> --EXPECT-- bool(true) bool(false) int(1) int(1) bool(false) int(0) OK uopz-7.1.1/tests/019.phpt0000664000175000017500000000336514133502033013542 0ustar remiremi--TEST-- uopz_set_property/uopz_get_property --EXTENSIONS-- uopz --INI-- uopz.disable=0 --FILE-- bar; } } echo "----------------------\n"; $foo = new Foo(); uopz_set_property($foo, "bar", 10); var_dump(uopz_get_property($foo, "bar")); echo "-----------------------\n\n"; echo "----------------------\n"; uopz_set_property(Foo::class, "staticBar", 100); var_dump(uopz_get_property(Foo::class, "staticBar")); var_dump($foo->testBar(), $foo->testStaticBar()); try { uopz_set_property(Foo::class, "staticQux", 10); } catch (RuntimeException $ex) { var_dump($ex->getMessage()); } echo "-----------------------\n\n"; echo "----------------------\n"; class Bar { private static $bar; public function foo() { return self::$bar; } } class Qux extends Bar {} $qux = new Qux; uopz_set_property(Qux::class, "bar", 10); var_dump($qux->foo(), uopz_get_property(Qux::class, "bar")); class Baz { private $bar; public function foo() { return $this->bar; } } class Bill extends Baz {} $bill = new Bill; uopz_set_property($bill, "bar", 100); var_dump($bill->foo(), uopz_get_property($bill, "bar")); uopz_set_property($bill, "qux", 1000); var_dump($bill->qux, uopz_get_property($bill, "qux")); var_dump(@uopz_get_property($bill, "none")); echo "-----------------------\n\n"; ?> --EXPECTF-- ---------------------- int(10) ----------------------- ---------------------- int(100) int(10) int(100) string(%d) "cannot set non-existent static property %s::%s" ----------------------- ---------------------- int(10) int(10) int(100) int(100) int(1000) int(1000) NULL ----------------------- uopz-7.1.1/tests/020.phpt0000664000175000017500000000053514133502033013526 0ustar remiremi--TEST-- uopz_get_exit_status --EXTENSIONS-- uopz --INI-- uopz.disable=0 uopz.exit=0 opcache.enable_cli=0 xdebug.enable=0 --FILE-- --EXPECT-- int(10) int(20) uopz-7.1.1/tests/021.phpt0000664000175000017500000000042214133502033013522 0ustar remiremi--TEST-- uopz.disabled --EXTENSIONS-- uopz --INI-- uopz.disable=1 --FILE-- --EXPECTF-- Fatal error: Uncaught %s: uopz is disabled by configuration (uopz.disable) in %s:2 Stack trace: #0 %s(2): uopz_set_return() #1 {main} thrown in %s on line 2 uopz-7.1.1/tests/027.phpt0000664000175000017500000000051414133502033013532 0ustar remiremi--TEST-- init method call non string method --EXTENSIONS-- uopz --FILE-- $method()); ?> --EXPECTF-- Fatal error: Uncaught Error: Method name must be a string in %s:11 Stack trace: #0 {main} thrown in %s on line 11 uopz-7.1.1/tests/028.phpt0000664000175000017500000000041414133502033013532 0ustar remiremi--TEST-- init method call non object object --EXTENSIONS-- uopz --FILE-- method()); ?> --EXPECTF-- Fatal error: Uncaught Error: Call to a member function method() on string in %s:4 Stack trace: #0 {main} thrown in %s on line 4 uopz-7.1.1/tests/029.phpt0000664000175000017500000000034214133502033013533 0ustar remiremi--TEST-- init method call object ref --EXTENSIONS-- uopz --FILE-- method()); ?> --EXPECTF-- bool(false) uopz-7.1.1/tests/032.phpt0000664000175000017500000000070514133502033013530 0ustar remiremi--TEST-- init ns fcall --EXTENSIONS-- uopz --INI-- uopz.disable=0 opcache.enable_cli=0 --FILE-- method()); } } class Mock extends Test { public static function method() { return true; } } Test::run(); --EXPECTF-- bool(true) uopz-7.1.1/tests/035.phpt0000664000175000017500000000033614133502033013533 0ustar remiremi--TEST-- fetch class constant non existent class --EXTENSIONS-- uopz --FILE-- __construct() #1 {main} thrown in %s on line 10 uopz-7.1.1/tests/038.phpt0000664000175000017500000000032214133502033013531 0ustar remiremi--TEST-- fetch class string no mock --EXTENSIONS-- uopz --FILE-- --EXPECTF-- Fatal error: Uncaught Error: Class name must be a valid object or a string in %s:%d Stack trace: #0 {main} thrown in %s on line %d uopz-7.1.1/uopz.h0000664000175000017500000000400014133502033012323 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifndef UOPZ_H #define UOPZ_H extern zend_module_entry uopz_module_entry; #define phpext_uopz_ptr &uopz_module_entry #define PHP_UOPZ_VERSION "7.1.1" #define PHP_UOPZ_EXTNAME "uopz" ZEND_BEGIN_MODULE_GLOBALS(uopz) zend_long copts; HashTable returns; HashTable mocks; HashTable hooks; zend_bool exit; zval estatus; zend_bool disable; ZEND_END_MODULE_GLOBALS(uopz) #ifdef ZTS #define UOPZ(v) TSRMG(uopz_globals_id, zend_uopz_globals *, v) #else #define UOPZ(v) (uopz_globals.v) #endif #include "ext/spl/spl_exceptions.h" #include "Zend/zend_inheritance.h" #include "Zend/zend_exceptions.h" #include "Zend/zend_closures.h" #define uopz_exception(message, ...) zend_throw_exception_ex\ (spl_ce_RuntimeException, 0, message, ##__VA_ARGS__) #endif /* UOPZ_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/uopz.c0000664000175000017500000004453114133502033012333 0ustar remiremi/* +----------------------------------------------------------------------+ | uopz | +----------------------------------------------------------------------+ | Copyright (c) Joe Watkins 2016-2021 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Joe Watkins | +----------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "uopz.h" #include "src/util.h" #include "src/return.h" #include "src/hook.h" #include "src/constant.h" #include "src/class.h" #include "src/function.h" #include "src/handlers.h" #include "src/executors.h" ZEND_DECLARE_MODULE_GLOBALS(uopz) #define uopz_parse_parameters(spec, ...) zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), spec, ##__VA_ARGS__) #define uopz_refuse_parameters(message, ...) zend_throw_exception_ex\ (spl_ce_InvalidArgumentException, 0, message, ##__VA_ARGS__) #define uopz_disabled_guard() do { \ if (UOPZ(disable)) { \ zend_throw_exception_ex(spl_ce_RuntimeException, 0, "uopz is disabled by configuration (uopz.disable)"); \ return; \ } \ } while(0) PHP_INI_BEGIN() STD_PHP_INI_ENTRY("uopz.disable", "0", PHP_INI_SYSTEM, OnUpdateBool, disable, zend_uopz_globals, uopz_globals) STD_PHP_INI_ENTRY("uopz.exit", "0", PHP_INI_SYSTEM, OnUpdateBool, exit, zend_uopz_globals, uopz_globals) PHP_INI_END() /* {{{ */ static void php_uopz_init_globals(zend_uopz_globals *ng) { memset(ng, 0, sizeof(zend_uopz_globals)); } /* }}} */ /* {{{ PHP_MINIT_FUNCTION */ static PHP_MINIT_FUNCTION(uopz) { ZEND_INIT_MODULE_GLOBALS(uopz, php_uopz_init_globals, NULL); REGISTER_INI_ENTRIES(); if (UOPZ(disable)) { return SUCCESS; } REGISTER_LONG_CONSTANT("ZEND_ACC_PUBLIC", ZEND_ACC_PUBLIC, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("ZEND_ACC_PRIVATE", ZEND_ACC_PRIVATE, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("ZEND_ACC_PROTECTED", ZEND_ACC_PROTECTED, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("ZEND_ACC_PPP_MASK", ZEND_ACC_PPP_MASK, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("ZEND_ACC_STATIC", ZEND_ACC_STATIC, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("ZEND_ACC_FINAL", ZEND_ACC_FINAL, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("ZEND_ACC_ABSTRACT", ZEND_ACC_ABSTRACT, CONST_CS|CONST_PERSISTENT); uopz_executors_init(); uopz_handlers_init(); return SUCCESS; } /* }}} */ /* {{{ */ static PHP_MSHUTDOWN_FUNCTION(uopz) { if (UOPZ(disable)) { return SUCCESS; } uopz_handlers_shutdown(); uopz_executors_shutdown(); return SUCCESS; } /* }}} */ /* {{{ PHP_RINIT_FUNCTION */ static PHP_RINIT_FUNCTION(uopz) { zend_class_entry *ce = NULL; zend_string *spl; #ifdef ZTS ZEND_TSRMLS_CACHE_UPDATE(); #endif if (UOPZ(disable)) { return SUCCESS; } if (INI_INT("opcache.optimization_level")) { zend_string *optimizer = zend_string_init( ZEND_STRL("opcache.optimization_level"), 1); zend_long level = INI_INT("opcache.optimization_level"); zend_string *value; /* must disable block pass 1 constant substitution */ level &= ~(1<<0); /* disable CFG optimization (exit optimized away here) */ level &= ~(1<<4); /* disable DCE (want code after exit) */ level &= ~(1<<13); /* don't inline functions */ level &= ~(1<<15); value = strpprintf(0, "0x%08X", (unsigned int) level); zend_alter_ini_entry(optimizer, value, ZEND_INI_SYSTEM, ZEND_INI_STAGE_ACTIVATE); zend_string_release(optimizer); zend_string_release(value); } spl = zend_string_init(ZEND_STRL("RuntimeException"), 0); spl_ce_RuntimeException = (ce = zend_lookup_class(spl)) ? ce : zend_exception_get_default(); zend_string_release(spl); spl = zend_string_init(ZEND_STRL("InvalidArgumentException"), 0); spl_ce_InvalidArgumentException = (ce = zend_lookup_class(spl)) ? ce : zend_exception_get_default(); zend_string_release(spl); uopz_request_init(); return SUCCESS; } /* }}} */ /* {{{ PHP_RSHUTDOWN_FUNCTION */ static PHP_RSHUTDOWN_FUNCTION(uopz) { if (UOPZ(disable)) { return SUCCESS; } uopz_request_shutdown(); return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ static PHP_MINFO_FUNCTION(uopz) { php_info_print_table_start(); php_info_print_table_header(2, "uopz support", UOPZ(disable) ? "disabled" : "enabled"); php_info_print_table_row(2, "Version", PHP_UOPZ_VERSION); php_info_print_table_end(); DISPLAY_INI_ENTRIES(); } /* }}} */ /* {{{ proto bool uopz_set_return(string class, string function, mixed variable [, bool execute ]) proto bool uopz_set_return(function, mixed variable [, bool execute ]) */ static PHP_FUNCTION(uopz_set_return) { zend_string *function = NULL; zval *variable = NULL; zend_class_entry *clazz = NULL; zend_bool execute = 0; uopz_disabled_guard(); if (uopz_parse_parameters("CSz|b", &clazz, &function, &variable, &execute) != SUCCESS && uopz_parse_parameters("Sz|b", &function, &variable, &execute) != SUCCESS) { uopz_refuse_parameters( "unexpected parameter combination, expected (class, function, variable [, execute]) or (function, variable [, execute])"); return; } if (execute && (Z_TYPE_P(variable) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(variable), zend_ce_closure))) { uopz_refuse_parameters( "only closures are accepted as executable return values"); return; } if (uopz_is_magic_method(clazz, function)) { uopz_refuse_parameters( "will not override magic methods, too magical"); return; } RETURN_BOOL(uopz_set_return(clazz, function, variable, execute)); } /* }}} */ /* {{{ proto bool uopz_unset_return(string class, string function) proto bool uopz_unset_return(string function) */ static PHP_FUNCTION(uopz_unset_return) { zend_string *function = NULL; zend_class_entry *clazz = NULL; uopz_disabled_guard(); if (uopz_parse_parameters("CS", &clazz, &function) != SUCCESS && uopz_parse_parameters("S", &function) != SUCCESS) { uopz_refuse_parameters( "unexpected parameter combination, expected (class, function) or (function)"); return; } RETURN_BOOL(uopz_unset_return(clazz, function)); } /* }}} */ /* {{{ proto mixed uopz_get_return(string class, string function) proto mixed uopz_get_return(string function) */ static PHP_FUNCTION(uopz_get_return) { zend_string *function = NULL; zend_class_entry *clazz = NULL; uopz_disabled_guard(); if (uopz_parse_parameters("CS", &clazz, &function) != SUCCESS && uopz_parse_parameters("S", &function) != SUCCESS) { uopz_refuse_parameters( "unexpected parameter combination, expected (class, function)"); return; } uopz_get_return(clazz, function, return_value); } /* }}} */ /* {{{ proto void uopz_set_mock(string class, mixed mock) */ static PHP_FUNCTION(uopz_set_mock) { zend_string *clazz; zval *mock; uopz_disabled_guard(); if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz", &clazz, &mock) != SUCCESS) { RETURN_THROWS(); } if (Z_TYPE_P(mock) != IS_STRING && Z_TYPE_P(mock) != IS_OBJECT) { uopz_refuse_parameters( "unexpected parameter combination, mock is expected to be a string, or an object"); return; } uopz_set_mock(clazz, mock); } /* }}} */ /* {{{ proto void uopz_unset_mock(string mock) */ static PHP_FUNCTION(uopz_unset_mock) { zend_string *clazz; uopz_disabled_guard(); if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &clazz) != SUCCESS) { RETURN_THROWS(); } uopz_unset_mock(clazz); } /* }}} */ /* {{{ proto void uopz_get_mock(string mock) */ static PHP_FUNCTION(uopz_get_mock) { zend_string *clazz; uopz_disabled_guard(); if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &clazz) != SUCCESS) { RETURN_THROWS(); } uopz_get_mock(clazz, return_value); } /* }}} */ /* {{{ proto array uopz_get_static(string class, string method) array uopz_get_static(string function) */ static PHP_FUNCTION(uopz_get_static) { zend_string *function = NULL; zend_class_entry *clazz = NULL; uopz_disabled_guard(); if (uopz_parse_parameters("CS", &clazz, &function) != SUCCESS && uopz_parse_parameters("S", &function) != SUCCESS) { uopz_refuse_parameters( "unexpected parameter combination, expected (class, function) or (function)"); return; } uopz_get_static(clazz, function, return_value); } /* }}} */ /* {{{ proto array uopz_set_static(string class, string method, array statics) array uopz_set_static(string function, array statics) */ static PHP_FUNCTION(uopz_set_static) { zend_string *function = NULL; zend_class_entry *clazz = NULL; zval *statics = NULL; uopz_disabled_guard(); if (uopz_parse_parameters("CSz", &clazz, &function, &statics) != SUCCESS && uopz_parse_parameters("Sz", &function, &statics) != SUCCESS) { uopz_refuse_parameters( "unexpected parameter combination, expected (class, function, statics) or (function, statics)"); return; } uopz_set_static(clazz, function, statics); } /* }}} */ /* {{{ proto bool uopz_set_hook(string class, string function, Closure hook) bool uopz_set_hook(string function, Closure hook) */ static PHP_FUNCTION(uopz_set_hook) { zend_string *function = NULL; zend_class_entry *clazz = NULL; zval *hook = NULL; uopz_disabled_guard(); if (uopz_parse_parameters("CSO", &clazz, &function, &hook, zend_ce_closure) != SUCCESS && uopz_parse_parameters("SO", &function, &hook, zend_ce_closure) != SUCCESS) { uopz_refuse_parameters( "unexpected parameter combination, expected (class, function, hook) or (function, hook)"); return; } RETURN_BOOL(uopz_set_hook(clazz, function, hook)); } /* }}} */ /* {{{ proto bool uopz_unset_hook(string class, string function) bool uopz_unset_hook(string function) */ static PHP_FUNCTION(uopz_unset_hook) { zend_string *function = NULL; zend_class_entry *clazz = NULL; uopz_disabled_guard(); if (uopz_parse_parameters("CS", &clazz, &function) != SUCCESS && uopz_parse_parameters("S", &function) != SUCCESS) { uopz_refuse_parameters( "unexpected parameter combination, expected (class, function) or (function)"); return; } RETURN_BOOL(uopz_unset_hook(clazz, function)); } /* }}} */ /* {{{ proto Closure uopz_get_hook(string class, string function) Closure uopz_get_hook(string function) */ static PHP_FUNCTION(uopz_get_hook) { zend_string *function = NULL; zend_class_entry *clazz = NULL; uopz_disabled_guard(); if (uopz_parse_parameters("CS", &clazz, &function) != SUCCESS && uopz_parse_parameters("S", &function) != SUCCESS) { uopz_refuse_parameters( "unexpected parameter combination, expected (class, function) or (function)"); return; } uopz_get_hook(clazz, function, return_value); } /* }}} */ /* {{{ proto bool uopz_add_function(string class, string method, Closure function [, int flags = ZEND_ACC_PUBLIC [, bool all = false]]) bool uopz_add_function(string function, Closure function [, int flags = ZEND_ACC_PUBLIC]) */ static PHP_FUNCTION(uopz_add_function) { zend_class_entry *clazz = NULL; zend_string *name = NULL; zval *closure = NULL; zend_long flags = ZEND_ACC_PUBLIC; zend_bool all = 1; uopz_disabled_guard(); if (uopz_parse_parameters("CSO|lb", &clazz, &name, &closure, zend_ce_closure, &flags, &all) != SUCCESS && uopz_parse_parameters("SO|l", &name, &closure, zend_ce_closure, &flags) != SUCCESS) { uopz_refuse_parameters( "unexpected parameter combination, expected (class, function, closure [, flags]) or (function, closure [, flags])"); return; } RETURN_BOOL(uopz_add_function(clazz, name, closure, flags, all)); } /* }}} */ /* {{{ proto bool uopz_del_function(string class, string method [, bool all = false]) bool uopz_del_function(string function) */ static PHP_FUNCTION(uopz_del_function) { zend_class_entry *clazz = NULL; zend_string *name = NULL; zend_bool all = 1; uopz_disabled_guard(); if (uopz_parse_parameters("CS|b", &clazz, &name, &all) != SUCCESS && uopz_parse_parameters("S", &name) != SUCCESS) { uopz_refuse_parameters( "unexpected parameter combination, expected (class, function) or (function)"); return; } RETURN_BOOL(uopz_del_function(clazz, name, all)); } /* }}} */ /* {{{ proto bool uopz_redefine(string constant, mixed variable) proto bool uopz_redefine(string class, string constant, mixed variable) */ static PHP_FUNCTION(uopz_redefine) { zend_string *name = NULL; zval *variable = NULL; zend_class_entry *clazz = NULL; uopz_disabled_guard(); if (uopz_parse_parameters("CSz", &clazz, &name, &variable) != SUCCESS && uopz_parse_parameters("Sz", &name, &variable) != SUCCESS) { uopz_refuse_parameters( "unexpected parameter combination, expected (class, constant, variable) or (constant, variable)"); return; } if (uopz_constant_redefine(clazz, name, variable)) { if (clazz) { while ((clazz = clazz->parent)) { uopz_constant_redefine( clazz, name, variable); } } RETURN_TRUE; } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto bool uopz_undefine(string constant) proto bool uopz_undefine(string class, string constant) */ static PHP_FUNCTION(uopz_undefine) { zend_string *name = NULL; zend_class_entry *clazz = NULL; uopz_disabled_guard(); if (uopz_parse_parameters("CS", &clazz, &name) != SUCCESS && uopz_parse_parameters("S", &name) != SUCCESS) { uopz_refuse_parameters( "unexpected parameter combination, expected (class, constant) or (constant)"); return; } if (uopz_constant_undefine(clazz, name)) { if (clazz) { while ((clazz = clazz->parent)) { uopz_constant_undefine(clazz, name); } } RETURN_TRUE; } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto int uopz_flags(string function [, int flags]) proto int uopz_flags(string class, string function [, int flags]) */ static PHP_FUNCTION(uopz_flags) { zend_string *name = NULL; zend_class_entry *clazz = NULL; zend_long flags = ZEND_LONG_MAX; uopz_disabled_guard(); if (uopz_parse_parameters("CS|l", &clazz, &name, &flags) != SUCCESS && uopz_parse_parameters("S|l", &name, &flags) != SUCCESS) { uopz_refuse_parameters( "unexpected parameter combination, expected " "(class, function, flags) or (function, flags)"); return; } uopz_flags(clazz, name, flags, return_value); } /* }}} */ /* {{{ proto void uopz_set_property(object instance, string property, mixed value) void uopz_set_property(string class, string property, mixed value) */ static PHP_FUNCTION(uopz_set_property) { zval *scope; zend_string *prop; zval *value; uopz_disabled_guard(); if (zend_parse_parameters(ZEND_NUM_ARGS(), "zSz", &scope, &prop, &value) != SUCCESS) { RETURN_THROWS(); } if (Z_TYPE_P(scope) != IS_OBJECT && Z_TYPE_P(scope) != IS_STRING) { uopz_refuse_parameters( "unexpected paramter combination, expected " "(class, property, value) or (object, property, value)"); return; } if (Z_TYPE_P(scope) == IS_OBJECT) { uopz_set_property(scope, prop, value); } else { zend_class_entry *ce = zend_lookup_class(Z_STR_P(scope)); if (!ce) { return; } uopz_set_static_property(ce, prop, value); } } /* }}} */ /* {{{ proto mixed uopz_get_property(object instance, string property) proto mixed uopz_get_property(string class, string property) */ static PHP_FUNCTION(uopz_get_property) { zval *scope; zend_string *prop; uopz_disabled_guard(); if (zend_parse_parameters(ZEND_NUM_ARGS(), "zS", &scope, &prop) != SUCCESS) { RETURN_THROWS(); } if (Z_TYPE_P(scope) != IS_OBJECT && Z_TYPE_P(scope) != IS_STRING) { uopz_refuse_parameters( "unexpected paramter combination, expected " "(class, property) or (object, property)"); return; } if (Z_TYPE_P(scope) == IS_OBJECT) { uopz_get_property(scope, prop, return_value); } else { zend_class_entry *ce = zend_lookup_class(Z_STR_P(scope)); if (!ce) { return; } uopz_get_static_property(ce, prop, return_value); } } /* }}} */ /* {{{ proto mixed uopz_get_exit_status(void) */ static PHP_FUNCTION(uopz_get_exit_status) { uopz_disabled_guard(); if (zend_parse_parameters_none() != SUCCESS) { RETURN_THROWS(); } if (Z_TYPE(UOPZ(estatus)) != IS_UNDEF) { ZVAL_COPY(return_value, &UOPZ(estatus)); } } /* }}} */ /* {{{ proto mixed uopz_allow_exit(bool allow) */ static PHP_FUNCTION(uopz_allow_exit) { zend_bool allow; uopz_disabled_guard(); if (zend_parse_parameters(ZEND_NUM_ARGS(), "b", &allow) != SUCCESS) { RETURN_THROWS(); } UOPZ(exit) = allow; } /* }}} */ /* {{{ uopz_functions[] */ ZEND_BEGIN_ARG_INFO(uopz_ignore_arginfo, 1) ZEND_ARG_VARIADIC_INFO(0, arguments) ZEND_END_ARG_INFO() # define UOPZ_FE(f) PHP_FE(f, uopz_ignore_arginfo) ZEND_BEGIN_ARG_INFO(uopz_no_args_arginfo, 0) ZEND_END_ARG_INFO() # define UOPZ_FE_NOARGS(f) PHP_FE(f, uopz_no_args_arginfo) static const zend_function_entry uopz_functions[] = { UOPZ_FE(uopz_set_return) UOPZ_FE(uopz_get_return) UOPZ_FE(uopz_unset_return) UOPZ_FE(uopz_set_mock) UOPZ_FE(uopz_get_mock) UOPZ_FE(uopz_unset_mock) UOPZ_FE(uopz_get_static) UOPZ_FE(uopz_set_static) UOPZ_FE(uopz_set_hook) UOPZ_FE(uopz_get_hook) UOPZ_FE(uopz_unset_hook) UOPZ_FE(uopz_add_function) UOPZ_FE(uopz_del_function) UOPZ_FE(uopz_flags) UOPZ_FE(uopz_redefine) UOPZ_FE(uopz_undefine) UOPZ_FE(uopz_set_property) UOPZ_FE(uopz_get_property) UOPZ_FE_NOARGS(uopz_get_exit_status) UOPZ_FE(uopz_allow_exit) UOPZ_FE(uopz_call_user_func) UOPZ_FE(uopz_call_user_func_array) ZEND_FE_END }; #undef UOPZ_FE /* }}} */ /* {{{ uopz_module_entry */ zend_module_entry uopz_module_entry = { STANDARD_MODULE_HEADER, PHP_UOPZ_EXTNAME, uopz_functions, PHP_MINIT(uopz), PHP_MSHUTDOWN(uopz), PHP_RINIT(uopz), PHP_RSHUTDOWN(uopz), PHP_MINFO(uopz), PHP_UOPZ_VERSION, STANDARD_MODULE_PROPERTIES }; /* }}} */ #ifdef COMPILE_DL_UOPZ ZEND_GET_MODULE(uopz) #ifdef ZTS ZEND_TSRMLS_CACHE_DEFINE(); #endif #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ uopz-7.1.1/config.m40000664000175000017500000000206214133502033012672 0ustar remiremiPHP_ARG_ENABLE(uopz, whether to enable uopz support, [ --enable-uopz Enable uopz support]) PHP_ARG_ENABLE(uopz-coverage, whether to enable uopz coverage support, [ --enable-uopz-coverage Enable uopz coverage support], no, no) PHP_ARG_WITH(uopz-sanitize, whether to enable AddressSanitizer for uopz, [ --with-uopz-sanitize Build uopz with AddressSanitizer support], no, no) if test "$PHP_UOPZ" != "no"; then if test "$PHP_UOPZ_SANITIZE" != "no"; then EXTRA_LDFLAGS="-lasan" EXTRA_CFLAGS="-fsanitize=address -fno-omit-frame-pointer" PHP_SUBST(EXTRA_LDFLAGS) PHP_SUBST(EXTRA_CFLAGS) fi PHP_NEW_EXTENSION(uopz, uopz.c src/util.c src/return.c src/hook.c src/constant.c src/function.c src/class.c src/handlers.c src/executors.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) PHP_ADD_BUILD_DIR($ext_builddir/src, 1) PHP_ADD_INCLUDE($ext_builddir) AC_MSG_CHECKING([uopz coverage]) if test "$PHP_UOPZ_COVERAGE" != "no"; then AC_MSG_RESULT([enabled]) PHP_ADD_MAKEFILE_FRAGMENT else AC_MSG_RESULT([disabled]) fi fi uopz-7.1.1/config.w320000664000175000017500000000053614133502033012771 0ustar remiremi// $Id$ // vim:ft=javascript ARG_ENABLE("uopz", "for uopz support", "no"); if (PHP_UOPZ != "no") { EXTENSION("uopz", "uopz.c"); ADD_SOURCES( configure_module_dirname + "/src", "util.c return.c hook.c constant.c function.c class.c handlers.c executors.c", "uopz" ); ADD_FLAG("CFLAGS_UOPZ", "/I" + configure_module_dirname + ""); } uopz-7.1.1/LICENSE0000664000175000017500000000614714133502033012200 0ustar remiremi-------------------------------------------------------------------- The PHP License, version 3.01 Copyright (c) 2012-2014 Joe Watkins. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name "PHP" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact group@php.net. 4. Products derived from this software may not be called "PHP", nor may "PHP" appear in their name, without prior written permission from group@php.net. You may indicate that your software works in conjunction with PHP by saying "Foo for PHP" instead of calling it "PHP Foo" or "phpfoo" 5. The PHP Group may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by the PHP Group. No one other than the PHP Group has the right to modify the terms applicable to covered code created under this License. 6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes PHP software, freely available from ". THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------- The PHP Group can be contacted via Email at group@php.net. For more information on the PHP Group and the PHP project, please see . PHP includes the Zend Engine, freely available at . uopz-7.1.1/README.md0000664000175000017500000001701014133502033012441 0ustar remiremiUOPZ ==== *User Operations for Zend* [![Build and Test](https://github.com/krakjoe/uopz/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/krakjoe/uopz/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/github/krakjoe/uopz/badge.svg?branch=master)](https://coveralls.io/github/krakjoe/uopz?branch=master) The ```uopz``` extension is focused on providing utilities to aid with unit testing PHP code. It supports the following activities: - Intercepting function execution - Intercepting object creation - Hooking into function execution - Manipulation of function statics - Manipulation of function flags - Redefinition of constants - Deletion of constants - Runtime creation of functions and methods *Note: All of the above activities are compatible with opcache* Requirements and Installation ============================= If you use XDebug you need to use version 2.9.4 or higher. See [INSTALL.md](INSTALL.md) API === *The PHP API for ```uopz```* ```php /** * Provide a return value for an existing function * @param string class * @param string function * @param mixed value * @param bool execute * If value is a Closure and execute flag is set, the Closure will * be executed in place of the existing function **/ function uopz_set_return(string class, string function, mixed value [, bool execute = 0]) : bool; /** * Provide a return value for an existing function * @param string function * @param mixed value * @param bool execute * If value is a Closure and execute flag is set, the Closure will * be executed in place of the existing function **/ function uopz_set_return(string function, mixed value [, bool execute = 0]) : bool; /** * Get a previously set return value * @param string class * @param string function **/ function uopz_get_return(string class, string function) : mixed; /** * Get a previously set return value * @param string function **/ function uopz_get_return(string function) : mixed; /** * Unset a previously set return value * @param string class * @param string function **/ function uopz_unset_return(string class, string function) : bool; /** * Unset a previously set return value * @param string function **/ function uopz_unset_return(string function) : bool; /** * Use mock in place of class * @param string class * @param mixed mock * Mock can be an object, or the name of a class **/ function uopz_set_mock(string class, mixed mock); /** * Get previously set mock for class * @param string class **/ function uopz_get_mock(string class); /** * Unset previously set mock * @param string class **/ function uopz_unset_mock(string class); /** * Get static variables from method scope * @param string class * @param string function **/ function uopz_get_static(string class, string function) : array; /** * Get static variables from function scope * @param string function **/ function uopz_get_static(string function) : array; /** * Set static variables in method scope * @param string class * @param string function * @param array static **/ function uopz_set_static(string class, string function, array static); /** * Set static variables in function scope * @param string function * @param array static **/ function uopz_set_static(string function, array static); /** * Execute hook when entering class::function * @param string class * @param string function **/ function uopz_set_hook(string class, string function, Closure hook) : bool; /** * Execute hook when entering function * @param string function **/ function uopz_set_hook(string function, Closure hook) : bool; /** * Get previously set hook on class::function * @param string class * @param string function **/ function uopz_get_hook(string class, string function) : Closure; /** * Get previously set hook on function * @param string function **/ function uopz_get_hook(string function) : Closure; /** * Remove previously set hook on class::function * @param string class * @param string function **/ function uopz_unset_hook(string class, string function) : bool; /** * Remove previously set hook on function * @param string function **/ function uopz_unset_hook(string function) : bool; /** * Add a non-existent method * @param string class * @param string function * @param Closure handler * @param int flags * @param bool all * If all is true, all classes that descend from class will also be affected **/ function uopz_add_function(string class, string function, Closure handler [, int flags = ZEND_ACC_PUBLIC [, bool all = true]]) : bool; /** * Add a non-existent function * @param string function * @param Closure handler * @param int flags * @param bool all * If all is true, all classes that descend from class will also be affected **/ function uopz_add_function(string function, Closure handler [, int flags = ZEND_ACC_PUBLIC [, bool all = true]]) : bool; /** * Delete a previously added method * @param string class * @param string function * @param bool all * If all is true, all classes that descend from class will also be affected **/ function uopz_del_function(string class, string function, [, bool all = true]); /** * Delete a previously added function * @param string function **/ function uopz_del_function(string function); /** * Redefine $class::$constant to $value * @param string class * @param string constant * @param mixed value * Note: only user constants should be redefined * Note: if the constant does not exist it will be created **/ function uopz_redefine(string class, string constant, mixed value); /** * Redefine $constant to $value * @param string constant * @param mixed value * Note: only user constants should be redefined * Note: if the constant does not exist it will be created **/ function uopz_redefine(string constant, mixed value); /** * Delete $class::$constant * @param string class * @param string constant * Note: only user constants should be undefined **/ function uopz_undefine(string class, string constant); /** * Delete $constant * @param string constant * Note: only user constants should be undefined **/ function uopz_undefine(string constant); /** * Get or set flags on $class::$method() * @param string class * @param string method * @param int flags */ function uopz_flags(string class, string method [, int flags]) : int; /** * Get or set flags on $method() * @param string method * @param int flags */ function uopz_flags(string function, [, int flags]) : int; /** * Set instance property * @param object instance * @param string property * @param mixed value */ function uopz_set_property(object instance, string property, mixed value); /** * Set static class property * @param string class * @param string property * @param mixed value */ function uopz_set_property(string class, string property, mixed value); /** * Get instance property * @param object instance * @param string property */ function uopz_get_property(object instance, string property) : mixed; /** * Get static class property * @param string class * @param string property */ function uopz_get_property(string class, string property) : mixed; /** * Retrieve the last set exit() status * Note: opcache optimizes away dead code after unconditional exit * Note: exit() breaks xdebug hooks */ function uopz_get_exit_status() : mixed; /** * Allows control over disabled exit opcode * @param bool allow * Note: by default exit will be ignored */ function uopz_allow_exit(bool allow) : void; ``` Supported Versions ================== The currently supported version of uopz is 7 which requires PHP8.0+ Testing ======= *Running the test suite* After ``make`` has executed, run: make test You are done reading ==================== That is all !!!