Skip to content
Draft
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ PHP NEWS
?? ??? ????, PHP 8.6.0alpha1

- Core:
. PHP is now case-sensitive for function and class names. Calling a function
using incorrect casing (e.g. STRLEN() instead of strlen()) now raises a
fatal Error. (jorgsowa)
. Class, interface, trait, and enum names are now stored and looked up using
their canonical casing. Referencing them with incorrect casing now raises a
fatal Error across all language constructs and APIs that accept a class
name. (jorgsowa)
. Namespace segments in references and use imports are now matched
case-sensitively. Using incorrect casing raises a fatal Error. (jorgsowa)
. Added first-class callable cache to share instances for the duration of the
request. (ilutov)
. It is now possible to use reference assign on WeakMap without the key
Expand Down
45 changes: 45 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,51 @@ PHP 8.6 UPGRADE NOTES
1. Backward Incompatible Changes
========================================

- Core:
. PHP is now case-sensitive for function and class names. The global function
table and class table are now keyed by the canonical (declared) name rather
than its lowercase equivalent. As a result, calling a function with
incorrect casing (e.g. STRLEN() instead of strlen()) now raises a fatal
Error ("Call to undefined function STRLEN()"), and referencing a class,
interface, trait, or enum with incorrect casing (e.g. new FOO() when the
class is declared as Foo) now raises a fatal Error ("Class 'FOO' not
found"). This applies to all language constructs and APIs that perform a
name lookup: new, instanceof, catch, extends, implements, use (trait), type
declarations, callable strings/arrays, class_exists() and related
introspection functions, Closure::bind()/bindTo(), Reflection constructors
and methods, and the SOAP classmap option.
RFC: https://wiki.php.net/rfc/case_sensitive_php
. Namespace segments in use imports are now matched case-sensitively. A use
import whose path does not match the canonical casing of the target
namespace segment will produce a fatal Error when the imported name is
first resolved.
RFC: https://wiki.php.net/rfc/case_sensitive_php
. Declaring a magic method with incorrect casing (e.g. __tostring() instead
of __toString()) is now a compile-time fatal Error. Previously magic
method names were matched case-insensitively; under case-sensitive method
names a wrong-cased declaration would otherwise silently lose its magic
behavior.
RFC: https://wiki.php.net/rfc/case_sensitive_php
. Attribute names are now matched case-sensitively, consistent with class
names. Internal attributes such as #[\Deprecated], #[\Override] or
#[\AllowDynamicProperties] are only recognized with their exact casing,
and the name filter of ReflectionFunctionAbstract::getAttributes() and
related methods compares case-sensitively. The "self", "parent" and
"static" keywords in callable strings remain case-insensitive.
RFC: https://wiki.php.net/rfc/case_sensitive_php
. The namespace portion of constant names is now case-sensitive, matching
the constant base name. Referencing a namespaced constant with incorrect
namespace casing (e.g. myapp\FOO when defined as MyApp\FOO) now raises
an "Undefined constant" Error, including via constant() and defined().
RFC: https://wiki.php.net/rfc/case_sensitive_php

- SOAP:
. WSDL operation names are now matched case-sensitively when dispatching
requests and when calling operations through SoapClient. This matches
XML, where element names are case-sensitive. SoapServer::addFunction()
also requires the exact case of the registered PHP function.
RFC: https://wiki.php.net/rfc/case_sensitive_php

- DOM:
. Properties previously documented as @readonly (e.g. DOMNode::$nodeType,
DOMDocument::$xmlEncoding, DOMEntity::$actualEncoding, ::$encoding,
Expand Down
24 changes: 12 additions & 12 deletions Zend/Optimizer/compact_literals.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,43 +134,43 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
LITERAL_INFO(opline->op1.constant, 1);
break;
case ZEND_INIT_FCALL_BY_NAME:
LITERAL_INFO(opline->op2.constant, 2);
LITERAL_INFO(opline->op2.constant, 1);
break;
case ZEND_INIT_NS_FCALL_BY_NAME:
LITERAL_INFO(opline->op2.constant, 3);
LITERAL_INFO(opline->op2.constant, 2);
break;
case ZEND_INIT_METHOD_CALL:
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, 1);
}
if (opline->op2_type == IS_CONST) {
LITERAL_INFO(opline->op2.constant, 2);
LITERAL_INFO(opline->op2.constant, 1);
}
break;
case ZEND_INIT_STATIC_METHOD_CALL:
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, 2);
LITERAL_INFO(opline->op1.constant, 1);
}
if (opline->op2_type == IS_CONST) {
LITERAL_INFO(opline->op2.constant, 2);
LITERAL_INFO(opline->op2.constant, 1);
}
break;
case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL:
LITERAL_INFO(opline->op1.constant, 1);
break;
case ZEND_CATCH:
LITERAL_INFO(opline->op1.constant, 2);
LITERAL_INFO(opline->op1.constant, 1);
break;
case ZEND_FETCH_CONSTANT:
if (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
LITERAL_INFO(opline->op2.constant, 3);
} else {
LITERAL_INFO(opline->op2.constant, 2);
} else {
LITERAL_INFO(opline->op2.constant, 1);
}
break;
case ZEND_FETCH_CLASS_CONSTANT:
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, 2);
LITERAL_INFO(opline->op1.constant, 1);
}
if (opline->op2_type == IS_CONST) {
LITERAL_INFO(opline->op2.constant, 1);
Expand All @@ -192,7 +192,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
case ZEND_POST_DEC_STATIC_PROP:
case ZEND_ASSIGN_STATIC_PROP_OP:
if (opline->op2_type == IS_CONST) {
LITERAL_INFO(opline->op2.constant, 2);
LITERAL_INFO(opline->op2.constant, 1);
}
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, 1);
Expand All @@ -201,12 +201,12 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
case ZEND_FETCH_CLASS:
case ZEND_INSTANCEOF:
if (opline->op2_type == IS_CONST) {
LITERAL_INFO(opline->op2.constant, 2);
LITERAL_INFO(opline->op2.constant, 1);
}
break;
case ZEND_NEW:
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, 2);
LITERAL_INFO(opline->op1.constant, 1);
}
break;
case ZEND_DECLARE_CLASS:
Expand Down
6 changes: 2 additions & 4 deletions Zend/Optimizer/dfa_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,7 @@ static inline bool can_elide_list_type(
return can_elide_list_type(script, op_array, use_info, *single_type);
}
if (ZEND_TYPE_HAS_NAME(*single_type)) {
zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(*single_type));
const zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, lcname);
zend_string_release(lcname);
const zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, ZEND_TYPE_NAME(*single_type));
bool result = ce && safe_instanceof(use_info->ce, ce);
if (result == !is_intersection) {
return result;
Expand Down Expand Up @@ -410,7 +408,7 @@ static uint32_t zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
if ((op->opcode == ZEND_FRAMELESS_ICALL_2
|| (op->opcode == ZEND_FRAMELESS_ICALL_3 && (op + 1)->op1_type == IS_CONST))
&& call_info->callee_func
&& zend_string_equals_literal_ci(call_info->callee_func->common.function_name, "in_array")) {
&& zend_string_equals_literal(call_info->callee_func->common.function_name, "in_array")) {
bool strict = false;
bool has_opdata = op->opcode == ZEND_FRAMELESS_ICALL_3;
ZEND_ASSERT(!call_info->is_prototype);
Expand Down
15 changes: 10 additions & 5 deletions Zend/Optimizer/optimize_func_calls.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,19 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
} else if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) {
fcall->opcode = ZEND_INIT_FCALL;
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
literal_dtor(&ZEND_OP2_LITERAL(fcall));
fcall->op2.constant = fcall->op2.constant + 1;
} else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
fcall->opcode = ZEND_INIT_FCALL;
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
literal_dtor(&op_array->literals[fcall->op2.constant]);
literal_dtor(&op_array->literals[fcall->op2.constant + 2]);
fcall->op2.constant = fcall->op2.constant + 1;
/* slot 0 = ns-qualified name, slot 1 = unqualified fallback */
zval *ns_slot = &op_array->literals[fcall->op2.constant];
zval *unq_slot = &op_array->literals[fcall->op2.constant + 1];
if (!zend_hash_find(EG(function_table), Z_STR_P(ns_slot))
&& Z_TYPE_P(unq_slot) == IS_STRING) {
literal_dtor(ns_slot);
fcall->op2.constant++;
} else {
literal_dtor(&op_array->literals[fcall->op2.constant + 1]);
}
} else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
|| fcall->opcode == ZEND_INIT_METHOD_CALL
|| fcall->opcode == ZEND_INIT_PARENT_PROPERTY_HOOK_CALL
Expand Down
2 changes: 1 addition & 1 deletion Zend/Optimizer/pass1.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
}

/* define("name", scalar); */
if (zend_string_equals_literal_ci(Z_STR(ZEND_OP2_LITERAL(init_opline)), "define")) {
if (zend_string_equals_literal(Z_STR(ZEND_OP2_LITERAL(init_opline)), "define")) {

if (Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING && send2_opline) {

Expand Down
8 changes: 3 additions & 5 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -2387,10 +2387,8 @@ static uint32_t zend_convert_type(const zend_script *script, zend_type type, zen
/* As we only have space to store one CE,
* we use a plain object type for class unions. */
if (ZEND_TYPE_HAS_NAME(type)) {
zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(type));
// TODO: Pass through op_array.
*pce = zend_optimizer_get_class_entry(script, NULL, lcname);
zend_string_release_ex(lcname, 0);
*pce = zend_optimizer_get_class_entry(script, NULL, ZEND_TYPE_NAME(type));
}
}
}
Expand Down Expand Up @@ -2477,7 +2475,7 @@ static const zend_property_info *zend_fetch_static_prop_info(const zend_script *
}
} else if (opline->op2_type == IS_CONST) {
const zval *zv = CRT_CONSTANT(opline->op2);
ce = zend_optimizer_get_class_entry(script, op_array, Z_STR_P(zv + 1));
ce = zend_optimizer_get_class_entry(script, op_array, Z_STR_P(zv));
}

if (ce) {
Expand Down Expand Up @@ -3369,7 +3367,7 @@ static zend_always_inline zend_result _zend_update_type_info(
} else if (opline->op2_type == IS_CONST) {
zval *zv = CRT_CONSTANT(opline->op2);
if (Z_TYPE_P(zv) == IS_STRING) {
ce = zend_optimizer_get_class_entry(script, op_array, Z_STR_P(zv+1));
ce = zend_optimizer_get_class_entry(script, op_array, Z_STR_P(zv));
UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_op->result_def);
} else {
UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def);
Expand Down
Loading
Loading