From ce0b480bd6446c81b909451cebd54fb8c53c8590 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Fri, 21 Aug 2020 10:13:33 +0200 Subject: [PATCH 3/7] Skip unsupported tests on PHP 7.4+ with OPcache As of PHP 7.4.0, uopz can no longer support manipulation of immutable classes and functions when OPcache is enabled. Therefore, we skip the respective test cases under these conditions. We also have to make sure that the `die()`s are actually executed. --- tests/016.phpt | 12 +++++++++++- tests/bugs/gh76.phpt | 8 ++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/016.phpt b/tests/016.phpt index 3d65ea4..79e9b3a 100644 --- a/tests/016.phpt +++ b/tests/016.phpt @@ -1,7 +1,17 @@ --TEST-- uopz_flags --SKIPIF-- - +=') + && function_exists('opcache_get_status') + && ($status = opcache_get_status()) + && $status['opcache_enabled']) +{ + die('skip not for PHP 7.4+ with OPcache'); +} +?> --INI-- uopz.disable=0 --FILE-- diff --git a/tests/bugs/gh76.phpt b/tests/bugs/gh76.phpt index 62680c3..ef09976 100644 --- a/tests/bugs/gh76.phpt +++ b/tests/bugs/gh76.phpt @@ -3,6 +3,14 @@ uopz_extend affects only explicit calls via parent:: but not inherited methods --SKIPIF-- =') + && function_exists('opcache_get_status') + && ($status = opcache_get_status()) + && $status['opcache_enabled']) +{ + die('skip not for PHP 7.4+ with OPcache'); +} ?> --INI-- uopz.disable=0 -- 2.29.2 From 0f975b64248ecc0af579370e1266b0cfc5775c69 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Thu, 20 Aug 2020 16:23:34 +0200 Subject: [PATCH 4/7] Don't remove methods of immutable classes These are not fully reloaded by OPcache, so the methods would be missing after the first request shutdown. Cf. . --- src/util.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/util.c b/src/util.c index 707b236..fce1fd3 100644 --- a/src/util.c +++ b/src/util.c @@ -169,7 +169,12 @@ int uopz_clean_function(zval *zv) { /* {{{ */ int uopz_clean_class(zval *zv) { /* {{{ */ zend_class_entry *ce = Z_PTR_P(zv); - + +#if PHP_VERSION_ID >= 70400 + if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { + return ZEND_HASH_APPLY_KEEP; + } +#endif zend_hash_apply( &ce->function_table, uopz_clean_function); -- 2.29.2 From cfaa8987096117eb0c921ef0c927320b4831b725 Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Thu, 1 Oct 2020 10:12:07 +0200 Subject: [PATCH 5/7] php 8 support --- src/class.c | 14 ++++++++++++++ src/copy.c | 9 ++++++++- src/function.c | 8 ++++++-- src/handlers.c | 20 +++++++++++++++++--- src/hook.c | 4 ++++ src/return.c | 4 ++++ src/util.c | 12 +++++++++++- tests/012.phpt | 2 +- tests/018.phpt | 4 ++-- tests/035.phpt | 2 +- tests/040.phpt | 2 +- tests/bugs/gh102.phpt | 2 +- uopz.c | 16 ++++++++++++++-- 13 files changed, 84 insertions(+), 15 deletions(-) diff --git a/src/class.c b/src/class.c index 95fa0f2..555577e 100644 --- a/src/class.c +++ b/src/class.c @@ -220,6 +220,10 @@ zend_bool uopz_implement(zend_class_entry *clazz, zend_class_entry *interface) { zend_do_implement_interface(clazz, interface); +#if PHP_VERSION_ID >= 80000 + clazz->ce_flags |= ZEND_ACC_RESOLVED_INTERFACES; +#endif + return instanceof_function(clazz, interface); } /* }}} */ @@ -246,8 +250,13 @@ void uopz_set_property(zval *object, zval *member, zval *value) { /* {{{ */ uopz_set_scope(Z_OBJCE_P(object)); } +#if PHP_VERSION_ID >= 80000 + Z_OBJ_HT_P(object) + ->write_property(Z_OBJ_P(object), Z_STR_P(member), value, NULL); +#else Z_OBJ_HT_P(object) ->write_property(object, member, value, NULL); +#endif uopz_set_scope(scope); } /* }}} */ @@ -276,8 +285,13 @@ void uopz_get_property(zval *object, zval *member, zval *value) { /* {{{ */ uopz_set_scope(Z_OBJCE_P(object)); } +#if PHP_VERSION_ID >= 80000 + prop = Z_OBJ_HT_P(object)->read_property( + Z_OBJ_P(object), Z_STR_P(member), BP_VAR_R, NULL, &rv); +#else prop = Z_OBJ_HT_P(object)->read_property( object, member, BP_VAR_R, NULL, &rv); +#endif uopz_set_scope(scope); diff --git a/src/copy.c b/src/copy.c index 11b1414..c8ddb0e 100644 --- a/src/copy.c +++ b/src/copy.c @@ -176,7 +176,14 @@ static inline zend_arg_info* uopz_copy_arginfo(zend_op_array *op_array, zend_arg while (it < end) { if (info[it].name) info[it].name = zend_string_copy(old[it].name); -#if PHP_VERSION_ID >= 70200 +#if PHP_VERSION_ID >= 80000 + if (ZEND_TYPE_IS_SET(old[it].type) && ZEND_TYPE_HAS_CLASS(old[it].type)) { + info[it].type = (zend_type) ZEND_TYPE_INIT_CLASS( + zend_string_copy( + ZEND_TYPE_NAME(info[it].type)), + ZEND_TYPE_ALLOW_NULL(info[it].type), 0); + } +#elif PHP_VERSION_ID >= 70200 if (ZEND_TYPE_IS_SET(old[it].type) && ZEND_TYPE_IS_CLASS(old[it].type)) { info[it].type = ZEND_TYPE_ENCODE_CLASS( zend_string_copy( diff --git a/src/function.c b/src/function.c index 8ce6c72..4245bd2 100644 --- a/src/function.c +++ b/src/function.c @@ -60,11 +60,15 @@ zend_bool uopz_add_function(zend_class_entry *clazz, zend_string *name, zval *cl zend_hash_update(functions, key, closure); zval_copy_ctor(closure); - +#if PHP_VERSION_ID >= 80000 + function = uopz_copy_closure(clazz, + (zend_function*) zend_get_closure_method_def(Z_OBJ_P(closure)), + flags); +#else function = uopz_copy_closure(clazz, (zend_function*) zend_get_closure_method_def(closure), flags); - +#endif zend_hash_update_ptr(table, key, (void*) function); if (clazz) { diff --git a/src/handlers.c b/src/handlers.c index aadf504..a71cc53 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -143,8 +143,14 @@ UOPZ_HANDLERS_DECL_BEGIN() UOPZ_HANDLER_DECL(ZEND_INIT_STATIC_METHOD_CALL, init_static_method_call) UOPZ_HANDLERS_DECL_END() +#if PHP_VERSION_ID >= 80000 +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) { +#else 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, zend_free_op *should_free, int type) { -#if PHP_VERSION_ID >= 70300 +#endif +#if PHP_VERSION_ID >= 80000 + return zend_get_zval_ptr(opline, op_type, node, execute_data); +#elif PHP_VERSION_ID >= 70300 return zend_get_zval_ptr(opline, op_type, node, execute_data, should_free, type); #else return zend_get_zval_ptr(op_type, node, execute_data, should_free, type); @@ -234,7 +240,9 @@ static zend_always_inline int _uopz_vm_dispatch(UOPZ_OPCODE_HANDLER_ARGS) { int uopz_vm_exit(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ UOPZ_USE_OPLINE; zval *estatus; +#if PHP_VERSION_ID < 80000 zend_free_op free_op1; +#endif if (UOPZ(exit)) { UOPZ_VM_DISPATCH(); @@ -245,8 +253,12 @@ int uopz_vm_exit(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ opline, opline->op1_type, &opline->op1, - execute_data, - &free_op1, BP_VAR_R); + execute_data +#if PHP_VERSION_ID < 80000 + , &free_op1, BP_VAR_R); +#else + ); +#endif do { if (Z_TYPE_P(estatus) == IS_LONG) { @@ -263,9 +275,11 @@ int uopz_vm_exit(UOPZ_OPCODE_HANDLER_ARGS) { /* {{{ */ } } while (0); +#if PHP_VERSION_ID < 80000 if (free_op1) { zval_ptr_dtor_nogc(free_op1); } +#endif ZVAL_COPY(&UOPZ(estatus), estatus); } diff --git a/src/hook.c b/src/hook.c index 7319ac7..6c03c02 100644 --- a/src/hook.c +++ b/src/hook.c @@ -162,7 +162,11 @@ void uopz_execute_hook(uopz_hook_t *uhook, zend_execute_data *execute_data, zend uhook->busy = 1; +#if PHP_VERSION_ID >= 80000 + zend_create_closure(&closure, (zend_function*) zend_get_closure_method_def(Z_OBJ(uhook->closure)), +#else zend_create_closure(&closure, (zend_function*) zend_get_closure_method_def(&uhook->closure), +#endif uhook->clazz, uhook->clazz, Z_OBJ(EX(This)) ? &EX(This) : NULL); zend_fcall_info_init(&closure, 0, &fci, &fcc, NULL, &error); diff --git a/src/return.c b/src/return.c index f47948b..3b6b0c0 100644 --- a/src/return.c +++ b/src/return.c @@ -172,7 +172,11 @@ void uopz_execute_return(uopz_return_t *ureturn, zend_execute_data *execute_data ureturn->flags ^= UOPZ_RETURN_BUSY; +#if PHP_VERSION_ID >= 80000 + zend_create_closure(&closure, (zend_function*) zend_get_closure_method_def(Z_OBJ(ureturn->value)), +#else zend_create_closure(&closure, (zend_function*) zend_get_closure_method_def(&ureturn->value), +#endif ureturn->clazz, ureturn->clazz, Z_OBJ(EX(This)) ? &EX(This) : NULL); zend_fcall_info_init(&closure, 0, &fci, &fcc, NULL, &error); diff --git a/src/util.c b/src/util.c index 707b236..311e1cb 100644 --- a/src/util.c +++ b/src/util.c @@ -90,8 +90,13 @@ void uopz_handle_magic(zend_class_entry *clazz, zend_string *name, zend_function case 7: clazz->__call = function; break; case 8: clazz->__callstatic = function; break; case 9: clazz->__tostring = function; break; +#if PHP_VERSION_ID >= 80000 + case 10: clazz->__serialize = function; break; + case 11: clazz->__unserialize = function; break; +#else case 10: clazz->serialize_func = function; break; case 11: clazz->unserialize_func = function; break; +#endif case 12: clazz->__debugInfo = function; break; } return; @@ -148,7 +153,11 @@ zend_bool uopz_is_magic_method(zend_class_entry *clazz, zend_string *function) / } /* }}} */ static inline int uopz_closure_equals(zval *closure, zend_function *function) { /* {{{ */ +#if PHP_VERSION_ID >= 80000 + const zend_function *cmp = zend_get_closure_method_def(Z_OBJ_P(closure)); +#else const zend_function *cmp = zend_get_closure_method_def(closure); +#endif if (cmp->common.prototype == function) { return 1; @@ -160,7 +169,8 @@ static inline int uopz_closure_equals(zval *closure, zend_function *function) { int uopz_clean_function(zval *zv) { /* {{{ */ zend_function *fp = Z_PTR_P(zv); - if (fp->type == ZEND_USER_FUNCTION) { + if (fp->type == ZEND_USER_FUNCTION && *((zend_op_array*)fp)->refcount > 1) { + return ZEND_HASH_APPLY_REMOVE; } diff --git a/tests/012.phpt b/tests/012.phpt index 018a231..2f3c6b7 100644 --- a/tests/012.phpt +++ b/tests/012.phpt @@ -82,7 +82,7 @@ string(%d) "string" string(%d) "clone" bool(true) bool(true) -string(50) "Call to private method Foo::priv() from context ''" +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" diff --git a/tests/018.phpt b/tests/018.phpt index 7d959bb..e570ca1 100644 --- a/tests/018.phpt +++ b/tests/018.phpt @@ -15,7 +15,7 @@ const UOPZ_DEFINED = "DEFINED"; var_dump(uopz_undefine("UOPZ_DEFINED")); -var_dump(@constant("UOPZ_DEFINED")); +var_dump(defined("UOPZ_DEFINED")); var_dump(FOO::BAR); @@ -39,7 +39,7 @@ try { ?> --EXPECT-- bool(true) -NULL +bool(false) int(1) int(1) bool(false) diff --git a/tests/035.phpt b/tests/035.phpt index 96c8141..70f52d2 100644 --- a/tests/035.phpt +++ b/tests/035.phpt @@ -6,7 +6,7 @@ fetch class constant non existent class --EXPECTF-- Fatal error: Uncaught Error: Class name must be a valid object or a string in %s:%d diff --git a/tests/bugs/gh102.phpt b/tests/bugs/gh102.phpt index 27d6ffc..74ede15 100644 --- a/tests/bugs/gh102.phpt +++ b/tests/bugs/gh102.phpt @@ -16,7 +16,7 @@ uopz_undefine('Test\UNDEF'); var_dump(Test\UNDEF); ?> --EXPECTF-- -Fatal error: Uncaught Error: Undefined constant 'Test\UNDEF' in %s:7 +Fatal error: Uncaught Error: Undefined constant %s in %s:7 Stack trace: #0 {main} thrown in %s on line 7 diff --git a/uopz.c b/uopz.c index f6d9900..311ddc3 100644 --- a/uopz.c +++ b/uopz.c @@ -651,7 +651,19 @@ static PHP_FUNCTION(uopz_allow_exit) { /* {{{ uopz_functions[] */ -#define UOPZ_FE(f) PHP_FE(f, NULL) +#if PHP_VERSION_ID >= 80000 +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) +#else +# define UOPZ_FE(f) PHP_FE(f, NULL) +#endif + +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) @@ -673,7 +685,7 @@ static const zend_function_entry uopz_functions[] = { UOPZ_FE(uopz_undefine) UOPZ_FE(uopz_set_property) UOPZ_FE(uopz_get_property) - UOPZ_FE(uopz_get_exit_status) + UOPZ_FE_NOARGS(uopz_get_exit_status) UOPZ_FE(uopz_allow_exit) UOPZ_FE(uopz_call_user_func) -- 2.29.2 From 8ac783d8c151485b5bea8f917a654bb39e1a61d9 Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Thu, 1 Oct 2020 10:19:01 +0200 Subject: [PATCH 6/7] fix that --- src/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.c b/src/util.c index 311e1cb..fb10d7c 100644 --- a/src/util.c +++ b/src/util.c @@ -169,7 +169,7 @@ static inline int uopz_closure_equals(zval *closure, zend_function *function) { int uopz_clean_function(zval *zv) { /* {{{ */ zend_function *fp = Z_PTR_P(zv); - if (fp->type == ZEND_USER_FUNCTION && *((zend_op_array*)fp)->refcount > 1) { + if (fp->type == ZEND_USER_FUNCTION && (((zend_op_array*)fp)->refcount && *((zend_op_array*)fp)->refcount > 1)) { return ZEND_HASH_APPLY_REMOVE; } -- 2.29.2 From 7b0ed871029df6e58dfa071315f0979b0bc15868 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 5 Mar 2021 09:09:38 +0100 Subject: [PATCH 7/7] missing uopz.disable=0 in test --- tests/bugs/gh109.phpt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/bugs/gh109.phpt b/tests/bugs/gh109.phpt index 5d440e4..6020366 100644 --- a/tests/bugs/gh109.phpt +++ b/tests/bugs/gh109.phpt @@ -4,6 +4,8 @@ hook closure call inconsistency +--INI-- +uopz.disable=0 --FILE--