Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/debug_info/debug_info-error-0.0.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion Zend/tests/debug_info/debug_info-error-0.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion Zend/tests/debug_info/debug_info-error-1.0.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion Zend/tests/debug_info/debug_info-error-1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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
20 changes: 20 additions & 0 deletions Zend/tests/debug_info/debug_info-error-case-insensitive.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
Testing __debugInfo() magic method declared with non-canonical case
--FILE--
<?php

class C {
public $val;
public function __DEBUGINFO() {
return $this->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
2 changes: 1 addition & 1 deletion Zend/tests/debug_info/debug_info-error-empty_str.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion Zend/tests/debug_info/debug_info-error-false.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion Zend/tests/debug_info/debug_info-error-object.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion Zend/tests/debug_info/debug_info-error-resource.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion Zend/tests/debug_info/debug_info-error-str.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion Zend/tests/debug_info/debug_info-error-true.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
Stringable is automatically implemented for __toString() declared with non-canonical case
--FILE--
<?php

class Test {
public function __TOSTRING() {
return "foo";
}
}

var_dump(new Test instanceof Stringable);
var_dump((new ReflectionClass(Test::class))->getInterfaceNames());
var_dump((string) new Test);

?>
--EXPECT--
bool(true)
array(1) {
[0]=>
string(10) "Stringable"
}
string(3) "foo"
70 changes: 58 additions & 12 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
Expand Down
11 changes: 9 additions & 2 deletions Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
;
}

Expand Down
4 changes: 2 additions & 2 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
16 changes: 12 additions & 4 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 | | | */
Expand Down Expand Up @@ -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 */
Expand Down
26 changes: 13 additions & 13 deletions Zend/zend_enum.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
12 changes: 0 additions & 12 deletions Zend/zend_operators.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 0 additions & 2 deletions Zend/zend_operators.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading
Loading