diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 43a81c87d4b7..f89123116c75 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -103,6 +103,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES . The deprecated Z_IMMUTABLE(), Z_IMMUTABLE_P(), Z_OPT_IMMUTABLE(), and Z_OPT_IMMUTABLE_P() macros have been removed. Check for IS_ARRAY && !REFCOUNTED directly. + . The zend_binary_zval_strcmp() and zend_binary_zval_strncmp() functions + have been removed, because they are unsafe by relying on the zvals + having a specific type. Use zend_binary_strcmp() / zend_binary_strncmp(), + string_compare_function() or similar instead. . Added zend_fcall_info.consumed_args together with zend_fci_consumed_arg(), which allows moving a selected callback argument instead of copying it in zend_call_function(). Currently only a single diff --git a/Zend/tests/debug_info/debug_info-error-0.0.phpt b/Zend/tests/debug_info/debug_info-error-0.0.phpt index ab41b440fc88..4df25c4c95af 100644 --- a/Zend/tests/debug_info/debug_info-error-0.0.phpt +++ b/Zend/tests/debug_info/debug_info-error-0.0.phpt @@ -17,4 +17,4 @@ $c = new C(0.0); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: __debugInfo() must return an array in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-0.phpt b/Zend/tests/debug_info/debug_info-error-0.phpt index 37289c6468ff..c0cedf3b88e5 100644 --- a/Zend/tests/debug_info/debug_info-error-0.phpt +++ b/Zend/tests/debug_info/debug_info-error-0.phpt @@ -17,4 +17,4 @@ $c = new C(0); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: __debugInfo() must return an array in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-1.0.phpt b/Zend/tests/debug_info/debug_info-error-1.0.phpt index 9b168621b5ef..6af945cb0e37 100644 --- a/Zend/tests/debug_info/debug_info-error-1.0.phpt +++ b/Zend/tests/debug_info/debug_info-error-1.0.phpt @@ -17,4 +17,4 @@ $c = new C(1.0); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: __debugInfo() must return an array in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-1.phpt b/Zend/tests/debug_info/debug_info-error-1.phpt index ae01a6055c00..7cadd485118d 100644 --- a/Zend/tests/debug_info/debug_info-error-1.phpt +++ b/Zend/tests/debug_info/debug_info-error-1.phpt @@ -17,4 +17,4 @@ $c = new C(1); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: __debugInfo() must return an array in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-case-insensitive.phpt b/Zend/tests/debug_info/debug_info-error-case-insensitive.phpt new file mode 100644 index 000000000000..c2a6f4357a5c --- /dev/null +++ b/Zend/tests/debug_info/debug_info-error-case-insensitive.phpt @@ -0,0 +1,20 @@ +--TEST-- +Testing __debugInfo() magic method declared with non-canonical case +--FILE-- +val; + } + public function __construct($val) { + $this->val = $val; + } +} + +$c = new C(1); +var_dump($c); +?> +--EXPECTF-- +Fatal error: __debugInfo() must return an array in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-empty_str.phpt b/Zend/tests/debug_info/debug_info-error-empty_str.phpt index bbab78cd8202..21611cc9bde5 100644 --- a/Zend/tests/debug_info/debug_info-error-empty_str.phpt +++ b/Zend/tests/debug_info/debug_info-error-empty_str.phpt @@ -17,4 +17,4 @@ $c = new C(""); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: __debugInfo() must return an array in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-false.phpt b/Zend/tests/debug_info/debug_info-error-false.phpt index 3e48372c420c..fb5a35c2d1e3 100644 --- a/Zend/tests/debug_info/debug_info-error-false.phpt +++ b/Zend/tests/debug_info/debug_info-error-false.phpt @@ -17,4 +17,4 @@ $c = new C(false); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: __debugInfo() must return an array in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-object.phpt b/Zend/tests/debug_info/debug_info-error-object.phpt index 42e073999908..5cdc6b289e62 100644 --- a/Zend/tests/debug_info/debug_info-error-object.phpt +++ b/Zend/tests/debug_info/debug_info-error-object.phpt @@ -17,4 +17,4 @@ $c = new C(new stdClass); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: __debugInfo() must return an array in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-resource.phpt b/Zend/tests/debug_info/debug_info-error-resource.phpt index ccacce7a74b4..1fc84fe83d87 100644 --- a/Zend/tests/debug_info/debug_info-error-resource.phpt +++ b/Zend/tests/debug_info/debug_info-error-resource.phpt @@ -19,4 +19,4 @@ $c = new C(fopen("data:text/plain,Foo", 'r')); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: __debugInfo() must return an array in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-str.phpt b/Zend/tests/debug_info/debug_info-error-str.phpt index 85d3f63b97e0..fe4201de1c9f 100644 --- a/Zend/tests/debug_info/debug_info-error-str.phpt +++ b/Zend/tests/debug_info/debug_info-error-str.phpt @@ -17,4 +17,4 @@ $c = new C("foo"); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: __debugInfo() must return an array in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-true.phpt b/Zend/tests/debug_info/debug_info-error-true.phpt index 3843c6a7a14a..12bb7329a856 100644 --- a/Zend/tests/debug_info/debug_info-error-true.phpt +++ b/Zend/tests/debug_info/debug_info-error-true.phpt @@ -17,4 +17,4 @@ $c = new C(true); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: __debugInfo() must return an array in %s on line %d diff --git a/Zend/tests/magic_methods/stringable_automatic_implementation_case_insensitive.phpt b/Zend/tests/magic_methods/stringable_automatic_implementation_case_insensitive.phpt new file mode 100644 index 000000000000..7f4564794452 --- /dev/null +++ b/Zend/tests/magic_methods/stringable_automatic_implementation_case_insensitive.phpt @@ -0,0 +1,23 @@ +--TEST-- +Stringable is automatically implemented for __toString() declared with non-canonical case +--FILE-- +getInterfaceNames()); +var_dump((string) new Test); + +?> +--EXPECT-- +bool(true) +array(1) { + [0]=> + string(10) "Stringable" +} +string(3) "foo" diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 65834adbafff..46b62dcc7c47 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2809,18 +2809,18 @@ ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, zend_check_magic_method_public(ce, fptr); zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING); zend_check_magic_method_arg_type(1, ce, fptr, error_type, MAY_BE_ARRAY); - } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_NAME)) { + } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_LCNAME)) { zend_check_magic_method_args(2, ce, fptr, error_type); zend_check_magic_method_static(ce, fptr, error_type); zend_check_magic_method_public(ce, fptr); zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING); zend_check_magic_method_arg_type(1, ce, fptr, error_type, MAY_BE_ARRAY); - } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) { + } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_LCNAME)) { zend_check_magic_method_args(0, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); zend_check_magic_method_public(ce, fptr); zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_STRING); - } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) { + } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_LCNAME)) { zend_check_magic_method_args(0, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); zend_check_magic_method_public(ce, fptr); @@ -2829,18 +2829,18 @@ ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, zend_error(E_DEPRECATED, "Returning null from %s::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead", ZSTR_VAL(ce->name)); } - } else if (zend_string_equals_literal(lcname, "__serialize")) { + } else if (zend_string_equals_literal(lcname, ZEND_SERIALIZE_FUNC_NAME)) { zend_check_magic_method_args(0, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); zend_check_magic_method_public(ce, fptr); zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_ARRAY); - } else if (zend_string_equals_literal(lcname, "__unserialize")) { + } else if (zend_string_equals_literal(lcname, ZEND_UNSERIALIZE_FUNC_NAME)) { zend_check_magic_method_args(1, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); zend_check_magic_method_public(ce, fptr); zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_ARRAY); zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_VOID); - } else if (zend_string_equals_literal(lcname, "__set_state")) { + } else if (zend_string_equals_literal(lcname, ZEND_SET_STATE_FUNC_NAME)) { zend_check_magic_method_args(1, ce, fptr, error_type); zend_check_magic_method_static(ce, fptr, error_type); zend_check_magic_method_public(ce, fptr); @@ -2888,16 +2888,16 @@ ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, c } else if (zend_string_equals_literal(lcname, ZEND_ISSET_FUNC_NAME)) { ce->__isset = fptr; ce->ce_flags |= ZEND_ACC_USE_GUARDS; - } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_NAME)) { + } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_LCNAME)) { ce->__callstatic = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) { + } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_LCNAME)) { ce->__tostring = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) { + } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_LCNAME)) { ce->__debugInfo = fptr; ce->ce_flags |= ZEND_ACC_USE_GUARDS; - } else if (zend_string_equals_literal(lcname, "__serialize")) { + } else if (zend_string_equals_literal(lcname, ZEND_SERIALIZE_FUNC_NAME)) { ce->__serialize = fptr; - } else if (zend_string_equals_literal(lcname, "__unserialize")) { + } else if (zend_string_equals_literal(lcname, ZEND_UNSERIALIZE_FUNC_NAME)) { ce->__unserialize = fptr; } } @@ -3111,7 +3111,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend /* If not specified, add __toString() return type for compatibility with Stringable * interface. */ - if (scope && zend_string_equals_literal_ci(internal_function->function_name, "__tostring") && + if (scope && zend_string_equals_literal_ci(internal_function->function_name, ZEND_TOSTRING_FUNC_LCNAME) && !(internal_function->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { zend_error(E_CORE_WARNING, "%s::__toString() implemented without string return type", ZSTR_VAL(scope->name)); @@ -4096,6 +4096,52 @@ ZEND_API zend_string *zend_get_callable_name_ex(const zval *callable, const zend } /* }}} */ +static bool zend_fcc_function_handler_equals(const zend_function *func1, const zend_function *func2) /* {{{ */ +{ + if (func1 == func2) { + return true; + } + + const bool fake_closure1 = (func1->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0; + const bool fake_closure2 = (func2->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0; + + if (!fake_closure1 && !fake_closure2) { + return false; + } + if (((func1->common.fn_flags & ZEND_ACC_CLOSURE) && !fake_closure1) || + ((func2->common.fn_flags & ZEND_ACC_CLOSURE) && !fake_closure2)) { + return false; + } + if (func1->type != func2->type || + func1->common.scope != func2->common.scope || + !zend_string_equals(func1->common.function_name, func2->common.function_name)) { + return false; + } + + if (func1->type == ZEND_USER_FUNCTION) { + return func1->op_array.opcodes == func2->op_array.opcodes; + } + + return func1->internal_function.handler == func2->internal_function.handler; +} +/* }}} */ + +ZEND_API bool zend_fcc_closure_equals_ex(const zend_fcall_info_cache* a, const zend_fcall_info_cache* b) /* {{{ */ +{ + const zend_function *func1 = a->function_handler; + const zend_function *func2 = b->function_handler; + + if (a->closure && a->closure->ce == zend_ce_closure) { + func1 = zend_get_closure_method_def(a->closure); + } + if (b->closure && b->closure->ce == zend_ce_closure) { + func2 = zend_get_closure_method_def(b->closure); + } + + return zend_fcc_function_handler_equals(func1, func2); +} +/* }}} */ + ZEND_API zend_string *zend_get_callable_name(const zval *callable) /* {{{ */ { return zend_get_callable_name_ex(callable, NULL); diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 2487c8b632f2..593be26788d7 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -758,20 +758,27 @@ ZEND_API void zend_fcall_info_argn(zend_fcall_info *fci, uint32_t argc, ...); ZEND_API zend_result zend_fcall_info_call(zend_fcall_info *fci, zend_fcall_info_cache *fcc, zval *retval, zval *args); /* Zend FCC API to store and handle PHP userland functions */ +ZEND_API bool zend_fcc_closure_equals_ex(const zend_fcall_info_cache* a, const zend_fcall_info_cache* b); + static zend_always_inline bool zend_fcc_equals(const zend_fcall_info_cache* a, const zend_fcall_info_cache* b) { + if (a->closure || b->closure) { + return a->object == b->object + && a->calling_scope == b->calling_scope + && a->called_scope == b->called_scope + && (a->closure == b->closure || zend_fcc_closure_equals_ex(a, b)) + ; + } if (UNEXPECTED((a->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) && (b->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))) { return a->object == b->object && a->calling_scope == b->calling_scope - && a->closure == b->closure && zend_string_equals(a->function_handler->common.function_name, b->function_handler->common.function_name) ; } return a->function_handler == b->function_handler && a->object == b->object && a->calling_scope == b->calling_scope - && a->closure == b->closure ; } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 105f99d24171..7e9f7ceac8db 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -8610,7 +8610,7 @@ static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string } zend_add_magic_method(ce, (zend_function *) op_array, lcname); - if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME) + if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_LCNAME) && !(ce->ce_flags & ZEND_ACC_TRAIT)) { add_stringable_interface(ce); } @@ -8841,7 +8841,7 @@ static zend_op_array *zend_compile_func_decl_ex( } zend_compile_params(params_ast, return_type_ast, - is_method && zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME) ? IS_STRING : 0); + is_method && zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_LCNAME) ? IS_STRING : 0); if (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) { zend_mark_function_as_generator(); zend_emit_op(NULL, ZEND_GENERATOR_CREATE, NULL, NULL); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 0e31332c97f0..2351882a560d 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -368,7 +368,7 @@ typedef struct _zend_oparray_context { #define ZEND_ACC_USES_THIS (1 << 17) /* | X | | */ /* | | | */ /* call through user function trampoline. e.g. | | | */ -/* __call, __callstatic | | | */ +/* __call, __callStatic | | | */ #define ZEND_ACC_CALL_VIA_TRAMPOLINE (1 << 18) /* | X | | */ /* | | | */ /* disable inline caching | | | */ @@ -1249,10 +1249,18 @@ END_EXTERN_C() #define ZEND_UNSET_FUNC_NAME "__unset" #define ZEND_ISSET_FUNC_NAME "__isset" #define ZEND_CALL_FUNC_NAME "__call" -#define ZEND_CALLSTATIC_FUNC_NAME "__callstatic" -#define ZEND_TOSTRING_FUNC_NAME "__tostring" +#define ZEND_CALLSTATIC_FUNC_NAME "__callStatic" +#define ZEND_CALLSTATIC_FUNC_LCNAME "__callstatic" +#define ZEND_TOSTRING_FUNC_NAME "__toString" +#define ZEND_TOSTRING_FUNC_LCNAME "__tostring" #define ZEND_INVOKE_FUNC_NAME "__invoke" -#define ZEND_DEBUGINFO_FUNC_NAME "__debuginfo" +#define ZEND_DEBUGINFO_FUNC_NAME "__debugInfo" +#define ZEND_DEBUGINFO_FUNC_LCNAME "__debuginfo" +#define ZEND_SLEEP_FUNC_NAME "__sleep" +#define ZEND_WAKEUP_FUNC_NAME "__wakeup" +#define ZEND_SERIALIZE_FUNC_NAME "__serialize" +#define ZEND_UNSERIALIZE_FUNC_NAME "__unserialize" +#define ZEND_SET_STATE_FUNC_NAME "__set_state" /* The following constants may be combined in CG(compiler_options) * to change the default compiler behavior */ diff --git a/Zend/zend_enum.c b/Zend/zend_enum.c index a5091f6c1b6f..7f0b58575387 100644 --- a/Zend/zend_enum.c +++ b/Zend/zend_enum.c @@ -92,21 +92,21 @@ static void zend_verify_enum_magic_methods(const zend_class_entry *ce) { // Only __get, __call, __debugInfo and __invoke are allowed - ZEND_ENUM_DISALLOW_MAGIC_METHOD(constructor, "__construct"); - ZEND_ENUM_DISALLOW_MAGIC_METHOD(destructor, "__destruct"); - ZEND_ENUM_DISALLOW_MAGIC_METHOD(clone, "__clone"); - ZEND_ENUM_DISALLOW_MAGIC_METHOD(__get, "__get"); - ZEND_ENUM_DISALLOW_MAGIC_METHOD(__set, "__set"); - ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unset, "__unset"); - ZEND_ENUM_DISALLOW_MAGIC_METHOD(__isset, "__isset"); - ZEND_ENUM_DISALLOW_MAGIC_METHOD(__tostring, "__toString"); - ZEND_ENUM_DISALLOW_MAGIC_METHOD(__serialize, "__serialize"); - ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unserialize, "__unserialize"); + ZEND_ENUM_DISALLOW_MAGIC_METHOD(constructor, ZEND_CONSTRUCTOR_FUNC_NAME); + ZEND_ENUM_DISALLOW_MAGIC_METHOD(destructor, ZEND_DESTRUCTOR_FUNC_NAME); + ZEND_ENUM_DISALLOW_MAGIC_METHOD(clone, ZEND_CLONE_FUNC_NAME); + ZEND_ENUM_DISALLOW_MAGIC_METHOD(__get, ZEND_GET_FUNC_NAME); + ZEND_ENUM_DISALLOW_MAGIC_METHOD(__set, ZEND_SET_FUNC_NAME); + ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unset, ZEND_UNSET_FUNC_NAME); + ZEND_ENUM_DISALLOW_MAGIC_METHOD(__isset, ZEND_ISSET_FUNC_NAME); + ZEND_ENUM_DISALLOW_MAGIC_METHOD(__tostring, ZEND_TOSTRING_FUNC_NAME); + ZEND_ENUM_DISALLOW_MAGIC_METHOD(__serialize, ZEND_SERIALIZE_FUNC_NAME); + ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unserialize, ZEND_UNSERIALIZE_FUNC_NAME); static const char *const forbidden_methods[] = { - "__sleep", - "__wakeup", - "__set_state", + ZEND_SLEEP_FUNC_NAME, + ZEND_WAKEUP_FUNC_NAME, + ZEND_SET_STATE_FUNC_NAME, }; uint32_t forbidden_methods_length = sizeof(forbidden_methods) / sizeof(forbidden_methods[0]); diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index a43fdcc9a48b..ab8f2c2b54f8 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -3354,18 +3354,6 @@ ZEND_API int ZEND_FASTCALL zend_binary_strncasecmp_l(const char *s1, size_t len1 } /* }}} */ -ZEND_API int ZEND_FASTCALL zend_binary_zval_strcmp(const zval *s1, const zval *s2) /* {{{ */ -{ - return zend_binary_strcmp(Z_STRVAL_P(s1), Z_STRLEN_P(s1), Z_STRVAL_P(s2), Z_STRLEN_P(s2)); -} -/* }}} */ - -ZEND_API int ZEND_FASTCALL zend_binary_zval_strncmp(const zval *s1, const zval *s2, const zval *s3) /* {{{ */ -{ - return zend_binary_strncmp(Z_STRVAL_P(s1), Z_STRLEN_P(s1), Z_STRVAL_P(s2), Z_STRLEN_P(s2), Z_LVAL_P(s3)); -} -/* }}} */ - ZEND_API bool ZEND_FASTCALL zendi_smart_streq(const zend_string *s1, const zend_string *s2) /* {{{ */ { uint8_t ret1, ret2; diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index 2dc6dbfbfc6d..b6e1923b3bf4 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -487,8 +487,6 @@ static zend_always_inline zend_string* zend_string_toupper(zend_string *str) { return zend_string_toupper_ex(str, false); } -ZEND_API int ZEND_FASTCALL zend_binary_zval_strcmp(const zval *s1, const zval *s2); -ZEND_API int ZEND_FASTCALL zend_binary_zval_strncmp(const zval *s1, const zval *s2, const zval *s3); ZEND_API int ZEND_FASTCALL zend_binary_strcmp(const char *s1, size_t len1, const char *s2, size_t len2); ZEND_API int ZEND_FASTCALL zend_binary_strncmp(const char *s1, size_t len1, const char *s2, size_t len2, size_t length); ZEND_API int ZEND_FASTCALL zend_binary_strcasecmp(const char *s1, size_t len1, const char *s2, size_t len2); diff --git a/Zend/zend_types.h b/Zend/zend_types.h index cfff3b942c4b..f3039d202519 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -713,6 +713,116 @@ static zend_always_inline uint8_t zval_get_type(const zval* pz) { #define Z_TYPE_FLAGS_SHIFT 8 #define Z_TYPE_INFO_EXTRA_SHIFT 16 +/* zval_gc_flags(zval.value->gc.u.type_info) (common flags) */ +#define GC_NOT_COLLECTABLE (1<<4) +#define GC_PROTECTED (1<<5) /* used for recursion detection */ +#define GC_IMMUTABLE (1<<6) /* can't be changed in place */ +#define GC_PERSISTENT (1<<7) /* allocated using malloc */ +#define GC_PERSISTENT_LOCAL (1<<8) /* persistent, but thread-local */ + +#define GC_TYPE_MASK 0x0000000f +#define GC_FLAGS_MASK 0x000003f0 +#define GC_INFO_MASK 0xfffffc00 +#define GC_FLAGS_SHIFT 0 +#define GC_INFO_SHIFT 10 + +static zend_always_inline uint8_t zval_gc_type(uint32_t gc_type_info) { + return (gc_type_info & GC_TYPE_MASK); +} + +static zend_always_inline uint32_t zval_gc_flags(uint32_t gc_type_info) { + return (gc_type_info >> GC_FLAGS_SHIFT) & (GC_FLAGS_MASK >> GC_FLAGS_SHIFT); +} + +static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { + return (gc_type_info >> GC_INFO_SHIFT); +} + +#define GC_TYPE_INFO(p) (p)->gc.u.type_info +#define GC_TYPE(p) zval_gc_type(GC_TYPE_INFO(p)) +#define GC_FLAGS(p) zval_gc_flags(GC_TYPE_INFO(p)) +#define GC_INFO(p) zval_gc_info(GC_TYPE_INFO(p)) + +#define GC_ADD_FLAGS(p, flags) do { \ + GC_TYPE_INFO(p) |= (flags) << GC_FLAGS_SHIFT; \ + } while (0) +#define GC_DEL_FLAGS(p, flags) do { \ + GC_TYPE_INFO(p) &= ~((flags) << GC_FLAGS_SHIFT); \ + } while (0) + +#ifndef ZEND_RC_DEBUG +# define ZEND_RC_DEBUG 0 +#endif + +#if ZEND_RC_DEBUG +extern ZEND_API bool zend_rc_debug; +/* The GC_PERSISTENT flag is reused for IS_OBJ_WEAKLY_REFERENCED on objects. + * Skip checks for OBJECT/NULL type to avoid interpreting the flag incorrectly. */ +# define ZEND_RC_MOD_CHECK(p) do { \ + if (zend_rc_debug) { \ + uint8_t type = zval_gc_type((p)->u.type_info); \ + if (type != IS_OBJECT && type != IS_NULL) { \ + ZEND_ASSERT(!(zval_gc_flags((p)->u.type_info) & GC_IMMUTABLE)); \ + ZEND_ASSERT((zval_gc_flags((p)->u.type_info) & (GC_PERSISTENT|GC_PERSISTENT_LOCAL)) != GC_PERSISTENT); \ + } \ + } \ + } while (0) +# define GC_MAKE_PERSISTENT_LOCAL(p) do { \ + GC_ADD_FLAGS(p, GC_PERSISTENT_LOCAL); \ + } while (0) +#else +# define ZEND_RC_MOD_CHECK(p) \ + do { } while (0) +# define GC_MAKE_PERSISTENT_LOCAL(p) \ + do { } while (0) +#endif + +static zend_always_inline uint32_t zend_gc_refcount(const zend_refcounted_h *p) { + return p->refcount; +} + +static zend_always_inline uint32_t zend_gc_set_refcount(zend_refcounted_h *p, uint32_t rc) { + p->refcount = rc; + return p->refcount; +} + +static zend_always_inline uint32_t zend_gc_addref(zend_refcounted_h *p) { + ZEND_RC_MOD_CHECK(p); + return ++(p->refcount); +} + +static zend_always_inline void zend_gc_try_addref(zend_refcounted_h *p) { + if (!(p->u.type_info & GC_IMMUTABLE)) { + ZEND_RC_MOD_CHECK(p); + ++p->refcount; + } +} + +static zend_always_inline void zend_gc_try_delref(zend_refcounted_h *p) { + if (!(p->u.type_info & GC_IMMUTABLE)) { + ZEND_RC_MOD_CHECK(p); + --p->refcount; + } +} + +static zend_always_inline uint32_t zend_gc_delref(zend_refcounted_h *p) { + ZEND_ASSERT(p->refcount > 0); + ZEND_RC_MOD_CHECK(p); + return --(p->refcount); +} + +static zend_always_inline uint32_t zend_gc_addref_ex(zend_refcounted_h *p, uint32_t rc) { + ZEND_RC_MOD_CHECK(p); + p->refcount += rc; + return p->refcount; +} + +static zend_always_inline uint32_t zend_gc_delref_ex(zend_refcounted_h *p, uint32_t rc) { + ZEND_RC_MOD_CHECK(p); + p->refcount -= rc; + return p->refcount; +} + #define GC_REFCOUNT(p) zend_gc_refcount(&(p)->gc) #define GC_SET_REFCOUNT(p, rc) zend_gc_set_refcount(&(p)->gc, rc) #define GC_ADDREF(p) zend_gc_addref(&(p)->gc) @@ -754,36 +864,6 @@ static zend_always_inline uint8_t zval_get_type(const zval* pz) { } \ } while (0) -#define GC_TYPE_MASK 0x0000000f -#define GC_FLAGS_MASK 0x000003f0 -#define GC_INFO_MASK 0xfffffc00 -#define GC_FLAGS_SHIFT 0 -#define GC_INFO_SHIFT 10 - -static zend_always_inline uint8_t zval_gc_type(uint32_t gc_type_info) { - return (gc_type_info & GC_TYPE_MASK); -} - -static zend_always_inline uint32_t zval_gc_flags(uint32_t gc_type_info) { - return (gc_type_info >> GC_FLAGS_SHIFT) & (GC_FLAGS_MASK >> GC_FLAGS_SHIFT); -} - -static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { - return (gc_type_info >> GC_INFO_SHIFT); -} - -#define GC_TYPE_INFO(p) (p)->gc.u.type_info -#define GC_TYPE(p) zval_gc_type(GC_TYPE_INFO(p)) -#define GC_FLAGS(p) zval_gc_flags(GC_TYPE_INFO(p)) -#define GC_INFO(p) zval_gc_info(GC_TYPE_INFO(p)) - -#define GC_ADD_FLAGS(p, flags) do { \ - GC_TYPE_INFO(p) |= (flags) << GC_FLAGS_SHIFT; \ - } while (0) -#define GC_DEL_FLAGS(p, flags) do { \ - GC_TYPE_INFO(p) &= ~((flags) << GC_FLAGS_SHIFT); \ - } while (0) - #define Z_GC_TYPE(zval) GC_TYPE(Z_COUNTED(zval)) #define Z_GC_TYPE_P(zval_p) Z_GC_TYPE(*(zval_p)) @@ -795,13 +875,6 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_GC_TYPE_INFO(zval) GC_TYPE_INFO(Z_COUNTED(zval)) #define Z_GC_TYPE_INFO_P(zval_p) Z_GC_TYPE_INFO(*(zval_p)) -/* zval_gc_flags(zval.value->gc.u.type_info) (common flags) */ -#define GC_NOT_COLLECTABLE (1<<4) -#define GC_PROTECTED (1<<5) /* used for recursion detection */ -#define GC_IMMUTABLE (1<<6) /* can't be changed in place */ -#define GC_PERSISTENT (1<<7) /* allocated using malloc */ -#define GC_PERSISTENT_LOCAL (1<<8) /* persistent, but thread-local */ - #define GC_NULL (IS_NULL | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT)) #define GC_STRING (IS_STRING | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT)) #define GC_ARRAY IS_ARRAY @@ -1299,79 +1372,6 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_TRY_ADDREF(z) Z_TRY_ADDREF_P(&(z)) #define Z_TRY_DELREF(z) Z_TRY_DELREF_P(&(z)) -#ifndef ZEND_RC_DEBUG -# define ZEND_RC_DEBUG 0 -#endif - -#if ZEND_RC_DEBUG -extern ZEND_API bool zend_rc_debug; -/* The GC_PERSISTENT flag is reused for IS_OBJ_WEAKLY_REFERENCED on objects. - * Skip checks for OBJECT/NULL type to avoid interpreting the flag incorrectly. */ -# define ZEND_RC_MOD_CHECK(p) do { \ - if (zend_rc_debug) { \ - uint8_t type = zval_gc_type((p)->u.type_info); \ - if (type != IS_OBJECT && type != IS_NULL) { \ - ZEND_ASSERT(!(zval_gc_flags((p)->u.type_info) & GC_IMMUTABLE)); \ - ZEND_ASSERT((zval_gc_flags((p)->u.type_info) & (GC_PERSISTENT|GC_PERSISTENT_LOCAL)) != GC_PERSISTENT); \ - } \ - } \ - } while (0) -# define GC_MAKE_PERSISTENT_LOCAL(p) do { \ - GC_ADD_FLAGS(p, GC_PERSISTENT_LOCAL); \ - } while (0) -#else -# define ZEND_RC_MOD_CHECK(p) \ - do { } while (0) -# define GC_MAKE_PERSISTENT_LOCAL(p) \ - do { } while (0) -#endif - -static zend_always_inline uint32_t zend_gc_refcount(const zend_refcounted_h *p) { - return p->refcount; -} - -static zend_always_inline uint32_t zend_gc_set_refcount(zend_refcounted_h *p, uint32_t rc) { - p->refcount = rc; - return p->refcount; -} - -static zend_always_inline uint32_t zend_gc_addref(zend_refcounted_h *p) { - ZEND_RC_MOD_CHECK(p); - return ++(p->refcount); -} - -static zend_always_inline void zend_gc_try_addref(zend_refcounted_h *p) { - if (!(p->u.type_info & GC_IMMUTABLE)) { - ZEND_RC_MOD_CHECK(p); - ++p->refcount; - } -} - -static zend_always_inline void zend_gc_try_delref(zend_refcounted_h *p) { - if (!(p->u.type_info & GC_IMMUTABLE)) { - ZEND_RC_MOD_CHECK(p); - --p->refcount; - } -} - -static zend_always_inline uint32_t zend_gc_delref(zend_refcounted_h *p) { - ZEND_ASSERT(p->refcount > 0); - ZEND_RC_MOD_CHECK(p); - return --(p->refcount); -} - -static zend_always_inline uint32_t zend_gc_addref_ex(zend_refcounted_h *p, uint32_t rc) { - ZEND_RC_MOD_CHECK(p); - p->refcount += rc; - return p->refcount; -} - -static zend_always_inline uint32_t zend_gc_delref_ex(zend_refcounted_h *p, uint32_t rc) { - ZEND_RC_MOD_CHECK(p); - p->refcount -= rc; - return p->refcount; -} - static zend_always_inline uint32_t zval_refcount_p(const zval* pz) { #if ZEND_DEBUG ZEND_ASSERT(Z_REFCOUNTED_P(pz) || Z_TYPE_P(pz) == IS_ARRAY); diff --git a/ext/date/tests/gh-124.phpt b/ext/date/tests/gh-124.phpt index 074e519d4399..30cccd74ef1f 100644 --- a/ext/date/tests/gh-124.phpt +++ b/ext/date/tests/gh-124.phpt @@ -4,7 +4,7 @@ Test for timelib #124: Problem with large negative timestamps date.timezone=UTC --SKIPIF-- --FILE-- diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 1b8bd1f76784..58e71daef78c 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -3750,7 +3750,7 @@ static void exif_process_TIFF_in_JPEG(image_info_type *ImageInfo, char *CharBuf, static void exif_process_APP1(image_info_type *ImageInfo, char *CharBuf, size_t length, size_t displacement) { /* Check the APP1 for Exif Identifier Code */ - static const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; + static const uchar ExifHeader[] = {'E', 'x', 'i', 'f', 0, 0}; if (length <= 8 || memcmp(CharBuf+2, ExifHeader, 6)) { exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Incorrect APP1 Exif Identifier Code"); return; @@ -4447,8 +4447,7 @@ static bool exif_scan_HEIF_header(image_info_type *ImageInfo, unsigned char *buf static bool exif_scan_WEBP_header(image_info_type *ImageInfo, size_t riff_size) { - /* "Exif\0\0" identifier code */ - static const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; + static const uchar ExifHeader[] = {'E', 'x', 'i', 'f', 0, 0}; unsigned char chunk_header[8]; size_t offset = 12; size_t riff_end = riff_size <= ImageInfo->FileSize - 8 ? riff_size + 8 : ImageInfo->FileSize; diff --git a/ext/ffi/tests/gh10403.phpt b/ext/ffi/tests/gh10403.phpt index 6bac0da92584..1c5c69113eef 100644 --- a/ext/ffi/tests/gh10403.phpt +++ b/ext/ffi/tests/gh10403.phpt @@ -3,7 +3,7 @@ GH-10403: Fix incorrect bitshifting and masking in ffi bitfield --EXTENSIONS-- ffi --SKIPIF-- - + --FILE-- --FILE-- load(...)); + var_dump(spl_autoload_unregister($this->load(...))); + spl_autoload_call('MissingDirect'); + + spl_autoload_register($this->missing(...)); + var_dump(spl_autoload_unregister($this->missing(...))); + spl_autoload_call('MissingTrampoline'); + + spl_autoload_register($this->load(...)); + var_dump(spl_autoload_unregister([$this, 'load'])); + spl_autoload_call('MissingArray'); + + spl_autoload_register($this(...)); + var_dump(spl_autoload_unregister($this)); + spl_autoload_call('MissingInvokable'); + } +} + +spl_autoload_register(autoload_string(...)); +var_dump(spl_autoload_unregister('autoload_string')); +spl_autoload_call('MissingString'); + +(new AutoloadTest())->run(); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/standard/tests/GHSA-96wq-48vp-hh57.phpt b/ext/standard/tests/GHSA-96wq-48vp-hh57.phpt index cf9a40062f84..f8a01f48a115 100644 --- a/ext/standard/tests/GHSA-96wq-48vp-hh57.phpt +++ b/ext/standard/tests/GHSA-96wq-48vp-hh57.phpt @@ -8,7 +8,7 @@ memory_limit=3G --FILE-- direct++; + } + + public function arrayCallable(): void + { + $this->array++; + } + + public function __invoke(): void + { + $this->invoke++; + } + + public function __call(string $name, array $arguments): void + { + $this->trampoline++; + } + + public function run(): void + { + register_tick_function($this->kick(...)); + unregister_tick_function($this->kick(...)); + echo "direct: {$this->direct}\n"; + + register_tick_function($this->missing(...)); + unregister_tick_function($this->missing(...)); + echo "trampoline: {$this->trampoline}\n"; + + register_tick_function($this->arrayCallable(...)); + unregister_tick_function([$this, 'arrayCallable']); + echo "array: {$this->array}\n"; + + register_tick_function($this(...)); + unregister_tick_function($this); + echo "invoke: {$this->invoke}\n"; + } +} + +register_tick_function(tick_string(...)); +unregister_tick_function('tick_string'); +echo "string: {$string}\n"; + +(new TickTest())->run(); +echo "done\n"; +?> +--EXPECT-- +string: 1 +direct: 1 +trampoline: 1 +array: 1 +invoke: 1 +done