rfc:deprecations_php_8_6

PHP RFC: Deprecations for PHP 8.6

Introduction

The RFC proposes to deprecate the listed functionality in PHP 8.6 and remove it in PHP 9 (except where otherwise noted).

The following list provides a short overview of the functionality targeted for deprecation, while more detailed explanation is provided in the Proposal section:

  • Deprecate the `list()` construct
  • Deprecate returning from a finally block
  • Deprecate changing by-reference return modifier via inheritance
  • Deprecate using certain names as identifiers to reserve them as keywords
    • Deprecate using “let” as an identifier
    • Deprecate using “in”, “out”, and “inout” as identifiers
    • Deprecate the possibility to name a function “readonly”
    • Deprecate using “_” as an identifier
    • Deprecate the _() function alias for gettext()
  • Passing objects which are interpreted as arrays
    • Passing objects for $array parameter of array_walk() and array_walk_recursive()
    • Passing objects for $options parameter of deflate_init() and inflate_init()
    • Passing objects as parameters to the zlib.inflate and zlib.deflate stream filters
    • Passing objects as parameters to the bzip2.decompress and bzip2.compress stream filters
    • Passing objects for $vars parameter of mb_convert_variables()
    • Passing objects for $data parameter of http_build_query()
  • Deprecate function aliases with non-canonical type name
  • Deprecate strcoll()
  • Deprecate SORT_LOCALE_STRING flag for sort() functions
  • Deprecate mysqli::stmt_init()
  • Deprecate mysqli_get_charset()
  • Deprecate ArrayIterator methods that inherit ArrayObject implementation
  • Deprecate metaphone function
  • Deprecate passing a $sessionhandler object to session_set_save_handler() which does not contain the create_sid() and validateId() methods
  • Deprecate spl_classes()
  • Deprecate is_subclass_of with string when $allow_string is false
  • Deprecate is_a with string when $allow_string is false
  • Deprecate define() with $case_insensitive being specified
  • Deprecate spl_object_hash
  • Deprecate ReflectionProperty::setValue() and ReflectionProperty::setRawValue() with wrong types
  • Deprecate using “namespace” as a class constant or static property name

Proposal

Each feature proposed for deprecation is voted separately and requires a 2/3 majority. All votes refer to deprecation in PHP 8.6 and removal in PHP 9 (except where otherwise noted).

Example Deprecation

Description

Deprecate the `list()` construct

The list() construct destructures an array into individual variables. It may appear only on the left-hand side of an assignment or as the target of a foreach:

list($a, $b) = [1, 2];
 
foreach ($pairs as list($key, $value)) {
    // ...
}

Since PHP 7.1, the short syntax [$a, $b] does exactly the same thing in exactly the same positions, with identical semantics for nesting, skipped elements, and keyed destructuring:

[$a, $b] = [1, 2];
 
foreach ($pairs as [$key, $value]) {
    // ...
}

The two forms are interchangeable. list() adds no capability the short syntax lacks; it is a longer spelling of the same operation, kept only for backward compatibility. Having two ways to write the same destructuring is a cost with no return: it is more for newcomers to learn, more for tooling to special-case, and a recurring source of “which one should I use” with no real answer.

The name itself is misleading. Set next to array("a" => $a, "b" => $b), the construct list($a, $b) reads as though it builds some kind of list value, a sibling of array(). It does no such thing. It constructs nothing; it pulls values out of an existing array and binds them to variables. The name implies creation where the operation is the exact opposite, which is precisely backwards for someone meeting it for the first time.

list() also looks like a function call but is not one. It cannot be assigned to a variable, passed as a callable, or used anywhere other than the two assignment positions above. Despite this, it occupies the list keyword, which therefore cannot be used as a function name, or class-like name. Reclaiming it would free list for ordinary use, or leave it available should a future list type ever be proposed.

This proposal adds a deprecation at compile time wherever the list() construct is used. The short array syntax is unaffected, runtime behaviour does not change, and every existing use can be rewritten mechanically by replacing list(...) with [...].

The goal is removal in the next major version, freeing the list keyword entirely.

Migration is automatable. The entire transformation can be applied across a codebase with a single command:

ast-grep run --pattern 'list($$$ARGS)' --rewrite '[$$$ARGS]' --lang php -U

Other tools such as Rector can perform the same migration.

If accepted, use of the list() construct emits the following deprecation notice at compile time.

Deprecated: The list() construct is deprecated, use [...] instead

Deprecate returning from a finally block

When a try or catch block is exiting by throwing an exception or returning a value, a return inside the finally silently discards it. The exception is swallowed, or the return value is overwritten, and the function hands its caller a normal-looking result. Nothing records that a failure occurred. No error, no warning, no notice, no trace.

function getConfig(): array {
    try {
        return loadConfig(); // throws if the file is missing
    } finally {
        return []; // the exception is gone, the caller just gets []
    }
}

The silence is the problem. The failure does not merely change a result. It vanishes, and if it resurfaces at all it does so far from the finally and long after, as a wrong value, an empty result, or an action that quietly never happened, with nothing pointing back to its cause. A discarded exception is exactly the kind of failure a language should make visible rather than swallow without a word.

A static scan of the ~5000 most-installed Composer packages found a return inside a finally in 12 places across 9 packages. Reading each one, 9 are deliberate or harmless, and in 3 the try can throw on a real path whose exception the finally return would discard.

Returning from a finally is never the only way to express a result. The same outcome can always be written without it, so no capability is lost. Runtime behaviour does not change. The language raises nothing for this construct today, and this proposal adds a deprecation at compile time wherever a finally contains a return, so the pattern is caught while the code is being written, before it silently swallows an exception in production.

If accepted, a return inside a finally block emits the following deprecation notice at compile time.

Deprecated: Returning from a finally block is deprecated

Deprecate changing by-reference return modifier via inheritance

PHP currently permits a child class to override an inherited method while flipping its return-by-reference modifier, changing a fundamental aspect of the method's calling convention.

class A {
    public function get() { /* by value */ }
}
class B extends A {
    public function &get() { /* by reference */ }
}

A caller holding an A cannot tell, without reflection, whether the result of $obj->get() could be reference. Given the issues with by-reference semantics and foreach this is an issue, and a LSP violation.

This proposal deprecates declaring an overriding method whose return-by-reference modifier differs from the parent's. Interfaces and abstract methods are covered by the same rule.

If accepted, such a declaration will emit a deprecation notice at the point the child class is declared:

Deprecated: Declaration of B::&get() should match A::get() (return-by-reference modifier differs)

Deprecate using certain names as identifiers to reserve them as keywords

We propose deprecating using certain names as identifiers, i.e. function, constant, class, interface, trait, or enum names, to reserve them as keywords for potential future PHP features.

Deprecate using "let" as an identifier

The motivation for this deprecation is to properly reserve this identifier as a potential keyword for PHP. It was previously proposed as part of the let construct (Block Scoping) RFC, which was not accepted. Nevertheless let is the obvious keyword for block scoping and reserving it now allows a possible future block scoping RFC to use the keyword without a breaking change.

The analysis performed at part of the let construct (Block Scoping) RFC indicated an insignificant usage of “let” in the wild.

If accepted, calling a class, interface, trait, enum, constant, or function “let” will emit a deprecation

Deprecated: Calling a {class|trait|interface|enum|constant|function} “let” is deprecated

If accepted, using “let” as a value for the second argument of class_alias() ($alias) will emit a deprecation:

Deprecated: class_alias: Passing “let” as value to argument #2 ($alias) is deprecated

Deprecate using "in", "out", and "inout" as identifiers

The motivation for this deprecation is to properly reserve those words as potential keywords for PHP. These keywords may form the basis of a better way to deal with references in PHP, namely regarding type consistency and mutability. These keywords may also be used as part of co/contra-variance indications for generic types; this observation comes from the secondary vote of the "Bound-Erased Generic Types" RFC about what type of variance marker syntax should be adopted.

If accepted, calling a class, interface, trait, enum, constant, or function “in”, “out”, or “inout” will emit a deprecation:

Deprecated: Calling a {class|trait|interface|enum|constant|function} “{in|out|inout}” is deprecated

If accepted, using “in”, “out”, or “inout” as a value for the second argument of class_alias() ($alias) will emit a deprecation:

Deprecated: class_alias: Passing “{in|out|inout}” as value to argument #2 ($alias) is deprecated

Deprecate the possibility to name a function "readonly"

readonly is nominally a reserved keyword for PHP. However, late in the development cycle of PHP 8.1 it was identified that WordPress has a function named “readonly”; as such we introduced a lexer “hack” to allow this. 1) This needed to be changed with the introduction of DNF types. 2)

This is a rather odd situation of a keyword being allowed in a specific circumstance when no other keyword in PHP is able to do so.

As such we propose deprecating using “readonly” as a function name.

If accepted, calling a function “readonly” will emit a deprecation:

Deprecated: Calling a function “readonly” is deprecated

Deprecate using "_" as an identifier

Back in PHP 8.4 naming a class/interface/trait/enum "_" (single underscore) was deprecated so that it could potentially be used as a potential wildcard pattern for the pattern matching RFC.

We propose to extend this deprecation to constants and functions, so that this reserved identifier follows the same rule as other identifiers in PHP. Moreover, in the initial deprecation we missed the fact that class_alias() could be used to alias a class to the “_” name.

If accepted, calling a constant or function “_” will emit a deprecation:

Deprecated: Calling a {constant|function} “_” is deprecated

If accepted, using “_” as a value for the second argument of class_alias() ($alias) will emit a deprecation:

Deprecated: class_alias: Passing “_” as value to argument #2 ($alias) is deprecated

Deprecate the _() function alias for gettext()

The primary motivation for this deprecation is to make “_” a reserved keyword, as indicated by one of the previous deprecation proposals. As such it would be odd for an extension to be able to name a function “_” when this would be deprecated in userland.

Luckily, the _() function is an alias for the gettext() function and both have existed since PHP 4. Therefore, we deem the impact of deprecating this function alias to be relatively low as the function calls can be renamed on virtually any PHP codebase in existence with various PHP userland tools.

If accepted, calling the _() function will emit the following deprecation:

Deprecated: The _() function is deprecated, use gettext() instead

Passing objects which are interpreted as arrays

A limited amount of PHP functions and features permit passing an object rather than an array and have it be interpreted as an array of the visible properties. This is done via the HASH_OF internal macro, the A and H ZPP specifiers, or one of the Z_PARAM_ARRAY_OR_OBJECT* fast ZPP macros.

Current functions and features utilising this capability which aren't already deprecated are:

For nearly all of these cases, instead of passing an object one can simply call get_object_vars() on the object prior to passing it to the function/stream context.

There are a few motivations for removing this ability from the engine. Primarily it has to do with semantics; modifying such a HashTable (the internal representation of an array) violates readonly semantics, and can corrupt enum cases. However, even if not modifying, care must be taken when dealing with HashTables resulting from objects, as they may include indirect zvals which refer to properties. We have had multiple bugs due to the lack of handling indirect properties, partly due to lack of testing. Finally, the ability to pass objects to a small number of functions and features seems to rarely be known about.

Passing objects for $array parameter of array_walk() and array_walk_recursive()

Other than the reasons previously explained as to why it would be beneficial to deprecate this behaviour, array_walk_recursive() will not recursively walk through objects, only arrays. As such an instance of a class:

class Foo {
    public array $array;
    public Foo $innerFoo;
    public string $str;
}

will not walk through the properties of the $innerFoo object.

As explained previously, if the callback provided takes the argument by reference, then it is possible to modify the value and violate readonly semantics.

Therefore we propose to deprecate passing objects for the $array parameter of array_walk() and array_walk_recursive()

If accepted, calling array_walk() or array_walk_recursive() with an object will emit a deprecation notice:

Deprecated: Passing an object for argument #1 $array to array_walk{_recursive}() is deprecated, call get_object_vars() first instead

Passing objects for $options parameter of deflate_init() and inflate_init()

The motivation has been detailed above, and this is a relatively unknown feature. Therefore, we propose to deprecate passing an object for the $option argument to inflate_init() and deflate_init().

If accepted, calling inflate_init() or deflate_init() with an object will emit a deprecation notice:

Deprecated: Passing an object for argument #2 $option to {in|de}flate_init() is deprecated, call get_object_vars() first instead

Passing objects as parameters to the ''zlib.inflate'' and ''zlib.deflate'' stream filters

Stream filter parameters can be set via the stream_filter_prepend() or stream_filter_append() and are validated in the filter creation factory method. However we cannot prevent passing objects to stream_filter_prepend() or stream_filter_append() as a user stream filter may expect an object as a parameter. Therefore we propose to check in the php_zlib_filter_create() C function that the filter parameters, if provided, are of type array, or for zlib.deflate it may also be of type int. Especially as the PHP documentation only mentions the possibility to pass the parameters as an associative array.

If accepted, attempting to set an object as filter parameters for the zlib.inflate and zlib.deflate stream filters will emit a deprecation notice:

Deprecated: Passing an object for filter parameters for zlib.{de|in}flate is deprecated, call get_object_vars() first instead

Passing objects as parameters to the ''bzip2.decompress'' and ''bzip2.compress'' stream filters

Stream filter parameters can be set via the stream_filter_prepend() or stream_filter_append() and are validated in the filter creation factory method. However we cannot prevent passing objects to stream_filter_prepend() or stream_filter_append() as a user stream filter may expect an object as a parameter. Therefore we propose to check in the php_bz2_filter_create() C function that the filter parameters, if provided, are of type array. Especially as the PHP documentation only mentions the possibility to pass the parameters as an associative array.

If accepted, attempting to set an object as filter parameters for the bzip2.decompress and bzip2.compress stream filters will emit a deprecation notice:

Deprecated: Passing an object for filter parameters for bzip2.{de}compress is deprecated, call get_object_vars() first instead

And in line with the new policy exempting input type and value validation from our BC Break policy, a TypeError would be thrown if attempting to pass values of type different than array or object:

TypeError: Expected value of type array for parameters of the the bzip2.{de}compress filter, TYPE given

Passing objects for $vars parameter of mb_convert_variables()

The motivation has been detailed above, and this is a relatively unknown feature. Therefore, we propose to deprecate passing an object for the $vars argument to mb_convert_variables(). Moreover, this function is extremely weird in that it takes the variable by reference to modify it in-place. The same behaviour can be achieved using the mb_convert_encoding() function in a cleaner and more intuitive way, as it also permits arrays that are converted recursively.

If accepted, calling mb_convert_variables() with an object will emit a deprecation notice:

Deprecated: Passing an object for argument #3 $vars to mb_convert_variables() is deprecated, call get_object_vars() first instead

As future scope it may be worthwhile to deprecate this function altogether in favour of mb_convert_encoding().

Passing objects for $data parameter of http_build_query()

The motivation has been detailed above, however in this case the feature is somewhat known and well documented in the manual. Thus it might be the most controversial deprecation of the lot. Ideally the Query Params RFC would have been finalised for PHP 8.6 as it would be a great alternative to this function.

However, the fact objects are treated like arrays has caused confusion and bug reports, namely regarding Stringable objects, as users expect them to get cast to a string rather than being used as an array. 3)4)

While the argument that object properties satisfy a key-value relationship (which is what this function cares about) and thus are perfectly suitable for this case, the confusion around this behaviour, the impossibility to support stringable objects in a non BC-breaking manner, and the rarity of the ability to use objects as an array we propose to deprecate this behaviour in favour of calling get_object_vars() (possibly recursively) on the objects and pass an associative array instead.

If accepted, calling http_build_query() with an object will emit a deprecation notice:

Deprecated: Passing an object for argument #1 $data to http_build_query() is deprecated, call get_object_vars() first instead

As a future scope, we may want to mark the php_url_encode_hash_ex() C API as static instead of exposing it, as a SourceGraph query shows basically no usage. 5)

Deprecate function aliases with non-canonical type name

Following the deprecation of non-standard cast names in PHP 8.5, it is consistent to consider deprecating legacy aliases which preserve the same non-canonical type terminology.

The following proposals aim to deprecate using “integer” or “long” as aliases for the int type and using “double” as an alias for the float type.

These names originate from historical internal naming and platform-specific integer sizing, but it no longer communicates anything useful to userland code. Keeping those aliases offers no functional value and keeps outdated terminology alive.

Deprecate is_double()

is_double() is a legacy alias of is_float(). Modern PHP presents the scalar type as float, and the canonical predicate is is_float().

This proposal deprecates is_double() and recommends using is_float() instead.

if (is_double($value)) {
    // ...
}
// To:
if (is_float($value)) {
    // ...
}

If accepted, calling is_double() will emit a deprecation notice:

Deprecated: Function is_double() is deprecated, use is_float() instead

Deprecate is_integer()

is_integer() is a legacy alias of is_int(). Modern PHP presents the scalar type as int, and the canonical predicate is is_int().

This proposal deprecates is_integer() and recommends using is_int() instead.

if (is_integer($value)) {
    // ...
}
// To:
if (is_int($value)) {
    // ...
}

If accepted, calling is_integer() will emit a deprecation notice:

Deprecated: Function is_integer() is deprecated, use is_int() instead

Deprecate is_long()

is_long() is a legacy alias of is_int(). Modern PHP presents the scalar type as int, and the canonical predicate is is_int().

This proposal deprecates is_long() and recommends using is_int() instead.

if (is_long($value)) {
    // ...
}
// To:
if (is_int($value)) {
    // ...
}

If accepted, calling is_long() will emit a deprecation notice:

Deprecated: Function is_long() is deprecated, use is_int() instead

Deprecate doubleval()

doubleval() is a legacy alias of floatval(). Since floatval() is the canonical function name and matches the scalar type name float, keeping doubleval() provides no distinct behavior and only increases the number of ways to express the same conversion.

This proposal deprecates doubleval() and recommends using floatval() instead.

$number = doubleval($value);
// To:
$number = floatval($value);

If accepted, calling doubleval() will emit a deprecation notice:

Deprecated: Function doubleval() is deprecated, use floatval() instead

Deprecate strcoll()

The strcoll() function permits comparing two strings depending on the current LC_COLLATE setting set via setlocale(). Because setlocale() relies on global state that can be set by any application/library running on the system it is generally discouraged to rely on it. Moreover, some locales may not necessarily properly implement the collation rules to sort strings in different natural languages/locales as can be seen by a user note on the strcoll() documentation from 2019.

This function is also not binary safe, meaning if the string contains a null byte, the string will be truncated and discard anything past the null byte. While this shouldn't happen in practice, all other string comparison functions are binary safe.

Finally, the intl extension provides a direct replacement which is more accurate and has up-to-date sorting rules via the Collator::compare() method.

Therefore, we propose to deprecate the strcoll() function and recommand using Collator::compare() instead.

If accepted, calling strcoll() will emit a deprecation notice:

Deprecated: Function strcoll() is deprecated, use Collator::compare() instead

Deprecate SORT_LOCALE_STRING flag for sort() functions

This flag sorts an array using the strcoll() function. The same problems that strcoll() has also apply to this flag: relying on global state, not binary safe, may sort incorrectly. And like strcoll() it has alternatives provided by the intl extension, which are:

  • Collator::sort()
  • Collator::asort()
  • Collator::sort()
  • Collator::sortWithSortKeys()

As such, we propose deprecating the SORT_LOCALE_STRING constant in favour of one of the listed Collator methods.

If accepted, using the SORT_LOCALE_STRING constant will emit a deprecation notice:

Deprecated: Constant SORT_LOCALE_STRING is deprecated, use one of the Collator::*sort*() methods instead

Deprecate mysqli::stmt_init

  • Author: Kamil Tekiela

There is no justified reason for this function to exist. All it does is create a barren mysqli_stmt object that still needs to be prepared before use. It is the source of many ways in which mysqli objects can be left in an inconsistent state. It looks like it is only used in PHP unit tests, and in most cases for no good reason. Since this function doesn't provide any tangible value and leads to confusing code, it should be removed to reduce language complexity.

$stmt = $mysqli->stmt_init();
$stmt->prepare($sql);
// To:
$stmt = $mysqli->prepare($sql);

If accepted, calling mysqli::stmt_init() will emit a deprecation notice:

Deprecated: Function mysqli::stmt_init() is deprecated, use mysqli::prepare() instead

An alternative and almost unknown way of calling this function is through the constructor, which is equally useless. This proposal includes the deprecation of this feature, i.e. calling mysqli_stmt::__construct without the second argument.

$stmt = new mysqli_stmt($mysqli);
$stmt->prepare($sql);
// To:
$stmt = new mysqli_stmt($mysqli, $sql);

If accepted, calling new mysqli_stmt() without the second argument will emit a deprecation notice:

Deprecated: Instantiation of mysqli_stmt without providing the $query parameter is deprecated

Deprecate mysqli_get_charset()

  • Author: Kamil Tekiela

This function can be used to retrieve internal implementation details of the currently selected character set. It was able to provide more meaningful values when mysqli was compiled against libmysql, but since PHP 8.2 that's no longer the case. The values reported are:

  • Character set name. The same as is returned by mysqli_character_set_name()
  • Default collation name for that charset as defined by mysqlnd, not by the server. As this information doesn't correspond to the true values defined by the server it is of no use to anyone.
  • Directory from which the charset information was imported. Used only with libmysql for dynamically loaded character sets. In mysqlnd it's an empty string.
  • Minimum and maximum character length of the character set. This is the only truly useful and correct information provided by this function, but it is still an implementation detail and in my opinion, not something that a PHP user would ever need to know.
  • Collation ID as defined by mysqlnd. It's just a number. It cannot be used for anything as it's not guaranteed that it's the same as defined in MySQL/MariaDB. As of PHP 8.6, it is set to 0 in all cases. Even in libmysql it's no longer used, other than for finding out the default collation of a charset for backwards compatibility reasons.
  • State. In libmysql it is always the value 1. In libmysql it was a bitflag of internal characteristics of the character set. It has no use in PHP.

The proposal is to deprecate and remove this function, and its OO-style alias mysqli::get_charset(), and later remove it without a substitute. It's pretty unlikely that any PHP project still uses this function, and if they do, they probably don't know that it returns falsified information. The only uses I was able to find (see the user comment in PHP manual) are people incorrectly assuming that it returns the currently set collation. By removing this function, we reduce one more point of confusion for PHP users.

If accepted, calling mysqli_get_charset() will emit a deprecation notice:

Deprecated: Function mysqli_get_charset() is deprecated, did you mean mysqli_character_set_name()?

The reason for this message is that mysqli_get_charset seems like a logical antonym to mysqli_set_charset and people wanting to know the name of currently selected character set should use mysqli_character_set_name

Deprecate ArrayIterator methods that inherit ArrayObject implementation

Various ArrayIterator methods only exist because for a long time it shared a common implementation with ArrayObject. Most of these methods do not make much sense, and prevent optimizing the implementation of ArrayIterator.

We propose deprecating the following methods:

Those methods would NOT be deprecated from ArrayObject.

Deprecate Metaphone Function

The metaphone() function calculates the metaphone key of a string. The original Metaphone algorithm was published in 1990. However, it has significant limitations:

  • It is strictly limited to English pronunciation rules.
  • It has been superseded by more accurate algorithms, namely Double Metaphone (1999) and Metaphone 3 (2009).

Currently, PHP core maintains the oldest and least accurate version of this algorithm in C. Since phonetic algorithms are language-specific and frequently updated, they are better suited for userland implementation. There are already robust and actively maintained userland libraries available via Composer that implement Double Metaphone and Metaphone 3. [1][2]

Maintaining this legacy function in the core provides little value to modern PHP applications, and therefore we propose to deprecate metaphone() in PHP 8.6.

Note: For the same reason, it is also reasonable to deprecate soundex. However, soundex is widely supported by SQL databases so its deprecation might cause a bigger impact.

The implementation is already done at: https://github.com/php/php-src/pull/21889

Deprecate passing a $sessionhandler object to session_set_save_handler() which does not contain the create_sid() and validateId() methods

User-defined session handlers aren't forced to implement methods that would ensure that the session extension is well behaved in regards to the session.use_strict_mode INI setting. This is important as the default value for it has been changed in the "Secure Session Configuration Defaults" RFC, and should never be disabled.

The methods which are required for the behaviour to be well-defined are create_sid() and validateId().

The proposal is effectively integrating the SessionIdInterface interface and the validateId() method from the SessionUpdateTimestampHandlerInterface interface into the SessionHandlerInterface interface.

Therefore, we propose to emit a deprecation when calling session_set_save_handler() with an object that doesn't implement the create_sid() and validateId() methods, regardless of whether it implements either SessionIdInterface or SessionUpdateTimestampHandlerInterface. But we also propose to emit a warning when implementing the SessionHandlerInterface interface on a class which doesn't define the create_sid() or validateId() methods.

If accepted, calling session_set_save_handler() with an object that doesn't implement the create_sid() and validateId() methods will emit the following deprecation:

Deprecated: Providing an object to argument #1 ($sessionhandler) which does not have the {create_sid|validateId}() method defined is deprecated

And the following warning will be emitted when implementing SessionHandlerInterface on a class which doesn't define the create_sid() or validateId() methods:

Warning: Interface SessionHandlerInterface will require the {create_sid|validateId}() method in PHP 9.0

In PHP 9 the validateId() method would be removed from SessionUpdateTimestampHandlerInterface.

As a future scope, during the PHP 9 release cycle one could deprecate the SessionIdInterface interface as it effectively becomes useless.

Deprecate spl_classes()

This function returns a list of all classes and interfaces that are part of the SPL extension. Since 2008, the Reflection extension has always been available, and using that extension should be preferred over this function that no other extensions expose. Use ReflectionExtension::getClassNames() instead.

If accepted, calling the function will emit the following deprecation

Deprecated: Function spl_classes() is deprecated since 8.6, use ReflectionExtension::getClassNames() instead.

Deprecate is_subclass_of with string when $allow_string is false

is_subclass_of is used to check if an object or class name is a subclass of some other named class/interface. The first parameter can be either an object to check, or the name of a class. The third parameter, $allow_string, controls whether the first parameter can be a string. The reason this is useful is that when set to false, strings will not trigger autoloading. However, if a string is given when $allow_string is false, the function will always return false, even if the class does not need to be autoloaded.

If a developer has specified that strings should not be allowed (the default is that they are allowed), then passing strings indicates a bug. We propose to deprecate providing a string to the first parameter if strings are not allowed. This will be bumped to an error in PHP 9.0.

If accepted, calling is_subclass_of with a string but strings not being allowed will emit the following deprecation

Deprecated: Calling is_subclass_of() with a string but with $allow_string = false is deprecated, if the class should not be autoloaded check class_exists() and then call is_subclass_of() with $allow_string = true if the class is already loaded.

Deprecate is_a with string when $allow_string is false

is_a is used to check if an object or class name is an instance of some other named class/interface. The first parameter can be either an object to check, or the name of a class. The third parameter, $allow_string, controls whether the first parameter can be a string. The reason this is useful is that when set to false, strings will not trigger autoloading. However, if a string is given when $allow_string is false, the function will always return false, even if the class does not need to be autoloaded.

If a developer has specified that strings should not be allowed (the default is that they are allowed), then passing strings indicates a bug. We propose to deprecate providing a string to the first parameter if strings are not allowed. This will be bumped to an error in PHP 9.0.

If accepted, calling is_subclass_of with a string but strings not being allowed will emit the following deprecation

Deprecated: Calling is_a() with a string but with $allow_string = false is deprecated, if the class should not be autoloaded check class_exists() and then call is_a() with $allow_string = true if the class is already loaded.

Deprecate define() with $case_insensitive being specified

In PHP 7.3, case-insensitive constants were deprecated, see PHP RFC: Deprecate and Remove Case-Insensitive Constants. The implementation of the deprecation included E_DEPRECATED alerts when trying to define case insensitive constants.

However, when support was removed in PHP 8.0, the deprecation message was replaced with a E_WARNING rather than triggering an error. We propose to convert the warning to an error in PHP 9.0. In PHP 8.6 and until PHP 9.0, the E_WARNING code level will be kept (since warnings are considered stronger than deprecations) but the message will be updated to indicate that it will be upgraded to throwing an error in the future.

If accepted, the message will be:

Warning: define(): Argument #3 ($case_insensitive) is ignored since declaration of case-insensitive constants is no longer supported, this will be an error in PHP 9.0.

To avoid the confusing case of a parameter still existing but only being allowed to be false, we also propose to deprecate explicitly passing in the value false (until 9.0) at which point the value can be removed entirely. If false is manually specified, a new E_DEPRECATION will be emitted:

Deprecated: Argument #3 ($case_insensitive) is ignored and treated as false since declaration of case-insensitive constants is no longer supported, passing the argument explicitly is unnecessary.

Since the default has always been that the value is false, there are not backwards compatibility concerns about callers needing to provide false on previous versions.

Deprecate spl_object_hash()

The spl_object_hash() function returns a unique identifier for an object. However, the “hash” part is a bit of a misnomer since it is not based on the contents of an object. Since PHP 7.2, spl_object_id() has been available that also returns a unique id, but as an integer and with a clearer function name.

Morgan noted on the mailing list that if you need an identifier in the format that spl_object_hash() returns, the following code does the same thing:

str_pad(dechex(spl_object_id($obj)), 16, '0', STR_PAD_LEFT) . '0000000000000000'

Accordingly, we propose to deprecate spl_object_hash(). If accepted, calling the method will emit the following deprecation:

Deprecated: Function spl_object_hash() is deprecated since 8.6, consider using spl_object_id() instead.

Deprecate ReflectionProperty::setValue() and ReflectionProperty::setRawValue() with wrong types

ReflectionProperty methods are used to interact with the representation of a property on a class. For example, ReflectionProperty::getValue() can only be called with an object that is either an instance of the target class, or a subclass thereof (i.e. any object that actually has the property).

However, ReflectionProperty::setValue() and ReflectionProperty::setRawValue() do not validate what type of object they are given. For example, the following code works fine:

<?php
 
class Foo {
    private mixed $value;
}
 
#[AllowDynamicProperties]
class Bar {}
 
$reflection = new ReflectionProperty(Foo::class, 'value');
$obj = new Bar();
$reflection->setValue($obj, true);
 
var_dump($obj);

this sets a new property named “value” on the instance of the Bar class, even though the Bar class is entirely unrelated to the Foo class. If the property does not already exist on the target object, it is no better than using a normal assignment; if it does already exist on the target object, it will be set, but it is still conceptually wrong because despite the reflection being about “a Foo::class property named $value” the property set will be unrelated to the Foo class.

This lack of validation differs from other ReflectionProperty methods, which generally throw exceptions when given an object that is not an instance of the class the property was declared in, e.g. getValue(), getRawValue(), isInitialized(), isReadable(), and isWritable().

Accordingly, we propose to emit deprecation warnings when ReflectionProperty::setValue() and ReflectionProperty::setRawValue() are used with objects that are not instances of the class that the property corresponds to. Doing so will emit a deprecation warning, similar to:

Deprecated: Calling ReflectionProperty::setValue() with a given object that is not an instance of the class this property was declared in is deprecated.

with the appropriate adjustment for ReflectionProperty::setRawValue().

Deprecate dechunk filter

  • Author: Sjoerd Langkemper

The dechunk stream filter is an undocumented feature in PHP designed to decode HTTP chunked transfer encoding. While it is utilized internally by PHP's HTTP stream wrapper, its exposure to userland (particularly through php://filter) has become a notorious vector for security exploits.

This proposal seeks to deprecate the dechunk filter for userland usage in PHP 8.6 to eliminate a high-risk attack surface.

The dechunk filter is heavily abused in filter chain attacks to create error-based side-channel oracles. Because it interprets strings based on whether they start with a valid hexadecimal character, attackers use it to exfiltrate local files blindly. The filter is completely undocumented in the official PHP manual. Furthermore, its implementation is historically loose, skipping non-hex characters arbitrarily to find chunk extensions. HTTP chunk decoding is easy to implement in userland PHP for the rare occasions it is required outside of standard HTTP clients.

The dechunk filter is not documented. This puts it in a limbo state of being a filter that is available but not fully supported. Deprecating it would solve this.

This proposal proposes to fully deprecate the dechunk filter. There was some discussion on the mailing list to deprecate it only for use in php://filter, but still allow use in stream_filter_append. This would keep the limbo state of a partially supported function, and increase the inconsistency in that some filters can be used in some contexts but not in others.

While the filter is largely unknown to everyday developers, it does have existing usage:

  • PHP's HTTP stream wrapper relies on this filter internally when handling Transfer-Encoding: chunked. This will remain as-is and will not be deprecated.
  • Some userland libraries (such as Symfony's HttpClient) explicitly invoke it via stream filter functions. These will need an alternative, such as decoding it using code written in PHP.

The proposal is to deprecate the filter entirely for all userland calls (both php://filter and stream_filter_append()), keeping it strictly as an unexposed internal mechanism for the HTTP stream wrapper.

Deprecated: The “dechunk” stream filter is deprecated and will be removed in a future version of PHP.

References:

Deprecate using "namespace" as a class constant or static property name

namespace” is currently a semi-reserved keyword, which makes it allowed to be used as a class member name. The proposal is to deprecate the usage of namespace as a constant and static property name, to later promote it to a fully reserved keyword.

class Foo { 
  const NAMESPACE = '';  // deprecated
  static $namespace = ''; // deprecated
}

The motivation is to reserve namespace for future syntax. In particular, it would allow a future ::namespace pseudo-constant, analogous to ::class, for directory namespaces (eg. to replace stringy APIs like “Order\\Domain\\Entities” with \Order\Domain\Entities::namespace for better IDE support). Methods named “namespace”, such as Route::namespace(), are not affected.

A search on GitHub yields ~120 matching files. Affected code can migrate by renaming the member to a name like NAMESPACE_NAME.

If accepted, declaring such member will emit a deprecation:

Deprecated: Declaring {class|interface|trait|enum} {constant|static property} “namespace” is deprecated

Backward Incompatible Changes

For PHP 8.6 additional deprecation notices will be emitted.

The actual removal of the affected functionality will happen no earlier than PHP 9.

Removed from this proposal

The following entries were originally added to this proposal and then dropped.

rfc/deprecations_php_8_6.txt · Last modified: by weilindu