Commit 0beb2a99d3b for php.net

commit 0beb2a99d3beb586a1ee5a2c768e43a9218be2a1
Author: Gina Peter Banyard <girgias@php.net>
Date:   Wed Sep 9 16:08:18 2026 +0100

    Zend: deprecate using "_" as a class and constant name (#23615)

    RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_using_as_a_constant_and_compile_time_alias

    Co-authored-by: Tim Düsterhus <timwolla@googlemail.com>

diff --git a/Zend/tests/deprecated_identifiers/underscore/class_alias_underscore.phpt b/Zend/tests/deprecated_identifiers/underscore/class_alias_underscore.phpt
new file mode 100644
index 00000000000..23afcbfeb92
--- /dev/null
+++ b/Zend/tests/deprecated_identifiers/underscore/class_alias_underscore.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Using "_" as a class name alias is deprecated
+--FILE--
+<?php
+
+class_alias('stdClass', '_');
+
+class_alias('stdClass', 'Foo\\_');
+
+?>
+--EXPECTF--
+Deprecated: Using "_" as a class alias is deprecated since 8.4 in %s on line %d
+
+Deprecated: Using "_" as a class alias is deprecated since 8.4 in %s on line %d
diff --git a/Zend/tests/class_underscore_as_name.phpt b/Zend/tests/deprecated_identifiers/underscore/class_underscore_as_name.phpt
similarity index 100%
rename from Zend/tests/class_underscore_as_name.phpt
rename to Zend/tests/deprecated_identifiers/underscore/class_underscore_as_name.phpt
diff --git a/Zend/tests/deprecated_identifiers/underscore/class_underscore_as_name_use.phpt b/Zend/tests/deprecated_identifiers/underscore/class_underscore_as_name_use.phpt
new file mode 100644
index 00000000000..685250b369a
--- /dev/null
+++ b/Zend/tests/deprecated_identifiers/underscore/class_underscore_as_name_use.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Using "_" as a class name in use statements is deprecated
+--FILE--
+<?php
+
+namespace Foo\Bar {
+    use stdClass as _;
+}
+
+namespace {
+    use stdClass as _;
+}
+
+?>
+--EXPECTF--
+Deprecated: Using "_" as a class name is deprecated in %s on line %d
+
+Deprecated: Using "_" as a class name is deprecated in %s on line %d
diff --git a/Zend/tests/deprecated_identifiers/underscore/constant_underscore_as_name.phpt b/Zend/tests/deprecated_identifiers/underscore/constant_underscore_as_name.phpt
new file mode 100644
index 00000000000..4859e8ca5ae
--- /dev/null
+++ b/Zend/tests/deprecated_identifiers/underscore/constant_underscore_as_name.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Using "_" as a constant name is deprecated
+--FILE--
+<?php
+
+namespace Foo\Bar {
+    const _ = 5;
+}
+
+namespace {
+    const _ = 5;
+
+    define('_', 'x');
+
+    define('foo\\_', 'x');
+}
+
+?>
+--EXPECTF--
+Deprecated: Calling a constant "_" is deprecated since 8.6 in %s on line %d
+
+Deprecated: Calling a constant "_" is deprecated since 8.6 in %s on line %d
+
+Deprecated: Calling a constant "_" is deprecated since 8.6 in %s on line %d
+
+Warning: Constant _ already defined, this will be an error in PHP 9 in %s on line %d
+
+Deprecated: Calling a constant "_" is deprecated since 8.6 in %s on line %d
diff --git a/Zend/tests/deprecated_identifiers/underscore/constant_underscore_as_name_use.phpt b/Zend/tests/deprecated_identifiers/underscore/constant_underscore_as_name_use.phpt
new file mode 100644
index 00000000000..e7421318aea
--- /dev/null
+++ b/Zend/tests/deprecated_identifiers/underscore/constant_underscore_as_name_use.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Using "_" as a constant in use statements is deprecated
+--FILE--
+<?php
+
+namespace Foo\Bar {
+    use const PHP_INT_MAX as _;
+}
+
+namespace {
+    use const PHP_INT_MAX as _;
+}
+
+?>
+--EXPECTF--
+Deprecated: Using "_" as a constant name is deprecated since 8.6 in %s on line %d
+
+Deprecated: Using "_" as a constant name is deprecated since 8.6 in %s on line %d
diff --git a/Zend/tests/deprecated_identifiers/underscore/function_underscore_as_name.phpt b/Zend/tests/deprecated_identifiers/underscore/function_underscore_as_name.phpt
new file mode 100644
index 00000000000..0d127fab630
--- /dev/null
+++ b/Zend/tests/deprecated_identifiers/underscore/function_underscore_as_name.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Using "_" as a function name is NOT deprecated
+--SKIPIF--
+<?php
+if (extension_loaded('gettext')) {
+    die("skip gettext extension defines the _ function");
+}
+?>
+--FILE--
+<?php
+
+namespace Foo\Bar {
+    function _() {}
+}
+
+namespace {
+    function _() {}
+    echo "OK";
+}
+
+?>
+--EXPECT--
+OK
diff --git a/Zend/tests/deprecated_identifiers/underscore/function_underscore_as_name_use.phpt b/Zend/tests/deprecated_identifiers/underscore/function_underscore_as_name_use.phpt
new file mode 100644
index 00000000000..688683a6039
--- /dev/null
+++ b/Zend/tests/deprecated_identifiers/underscore/function_underscore_as_name_use.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Using "_" as a function in use statements is NOT deprecated
+--FILE--
+<?php
+
+namespace Foo\Bar {
+    use function strlen as _;
+}
+
+namespace {
+    use function strlen as _;
+
+    echo "OK";
+}
+
+?>
+--EXPECT--
+OK
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 317114265c5..5aa1b07f304 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -252,7 +252,7 @@ void zend_assert_valid_class_name(const zend_string *name, const char *type) /*
 		zend_error_noreturn(E_COMPILE_ERROR,
 			"Cannot use \"%s\" as %s as it is reserved", ZSTR_VAL(name), type);
 	}
-	if (zend_string_equals_literal(name, "_")) {
+	if (zend_string_equals_literal(name, "_") || zend_string_ends_with_literal(name, "\\_")) {
 		zend_error(E_DEPRECATED, "Using \"_\" as %s is deprecated since 8.4", type);
 	}
 }
@@ -10213,6 +10213,20 @@ static void zend_compile_use(zend_ast *ast) /* {{{ */
 				"is a special class name", ZSTR_VAL(old_name), ZSTR_VAL(new_name), ZSTR_VAL(new_name));
 		}

+		if (zend_string_equals(new_name, ZSTR_CHAR('_'))) {
+			switch (type) {
+				case ZEND_SYMBOL_CLASS:
+					zend_error(E_DEPRECATED, "Using \"_\" as a class name is deprecated");
+					break;
+				case ZEND_SYMBOL_CONST:
+					zend_error(E_DEPRECATED, "Using \"_\" as a constant name is deprecated since 8.6");
+					break;
+				case ZEND_SYMBOL_FUNCTION:
+					break;
+				default: ZEND_UNREACHABLE();
+			}
+		}
+
 		if (current_ns) {
 			zend_string *ns_name = zend_string_alloc(ZSTR_LEN(current_ns) + 1 + ZSTR_LEN(new_name), 0);
 			zend_str_tolower_copy(ZSTR_VAL(ns_name), ZSTR_VAL(current_ns), ZSTR_LEN(current_ns));
diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c
index 18292203bee..c8a2ae62d75 100644
--- a/Zend/zend_constants.c
+++ b/Zend/zend_constants.c
@@ -538,6 +538,12 @@ ZEND_API zend_constant *zend_register_constant(zend_constant *c)

 	c->attributes = NULL;

+	if (
+		zend_string_equals(name, ZSTR_CHAR('_'))
+		|| (slash && zend_string_ends_with_literal(name, "\\_"))
+	) {
+		zend_error(E_DEPRECATED, "Calling a constant \"_\" is deprecated since 8.6");
+	}
 	/* Check if the user is trying to define any special constant */
 	if (zend_string_equals_literal(name, "__COMPILER_HALT_OFFSET__")
 		|| (!persistent && zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name)))