This is an old revision of the document!
PHP RFC: Deprecate and Remove Case-Insensitive Constants
- Date: 2018-06-24
- Author: Nikita Popov nikic@php.net
- Target PHP version: PHP 7.3
- Implementation: https://github.com/php/php-src/pull/3321
- Status: Under Discussion
Introduction
PHP currently supports both case-sensitive and case-insensitive constants. Case-insensitive constants see very little practical use, are subject to various inconsistencies in functionality and cause undue implementational complexity. This RFC proposes to deprecate and remove case-insensitive constants.
The current state of the matter is:
- Class constants are always case-sensitive.
- Global constants declared with
const
are always case-sensitive. It should be noted that this applies only to the shortname of the constant, while namespaces in PHP are always case-insensitive. - Constants declared with
define()
are case-sensitive by default. - It is possible to declare case-insensitive constants by passing
true
as the third parameter ofdefine()
.
This RFC proposes to:
- In PHP 7.3: Deprecate calling
define()
with third parametertrue
. - In PHP 7.3: Deprecate accessing a case-insensitive constant with a casing that differs from the declaration-site.
- In PHP 8.0: Remove the possibility of declaring case-insensitive constants.
- The
true
,false
andnull
constants continue to be case-insensitive. The exact way in which they are handled is still up to discussion.
Motivation
Symbols in PHP don't use consistent casing rules. Some symbols such as function names are case-insensitive, while others such as variable names are case-sensitive. The problem with constants is that they can be both. Apart from unnecessarily complicating the language, this also causes various issues outlined in the following.
Aliasing is case-sensitive
PHP supports the use const NS\FOO
syntax for importing constants in namespaced code. Because aliasing (and namespaces in general) is purely compile-time functionality, this syntax cannot depend on whether the constant NS\FOO
is case-sensitive or case-insensitive, as this is only decided at run-time. As such, the syntax has to choose one way or the other.
Because constants in PHP are predominantly case-sensitive, the use const
syntax assumes case-sensitivity. Of course, this means that the functionality will not work correctly with case-insensitive constants:
namespace { define('NS\FOO', 42, true); // Case-insensitive constant } namespace Test { use const NS\FOO; var_dump(FOO); // Works var_dump(foo); // Warning: Use of undefined constant foo }
This is a fundamental issue that cannot be resolved.
Constant redeclarations
Constants are supposed to be constant, i.e., their value may not change after initial declaration. For this reason PHP makes sure that constants are not redeclared:
define('FOO', 42); var_dump(FOO); // int(42) define('FOO', 24); // Notice: Constant FOO already defined var_dump(FOO); // int(42)
These checks are not performed (or only in some very narrow cases) if case-sensitive and case-insensitive constants are mixed:
define('foo', 42, true); var_dump(FOO); // int(42); define('FOO', 24); var_dump(FOO); // int(24)
Not only was the declaration of a clashing constant permitted, but it effectively changed the value of the FOO
constant.
This problem is further confounded by assumptions (such as: constants are constant) in the PHP engine and opcache optimizations, resulting in additional issues like bug #74450.
This is an issue that can in principle be resolved, however it would come with significant additional implementation complexity and a hit to performance and memory usage. At the least, it would require storing lower-cased variants of all constants and checking against them on new constant declarations.
Implementation complexity and overhead
Support for case-insensitive constants makes the implementation more complex and slower. Constant lookups are implemented by first looking up the constant name directly, and then looking up a lowercased variant.
A particularly extreme case are access to unqualified constants inside namespaces. For example, if constant FOO
is accessed inside namespace NS
, the FETCH_CONST
opcode is created with a record-breaking five literals. In order, ns\FOO
, ns\foo
, FOO
and foo
need to be looked up, and finally NS\FOO
is used for error-reporting. For the common case where the intended constant was FOO
this results in three lookups. Thankfully the impact is mitigated by runtime caching (which is actually incorrect due to the previous point).
Proposal
In PHP 7.3 both the declaration of case-insensitive constants, as well as their access (with a name different from the declared one) will result in a deprecation warning:
define('FOO', 42, true); // Deprecated: define(): Declaration of case-insensitive constants is deprecated var_dump(FOO); // Ok! var_dump(foo); // Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "FOO"
The defined()
function is not affected. It will continue to return true
for case-insensitive constants, without generating a deprecation warning.
Declaration of case-insensitive constants by extensions will not generate a deprecation warning (though their access will). The reason behind this is that the end-user will not be able to do anything about this deprecation warning, while the extension maintainer may not be able to change the declaration for BC reasons at this point.
The reason why both declaration and access generate deprecation warnings is that both may generally fall into the responsibility of different maintainers. While accesses are performed by library users and can always be trivially fixed, definitions may be part of libraries that cannot immediately switch to case-sensitive constants due to backwards compatibility guarantees.
In PHP 8 the ability to declare case-insensitive constants will be removed.
Handling of true, false and null
true
, false
and null
in PHP are originally “ordinary” case-insensitive constants, though in practice they are subject to various special casing. For example, these constants are not subject to namespace fallback, as we must be able to resolve their values at compile-time. (The are however subject to aliasing.)
There are at least two ways in which we can handle these special constants:
The first is to retain them as special case-insensitive constants. Given all the other special casing they receive, this would not be particularly unusual, though it would require us to introduce or keep special checks for these particular constants.
The second is to turn them into reserved keywords. This would be the more principled approach, but would also be a slightly larger BC break. It particular it would mean that constant(“null”)
and similar will no longer work and that true
, false
and null
can no longer be used as identifiers. It should be noted though that their use as identifiers is already limited, as they are reserved class names.
Backward Incompatible Changes
Additional deprecation warnings are thrown in PHP 7.3. Case-insensitive constants are removed in PHP 8.
Depending on the choice regarding true
, false
and null
, they may no longer be accessible as constants and may no longer be used as identifiers.
Unaffected PHP Functionality
Magic constants are not affected. These are already reserved keywords (always case-insensitive), not accessible via constant()
, etc.
Vote
Since this is a language change, a 2/3 majority is required.