====== PHP RFC: Deprecations for PHP 8.5 ====== * Date: 2024-09-26 * Authors: * Gina Peter Banyard * Christoph M. Becker * Daniel Scherzer * Tim Düsterhus * Theodore Brown * Jorg Sowa * David Carlier * Jakub Zelenka * Nicolas Grekas * Volker Dusch * Status: Draft * Implementation: TBD ===== Introduction ===== The RFC proposes to deprecate the listed functionality in PHP 8.5 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 ''key_length'' parameter of openssl_pkey_derive() * ext/pdo deprecations: * Deprecate PDO's 'uri:' scheme * Deprecate PDO::ERRMODE_WARNING error mode * Deprecate driver specific PDO constants and methods * Deprecate Pdo\Pgsql::PGSQL_TRANSACTION_IDLE, Pdo\Pgsql::PGSQL_TRANSACTION_ACTIVE, Pdo\Pgsql::PGSQL_TRANSACTION_INTRANS, Pdo\Pgsql::PGSQL_TRANSACTION_INERROR and Pdo\Pgsql::PGSQL_TRANSACTION_UNKNOWN * Deprecate intl.error_level INI setting * ext/reflection deprecations: * Deprecate Reflection*::setAccessible() * Deprecate ReflectionParameter::allowsNull() * Deprecate ReflectionClass::getConstant() for missing constants * ext/filter deprecations: * Deprecate FILTER_DEFAULT constant * Make ''$filter'' parameter mandatory for filter_*() functions * Deprecate FILTER_CALLBACK filter * Deprecate filter_input(), filter_input_array(), and filter_has_var() * Core INI directive deprecations: * Deprecate the ''docref_root'' and ''docref_ext'' INI directives * Deprecate the ''error_prepend_string'' and ''error_append_string'' INI directives * Deprecate the ''report_memleaks'' INI directive * Deprecate the ''register_argc_argv'' INI directive * Formally deprecate mysqli_execute * Formally deprecate socket_set_timeout * Language/syntax deprecations: * Deprecate semicolon after ''case'' in switch statement * Deprecate non-standard cast names * Deprecate attributes applying to multiple class properties/constants * Deprecate backticks ` as an alias for shell_exec * Deprecate __construct() and __destruct() in interfaces * Deprecate the __sleep() and __wakeup() magic methods * Deprecate the $exclude_disabled parameter of get_defined_functions() * Deprecate building ext/ldap against Oracle LDAP * Deprecate passing ''null'' to readdir(), rewinddir(), and closedir() * Deprecate the $context parameter for finfo_buffer() * Deprecate no-op functions from the resource to object conversion: * Deprecate finfo_close() * Deprecate xml_parser_free() * Deprecate curl_close() * Deprecate curl_share_close() * Deprecate imagedestroy() * Deprecate DATE_RFC7231 and DateTimeInterface::RFC7231 * ext/spl deprecations: * Deprecate ArrayObject and ArrayIterator with objects * Deprecate SplObjectStorage::contains(), SplObjectStorage::attach(), and SplObjectStorage::detach() * Deprecate passing spl_autoload_call() to spl_autoload_unregister() * Deprecate using values of type null, bool, and float as array offsets and when calling array_key_exists() * Deprecate __debugInfo() returning null * Deprecate constant redeclaration * Deprecate Closure binding issues * Enact follow-up phase of the "[[rfc:saner-inc-dec-operators|Path to Saner Increment/Decrement operators]]" RFC * Remove the ''disable_classes'' INI setting * Deprecate the $http_response_header predefined variable ===== Proposal ===== Each feature proposed for deprecation is voted separately and requires a 2/3 majority. All votes refer to deprecation in PHP 8.5 and removal in PHP 9 (except where otherwise noted). ==== Deprecate key_length parameter of openssl_pkey_derive() ==== * Author: Jakub Zelenka The key_length parameter is supposed to attempt to set the length of derived secret. However in reality it is completely ignored if the size is higher than the size of key prime. It has effect only if the size is lower that the size of the prime. In such case, the key is truncated for ECDH and the derivation fails for DH. The idea is to deprecate this parameter so users do not get false sense of security when providing higher number than the size of prime. This issue was raised during the security audit. The caution notes has been added in https://github.com/php/doc-en/pull/3789 . * Yes * No ==== ext/pdo deprecations ==== === Deprecate PDO's 'uri:' scheme === * Author: Tim Düsterhus PDO supports a ''uri:'' DSN scheme that does not refer to a database driver named “uri” but instead is an indication to read the actual DSN from the specified URI, including remote stream wrappers, such as HTTP URIs. While this feature might appear to be useful to make it easier for environment-specific configuration of an application, the username and password notably are not part of the DSN, which would require a separate configuration mechanism and which point the actual DSN could use it as well. Similarly authentication via TLS certificates with the MySQL driver would require specifying the certificate and private key as the $options. In case of remote stream wrappers, the feature can be actively dangerous, unless the URL referenced is 100% trustworthy, since the username and password for the database might be sent to an untrusted database servers. In case of the SQLite PDO driver, a remote URL might enable access to arbitrary SQLite files on the server. Furthermore remote stream wrappers might result in performance-bottlenecks, since the file is requested for every single connection. If this feature is somehow exactly what is desired, it can easily replicated in userland as follows (while preserving the main semantics of only looking at the first line): $dsn = '…'; if (str_starts_with($uri, 'uri:')) { if (($f = fopen(substr($uri, strlen('uri:')), 'rb'))) { $dsn = fgets($f, 512); fclose($f); } else { // Emit error message } } * Yes * No === Deprecate PDO::ERRMODE_WARNING error mode === * Author: Gina Peter Banyard PDO can be set to report errors in 3 different ways: * PDO::ERRMODE_SILENT * PDO::ERRMODE_WARNING * PDO::ERRMODE_EXCEPTION In silent mode, PDO will proceed if an error has occured within a PDO statement or PDO instance, and set the error code and info so that it can be queried manually. This allows for a more hands on approach to error handling. In exception mode, PDO will instead throw a PDOException whenever an error occurs, and unwind the stack unless it the exception is caught via a catch block. This is a more of a hands off approach as error handling doesn't need to be done after each method call of a PDO or PDO statement instance. This mode has been the default since PHP 8.0. In warning mode, PDO will behave similarly as if set in silent mode, but also raise an E_WARNING similarly to how the exception mode behaves. As such this mode is a bit non-sensical as it is both a hands on and hands off approach to error handling. Moreover, an E_WARNING may be promoted to an exception via an error handler set via set_error_handler(), something that might not be expected by code written with PDO::ERRMODE_SILENT in mind. As such we propose to deprecate the PDO::ERRMODE_WARNING constant and recommend users either use the silent or exception mode. * Yes * No === Deprecate driver specific PDO constants and methods === * Author: Gina Peter Banyard With the acceptance and merging of the [[rfc:pdo_driver_specific_subclasses|PDO driver specific sub-classes]] RFC, the recommended way to provide driver specific functionality is by creating a subclass of PDO with said functionality. As such we propose deprecating the overloaded PDO constants which are declared using the REGISTER_PDO_CLASS_CONST_LONG macros and the overloaded PDO methods which are defined in the ''pgsql_driver.stub.php'' and ''sqlite_driver.stub.php'' files. This means the following PDO constants would be deprecated: * PDO::DBLIB_ATTR_CONNECTION_TIMEOUT * PDO::DBLIB_ATTR_QUERY_TIMEOUT * PDO::DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER * PDO::DBLIB_ATTR_VERSION * PDO::DBLIB_ATTR_TDS_VERSION * PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS * PDO::DBLIB_ATTR_DATETIME_CONVERT * PDO::FB_ATTR_DATE_FORMAT * PDO::FB_ATTR_TIME_FORMAT * PDO::FB_ATTR_TIMESTAMP_FORMAT * PDO::MYSQL_ATTR_USE_BUFFERED_QUERY * PDO::MYSQL_ATTR_LOCAL_INFILE * PDO::MYSQL_ATTR_INIT_COMMAND * PDO::MYSQL_ATTR_MAX_BUFFER_SIZE * PDO::MYSQL_ATTR_READ_DEFAULT_FILE * PDO::MYSQL_ATTR_READ_DEFAULT_GROUP * PDO::MYSQL_ATTR_COMPRESS * PDO::MYSQL_ATTR_DIRECT_QUERY * PDO::MYSQL_ATTR_FOUND_ROWS * PDO::MYSQL_ATTR_IGNORE_SPACE * PDO::MYSQL_ATTR_SSL_KEY * PDO::MYSQL_ATTR_SSL_CERT * PDO::MYSQL_ATTR_SSL_CA * PDO::MYSQL_ATTR_SSL_CAPATH * PDO::MYSQL_ATTR_SSL_CIPHER * PDO::MYSQL_ATTR_SERVER_PUBLIC_KEY * PDO::MYSQL_ATTR_MULTI_STATEMENTS * PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT * PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY * PDO::ODBC_ATTR_USE_CURSOR_LIBRARY * PDO::ODBC_ATTR_ASSUME_UTF8 * PDO::ODBC_SQL_USE_IF_NEEDED * PDO::ODBC_SQL_USE_DRIVER * PDO::ODBC_SQL_USE_ODBC * PDO::PGSQL_ATTR_DISABLE_PREPARES * PDO::PGSQL_TRANSACTION_IDLE * PDO::PGSQL_TRANSACTION_ACTIVE * PDO::PGSQL_TRANSACTION_INTRANS * PDO::PGSQL_TRANSACTION_INERROR * PDO::PGSQL_TRANSACTION_UNKNOWN * PDO::SQLITE_DETERMINISTIC * PDO::SQLITE_ATTR_OPEN_FLAGS * PDO::SQLITE_OPEN_READONLY * PDO::SQLITE_OPEN_READWRITE * PDO::SQLITE_OPEN_CREATE * PDO::SQLITE_ATTR_READONLY_STATEMENT * PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES And the following overloaded PDO methods would be deprecated: * PDO::pgsqlCopyFromArray() * PDO::pgsqlCopyFromFile() * PDO::pgsqlCopyToArray() * PDO::pgsqlCopyToFile() * PDO::pgsqlGetNotify() * PDO::pgsqlGetPid() * PDO::pgsqlLOBCreate() * PDO::pgsqlLOBOpen() * PDO::pgsqlLOBUnlink() * PDO::sqliteCreateAggregate() * PDO::sqliteCreateCollation() * PDO::sqliteCreateFunction() * Yes * No === Deprecate Pdo_Pgsql constants related to statement transaction state === * Author: David Carlier The following constants * Pdo\Pgsql::PGSQL_TRANSACTION_IDLE * Pdo\Pgsql::PGSQL_TRANSACTION_ACTIVE * Pdo\Pgsql::PGSQL_TRANSACTION_INTRANS * Pdo\Pgsql::PGSQL_TRANSACTION_INERROR * Pdo\Pgsql::PGSQL_TRANSACTION_UNKNOWN seem to have been an attempt to equalize, feature-wise, the pgsql and pdo/pgsql extensions. While it makes perfect sense for the former, it can't be usable in the latter in any way since it never returns the precise statement transaction state to the user code. Internally, the extension only checks if the transaction is stale or not but those constants are completely irrelevant in this context. Therefore, we propose to start deprecating them starting from 8.5 then remove them in the next major release. * Yes * No ==== Deprecate intl.error_level INI setting ==== * Author: Gina Peter Banyard Error handling within the Intl extension is handled by two ini settings: * The ''intl.use_exceptions'' INI setting which determines if errors are report by throwing an IntlException * The ''intl.error_level'' INI setting allows to set which type of error level should used, typically one of the E_* error constants. Moreover, it is possible to enable both exceptions and use a non-zero error level, in which case the error is emitted first. This means that it can be "caught" via an error handler set with set_error_handler() first and be promoted to an exception different than IntlException. However, more concerningly is that the error level could be set to E_ERROR which would trigger a bailout of the PHP VM. PHP's bailout mechanism is generaly reserved for severe engine failures, such as being unable to allocate memory. There are multiple issues with bailouts which are described in detail in the [[https://wiki.php.net/rfc/engine_exceptions_for_php7#issues_with_fatal_errors|"Issues with fatal errors" section of the Exceptions in the engine (for PHP 7)]] RFC. Some of them are: * finally blocks are not executed * Destructors are not executed * Memory may not be freed As this INI setting has some of the same pitfalls as the PDO::ERRMODE_WARNING constant and can cause bailouts, we propose deprecating this INI setting, meaning setting it to any value different than ''0'' will raise an E_DEPRECATED diagnostic. * Yes * No ==== ext/reflection deprecations ==== === Deprecate Reflection*::setAccessible() === * Author: Tim Düsterhus The [[rfc:make-reflection-setaccessible-no-op|“Make reflection setAccessible() no-op” RFC]] in PHP 8.1 made the Reflection*::setAccessible() methods a noop. However it did not deprecate the methods to simplify cross-version compatibility. Since PHP 8.0, which is the last version where the methods did anything, is already EOL and PHP 8.1, which introduced the change will be EOL shortly after the release of PHP 8.5, it seems reasonable to deprecate the methods to make users aware that they have no effect (especially for calls to ->setAccessible(false)) and to encourage users to remove them. If compatibility with PHP 8.0 or earlier is desired, the deprecation can be suppressed with @ or alternatively the call to ->setAccessible() can be guarded by an if (PHP_VERSION_ID < 80100) check. We therefore propose to add: #[\Deprecated(since: '8.5', message: "as this method does nothing")] to the signature. * Yes * No === Deprecate ReflectionParameter::allowsNull() === * Author: Daniel Scherzer With the introduction of ReflectionParameter::getType() in PHP 7.0, the preferred way to analyze the type of a parameter is by calling that method and then examining it, e.g. using ReflectionType::allowsNull(). However, while most of the methods for examining a parameter type directly from ReflectionParameter have been deprecated (::isArray(), ::isCallable(), and ::getClass()), the ::allowsNull() method was not. We propose to deprecate ReflectionParameter::allowsNull() in favor of using ReflectionType::allowsNull() by adding #[\Deprecated(since: '8.5', message: "use ReflectionParameter::getType() instead")] to the signature. * Yes * No === Deprecate ReflectionClass::getConstant() for missing constants === * Author: Daniel Scherzer ReflectionClass:getConstant() returns the value of the class constant with a given name, or false if no such class constant exists. This behavior is confusing because it makes no distinction between a class constant with the value false and one that does not exist: getConstant( 'EXISTS' ) ); // false echo "MISSING: "; var_dump( $r->getConstant( 'MISSING' ) ); // false Unlike with ReflectionClass::getReflectionConstant() (which returns either a ReflectionClassConstant or false for missing constants) the return value of ReflectionClass:getConstant() for a missing constant is also valid for an existing constant with the value false. This can result in confusion, and if the value is false, a call to ReflectionClass::hasConstant() to check if the constant exists or not. We propose that, if a constant with the given name does not exist, deprecation warnings be emitted (in PHP 8.5, becoming an exception in PHP 9.0). * Yes * No ==== ext/filter deprecations ==== * Author: Gina Peter Banyard === Deprecate FILTER_DEFAULT constant === The FILTER_DEFAULT constant is an alias for the FILTER_UNSAFE_RAW constant. This has been the case since at least PHP 5.3. This is confusing and seems to indicate that it corresponds to the filter set by the ''filter.default'' INI setting. Moreover, this INI setting was deprecated in [[https://wiki.php.net/rfc/deprecations_php_8_1#filterdefault_ini_setting|PHP 8.1]]. As this constant is confusing and misleading, we propose to deprecate it. * Yes * No === Make $filter parameter mandatory for filter_*() functions === The filter_*() functions do not require passing the $filter parameter, the default value of it is FILTER_DEFAULT which is an alias for the FILTER_UNSAFE_RAW filter. This filter does **nothing** if no flags are provided. This behaviour is indicative of a bug, therefore, we propose to make the $filter argument mandatory and emit a deprecation notice if the default value is used. * Yes * No === Deprecate FILTER_CALLBACK filter === The FILTER_CALLBACK filter allows providing a function to call on the value to filter. This makes little sense as one can pass the value to filter directly to the function instead of passing by the filter extension. Similarly, to filter an array of values, it is easier and more intuitive to use the array_map() function rather than the filter extension. As such, we propose to deprecate this filter. * Yes * No === Deprecate filter_input(), filter_input_array(), and filter_has_var() === The filter_input() and filter_input_array() functions operate on the **original** values provided by the SAPI that populate the superglobals for $_GET, $_POST, $_SERVER, $_ENV, and $_COOKIE. This means that modification to any entry of the superglobal will not be used when calling these functions. This is showcased by the following PHPT test: --TEST-- filter_input() filter with superglobal modified --EXTENSIONS-- filter --GET-- a=hello --FILE-- fn (string $s) => $s === "world"]); var_dump($f1); $_GET['a'] = "world"; var_dump($_GET); $f2 =filter_input(INPUT_GET, "a", FILTER_CALLBACK, ['options' => fn (string $s) => $s === "world"]); var_dump($f2); var_dump($_GET); ?> --EXPECT-- array(1) { ["a"]=> string(5) "hello" } bool(false) array(1) { ["a"]=> string(5) "world" } bool(false) array(1) { ["a"]=> string(5) "world" } As it is easy and straight forward to have the same behaviour by using filter_var($_GET['a'], /* other params */) and filter_var_array($_GET, /* other params */), we propose to deprecate filter_input() and filter_input_array(). As filter_has_var() is effectively equivalent to array_key_exists(), but has the same caveat as the two previous functions, we propose to also deprecate this function. * Yes * No ==== Core INI directive deprecations ==== === Deprecate the docref_root and docref_ext INI directives === * Author: Gina Peter Banyard Both of these INI settings allow overriding the output of HTML diagnostic errors (warning, notice, deprecations, etc.) to change the base URL and file extension for the clickable links pointing to functions and/or INI settings in error messages generated by calls to ''php_error_docref()''. This is a debug feature and had some value when the php.net documentation had mirrors, considering those have been retired, their use is now limited. As such, we propose deprecating those two INI settings. * Yes * No === Deprecate the error_prepend_string and error_append_string INI directives === * Author: Gina Peter Banyard Both of these INI settings allow overriding the output of HTML diagnostic errors (warning, notice, deprecations, etc.) to prepend or append HTML before the generated HTML of these diagnostic errors. This is a development and debugging feature which seems somewhat questionable and of limited use. As such, we propose deprecating those two INI settings. * Yes * No === Deprecate the report_memleaks INI directive === * Author: Gina Peter Banyard This INI directive allows to suppress ZendMM memory leaks in debug builds of PHP. This "feature" is highly questionable, as memory leaks should be fixed the moment they are made aware of. Because this cannot affect production builds of PHP we propose deprecating this INI setting. * Yes * No === Deprecate the register_argc_argv INI directive === * Author: Nicolas Grekas This INI directive tells PHP whether to declare the argv & argc variables. On the CLI, phpdbg and embed SAPIs it is forced to On. On other SAPIs, it defaults to On in the C code but is set to Off in the default php.ini files. This setting is dangerous on HTTP SAPIs because it allows defining the value of the argv/argc variables from the query string. This is almost always unwanted and certainly unexpected. It can lead to security issues if one reads argv/argc from an HTTP apps while not being aware of this behavior. We propose to deprecate this INI setting and make in default to Off in PHP 8.5, then to hardcode it to Off for all non-CLI-related SAPIs on PHP 9 (while keeping it hardcoded to On for CLI-related ones). * Yes * No ==== Formally deprecate mysqli_execute ==== * Author: Tim Düsterhus mysqli_execute() is an alias of mysqli_stmt_execute() and already marked as deprecated in the documentation (“mysqli_execute() is deprecated and will be removed.”). Its name is misleading since it operates on an mysqli_stmt object, rather than an mysqli object, leading to possible confusion with the mysqli_execute_query() function added in PHP 8.2 that //does// operate on an mysqli object. We therefore propose to add: #[\Deprecated(since: '8.5', message: "use mysqli_stmt_execute() instead")] to the signature. * Yes * No ==== Formally deprecate socket_set_timeout ==== * Author: Tim Düsterhus socket_set_timeout() is an alias of stream_set_timeout() and already marked as deprecated in the documentation (“This function was previously called as set_socket_timeout() and later socket_set_timeout() but this usage is deprecated.”). Its name is misleading since it not part of the ext/sockets extension, which uses the socket_x() prefix. We therefore propose to add: #[\Deprecated(since: '8.5', message: "use stream_set_timeout() instead")] to the signature. * Yes * No ==== Language/syntax deprecations ==== === Deprecate semicolon after case in switch statement === * Author: Theodore Brown It is possible to terminate ''case'' statements with a semicolon instead of the standard colon: switch ($value) { case 'foo'; case 'bar': case 'baz'; echo 'foo, bar, or baz'; break; default; echo 'Other'; } This syntax is a leftover from PHP/FI 2, where nearly all lines including if conditions and case statements were terminated by a semicolon. [[https://externals.io/message/109350#109363|1]] [[https://www.php.net/manual/phpfi2.php#lang|2]] There isn't a need for this syntax to exist anymore, and very few PHP developers are even aware of its existence. In the top 1000 Composer packages, zero out of 35,777 total case statements are using the alternate syntax (as of 2024-11-27). Edit: as of 2025-06-02 there are still zero usages of the alternate syntax in the top 1000 packages (out of 29,858 total case statements). Case statements followed by a semicolon can cause confusion, as a developer may think they behave differently in some way from regular case statements (e.g. preventing fallthrough), when they do not. Therefore, we propose to deprecate terminating case statements with a semicolon. * Yes * No === Deprecate non-standard cast names === * Author: Gina Peter Banyard This supercedes the following inactive RFC [[rfc:deprecate-inconsistent-cast-keywords|Deprecate Inconsistent Cast Names]]. We propose to deprecate the following non-standard cast names: * (integer) * (boolean) * (double) * (binary) This is inline how using non-standard names for built-in types emits a "confusable" warning at compile time, for example: Causes the following warning to be emitted:
Warning: "integer" will be interpreted as a class name. Did you mean "int"? Write "\integer" to suppress this warning
For consistency reasons with the rest of the language we propose to deprecate such casts and recommend using the standard ones. * Yes * No === Deprecate attributes applying to multiple class properties/constants === * Author: Daniel Scherzer Multiple class properties or constants can be declared in a single declaration, e.g. This can be confusing, because the attributes are added to all of the properties/constants in the declaration, not just the first. Because of the potential for confusion, when attributes were added to compile-time constants in [[rfc:attributes-on-constants]], adding attributes to multiple non-class constants at once was not allowed. We now propose to deprecate the same confusing behavior for the existing cases of class properties/constants. * Yes * No === Deprecate backticks as an alias for shell_exec === * Author: Volker Dusch Backticks are a rarely used and somewhat unknown feature of PHP that can cause confusion and hide the need for error handling by not allowing access to exit codes. * In code review, it's hard to spot the difference between ` and ' at a glance. * JavaScript uses it for template strings, causing confusion between the syntax, even for more senior developers switching between the languages. * It's not trivial to look up what the operator does. * Refactoring efforts, even for complex calls, are minimal thanks to <<<'NOWDOC'. * It would eventually free up the backtick operator for future use. * Simplifies the languages for humans, static analysis and other tooling. * Hides the need for escapeshellarg more than other ways of executing shell code. Therefore, we propose to deprecate the backtick operator. * Yes * No === Deprecate __construct() and __destruct() in interfaces === * Authors: * Gina Peter Banyard * Tim Düsterhus In PHP the __construct() magic methods is exempt from LSP compatibility obligations, unless the class implements an interface that declares a __construct() method. However, the __construct() should never be called manually unless when calling parent::__construct() as it is tied to the the new keyword when instantiating a new class. As the instantiation of a class is //dependent// on which class is being instantiated, restricting how it can be done by enforcing a specific signature for __construct() is ill-advised. Similarly, requiring a class to implement a __destruct() is nonsensical as this should never be called manually and a class might not even need one. As such we propose deprecating specifying the __construct() and __destruct() in interfaces. * Yes * No === Deprecate the __sleep() and __wakeup() magic methods === * Author: Gina Peter Banyard Class serialization in PHP has had multiple mechanism introduced to control its behaviour by userland. Initially with the __sleep() and __wakeup() magic methods, then with the Serializable interface and finally with __serialize() and __unserialize(). The last mechanism of __serialize() and __unserialize() was introduced in PHP 7.4 with the [[custom_object_serialization|New custom object serialization mechanism RFC]] to fix deep issues with the way the Serializable interface worked and improve on the usability of the custom serialisation mechanism. The [[phase_out_serializable|Serializable interface is in the process of being phased out]], however no such thing is being done for the initial serialization mechanism of __sleep() and __wakeup(). Having multiple serializations methods which can be supersceded by newer methods is somewhat confusing and add unnecessary complexity to the language. Because the __serialize()/__unserialize() mechanism is a straight up improvement over __sleep()/__wakeup() we propose to deprecate the latter in favour of the former. * Yes * No ==== Deprecate the $exclude_disabled parameter of get_defined_functions() ==== * Author: Gina Peter Banyard As of PHP 8.0.0, functions that are disabled via the ''disable_functions'' INI setting are simply removed from the function table. As such, this parameter no longer has any effect and is pointless. Therefore, we propose to deprecate it. * Yes * No ==== Deprecate building ext/ldap against Oracle LDAP ==== * Author: Christoph M. Becker Building ext/ldap against Oracle LDAP had been supported, and is theoretically still supported, but is apparently [[https://github.com/php/php-src/issues/15051|broken for a while]]. The Oracle LDAP implementation is part of Oracle Instant Client, and uses an older LDAP API; apparently, there are no plans for updating this. So users are almost certainly better off to build against OpenLDAP (and if they need OCI8, to build that as shared library, and load after ext/ldap). Therefore we supposed to deprecate building ext/ldap against Oracle LDAP. It should be noted that the ''ldap_connect_wallet()'' function, [[https://wiki.php.net/rfc/deprecate_functions_with_overloaded_signatures#ldap_connect|available only as of PHP 8.3]], would also be part of the deprecation, as well as other existing Oracle LDAP specific features. ==== Deprecate passing null to readdir(), rewinddir(), and closedir() ==== * Author: Gina Peter Banyard The readdir(), rewinddir(), and closedir() functions accept null as the value for the $dir_handle, in which case it will used the previously opened directory stream opened with opendir(). This is global state that can change if another call to opendir() is made, which could happen by calling a library function, and leading to spooky action at a distance. Prior extensions and functions that behaved this way have been deprecated, most notably the original ext/mysql extension and more recently [[rfc:deprecations_php_8_1#calling_overloaded_pgsql_functions_without_the_connection_argument|the ext/pgsql functions that were overloaded were deprecated in PHP 8.1]] As such, we propose deprecating passing null to readdir(), rewinddir(), and closedir(). * Yes * No ==== Deprecate the $context parameter for finfo_buffer() ==== * Author: Gina Peter Banyard This parameter is unused, and the only reason it exists in the first place is because the implementation of it was delegated to a "god" C function that also was handling finfo_file() which does use the provided context. As this parameter is not useful for this function it should be deprecated. * Yes * No ==== Deprecate no-op functions from the resource to object conversion ==== * Author: Daniel Scherzer In the migration of various extensions from resources to objects, a number of functions to close or free the resources became no-ops. Specifically: * finfo_close() since the ext/fileinfo migration in PHP 8.1 * xml_parser_free() since the ext/xml migration in PHP 8.0 * curl_close() since the ext/curl migration in PHP 8.0 * curl_share_close() since the ext/curl migration in PHP 8.0 * imagedestroy() since the ext/gd migration in PHP 8.0 Since these functions are not needed anymore, they should be deprecated. We therefore propose to add #[\Deprecated(since: '8.5', message: "as this method does nothing")] to the signatures. === Deprecate finfo_close() === * Implementation: https://github.com/php/php-src/pull/18396 * Yes * No === Deprecate xml_parser_free() === * Yes * No === Deprecate curl_close() === * Yes * No === Deprecate curl_share_close() === * Yes * No === Deprecate imagedestroy() === * Yes * No ==== Deprecate DATE_RFC7231 and DateTimeInterface::RFC7231 ==== * Author: Jorg Sowa Because the implementation of the constant contains the timezone `GMT` designation, the format of date will always show the `GMT` timezone, despite the real timezone of the instance of `DateTimeInterface`. This may lead to unwanted results. The implementation of this constant was a mistake, as this was described as bug. This wasn't bug, but a feature request: https://github.com/php/php-src/pull/2450 Implementation: https://github.com/php/php-src/pull/12989 ==== ext/spl deprecations ==== === Deprecate ArrayObject and ArrayIterator with objects === * Author: Daniel Scherzer * Author: Gina Peter Banyard ArrayObject allows using an instance of a class as a "backing array" rather than the more typical behaviour of using a standard PHP array. However the way this feature was designed breaks nearly every single expectation and invariant around objects not just for users, but also the engine. It allows accessing private and protected properties from the global scope, write multiple times to readonly properties, write values of invalid types to typed properties, misshandles isset() and empty(), allows writing dynamic properties without issuing any warning , and can even add dynamic properties to internal classes that ban dynamic properties, doesn't call the magic __get() and __set() methods, and probably some other hidden issues. One example to showcase some of the issues is the following: p; } } $o = new T(); $a = new ArrayObject($o); $a["\0T\0p"] = 'str'; var_dump($o->getP()); $a["\0T\0p"] = 'modified'; var_dump($o->getP()); var_dump(isset($a["p2"])); var_dump($o->p2); which outputs the following: string(3) "str" string(8) "modified" bool(true) Fatal error: Uncaught Error: Typed property T::$p2 must not be accessed before initialization in php-wasm run script:%d Stack trace: #0 {main} thrown in php-wasm run script on line %d As such we recommend deprecating passing an object to the constructor of ArrayObject. Because ArrayIterator shares a lot of the implementation details with ArrayObject we also recommend deprecating passing objects to it. * Yes * No === Deprecate SplObjectStorage::contains(), SplObjectStorage::attach(), and SplObjectStorage::detach() === * Author: Gina Peter Banyard This is lifted from the [[rfc:container-offset-behaviour|Improve language coherence for the behaviour of offsets and containers]] RFC as it is time sensitive. The following three methods are aliases for methods defined by ArrayAccess, namely: * SplObjectStorage::contains() is an alias for SplObjectStorage::offsetExists() * SplObjectStorage::attach() is an alias for SplObjectStorage::offsetSet() * SplObjectStorage::detach() is an alias for SplObjectStorage::offsetUnset() However, extending `SplObjectStorage` and overwriting one of the alias methods does //not// modify the behaviour of using the offset access operators. Meaning that it is possible to have an asymetry between using methods calls and the overloaded offset access operators. As such we propose deprecating the method aliases in favour of using the ArrayAccess methods. * Yes * No === Deprecate passing spl_autoload_call() to spl_autoload_unregister() === * Author: Gina Peter Banyard The spl_autoload_register() function allows registering a callable to find classes during autoloading. Naturally it prevents passing the spl_autoload_call() as a callback, as this would be non-sensical. However, the spl_autoload_unregister() function, which is used to remove a callback from the autoloading sequence does permit passing the spl_autoload_call() function as a callback, in which case **//all//** autoloading callbacks are removed. This behaviour is undocumented and frankly baffling, as such we propose to deprecate passing spl_autoload_call() to spl_autoload_unregister(). If there is a need to remove all autoloading functions it is possible to do so via the following obvious method: foreach (spl_autoload_functions() as $fn) { spl_autoload_unregister($fn); } * Yes * No ==== Deprecate using values of type null, bool, and float as array offsets and when calling array_key_exists() ==== * Author: Gina Peter Banyard This is lifted from the [[rfc:container-offset-behaviour|Improve language coherence for the behaviour of offsets and containers]] RFC as it is time sensitive. How offsets are handled in PHP is rather unintuitive and not consistent with many other areas of the language. Notably an offset of null is type juggled to the empty string "" rather than 0, contrasting with how values of type bool, float, and resources are cast to int. Which is even more surprising that the other operators that accept both string and int are bitwise operators and they throw a type error when attempting to use null with a value of type string. Moreover, invalid types for string offsets always warn, which is not the case for array offsets (except when using a resource). As such we propose deprecating using values of type null, bool, and float as array offsets and when calling array_key_exists() * Yes * No ==== Deprecate __debugInfo() returning null ==== * Author: Daniel Scherzer Since the __debugInfo() magic method was added, it has been documented as needing to return an array (see [[https://github.com/php/php-src/commit/1e752ce9c57310cced0a5ba8399778c1f500f2b4|original implementation]]) in the error message if you return a non-array and non-null value. However, null has been silently treated the same as an empty array. This can result in confusion about if null means something different. Accordingly, we propose to deprecate returning null from __debugInfo() magic methods. ==== Deprecate constant redeclaration ==== * Author: Daniel Scherzer In PHP 8, attempting to declare a constant that already existing was changed to emit a warning rather than just a notice. Since a warning is considered stronger than a deprecation, the error code should not change, but we propose that * The behavior trigger an error in PHP 9 * The warning messages in 8.5+ indicate that the behavior will trigger an error in PHP 9 Redeclaring a constant can lead to invalid assumptions, specifically if a developer assumes that the declaration will succeed they may assume a constant has one value when it actually has a different value. ==== Deprecate closure binding issues ==== * Author: Daniel Scherzer When trying to bind a Closure object, multiple issues can potentially be raised, but only as warnings: * "Cannot bind an instance to a static closure" * "Cannot bind method %s::%s() to object of class %s" - for trying to change the type of object bound to * "Cannot unbind $this of method" * "Cannot unbind $this of closure using $this" * "Cannot bind closure to scope of internal class %s" * "Cannot rebind scope of closure created from function" * "Cannot rebind scope of closure created from method" Since a warning is considered stronger than a deprecation, the error code should not change, but we propose that * The behavior trigger errors in PHP 9 * The warning messages in 8.5+ indicate that the behavior will trigger errors in PHP 9 ==== Enact follow-up phase of the "Path to Saner Increment/Decrement operators" RFC ==== * Author: Gina Peter Banyard The [[rfc:saner-inc-dec-operators|Path to Saner Increment/Decrement operators]] RFC delayed the deprecation of the PERL string increment feature so that users could have time to migrate to the new str_increment() function introduced in PHP 8.3. As there have been two minor versions to allow this migration we now proposed to enact the following delayed deprecation from the RFC:
Deprecate using the increment operator with non-numeric strings.
* Yes * No ==== Remove disable_classes INI setting ==== * Author: Gina Peter Banyard This supercedes the following inactive RFC [[rfc:remove_disable_classes|Remove disable_classes INI setting]]. The ''disable_classes'' INI setting is fundamentally useless and causes various issues in the engine, as using it will result in unexpected states for the engine to deal with. Only internal classes can be disabled, which brings the following observation. On a minimal build of PHP, with only the mandatory extensions enabled, there are 148 classes/interfaces/traits defined. [2] Other than a specific subset of classes defined in the SPL extension, disabling any of these classes will cause issues within the engine. Moreover, the subset of SPL classes that could be disabled are not a security concern. Therefore, any other class that can be disabled must come from an extension that can be disabled altogether. And "disabling" a class from an extension without disabling said extension will, other than rendering it useless, cause various issues that the extension is not prepared to handle as they expect the classes they defined to exist. If a hosting provider is concerned about an extension, then it should not enable it in the first place. Not break it ad hoc. Considering the above, the usefulness of this feature has always been dubious. This is in stark contrast to the ''disable_functions'' INI setting, which can be used to selectively remove functionality of an extension without breaking it overall. What makes this setting particularly broken is that it does not unregister the class, it only overwrites the create CE handler to emit a warning and purge the properties and function hash tables. This leads to various use after free, segfaults, and broken expectations for the engine and extensions which define said classes. On top of that, it is possible to actually instantiate such a class (and even classes which actually disallow this like ''ext/imap'') in userland, and pass it to functions that are typed against said class without raising a TypeError. However, when trying to do anything with said object, stuff is going to explode in countless ways. The only usage we can find is within the Fuzzer SAPI to block instantiation of the InfiniteIterator class so that it does not try to fuzz this class. This can be fixed by manually changing the create handler of the InfiniteIterator CE to throw in the fuzzer initiation code. As such we propose removing the ''disable_classes'' INI setting. * Yes * No ==== Deprecate the $http_response_header predefined variable ==== * Author: Gina Peter Banyard This was initially slated for [[rfc:deprecations_php_8_1#predefined_variable_http_response_header|deprecation in PHP 8.1]], however it was removed from the proposal as there were no practical alternatives for this functionality. This was addressed in PHP 8.4 by adding the [[rfc:http-last-response-headers|http_get_last_response_headers() function]]. As the usage of this predefined variable is small, at the time of the RFC introducing the new functions, 65 times. And it is possible to write cross version compatible code in the following way: $content = file_get_contents('http://example.com/'); if (function_exists('http_get_last_response_headers')) { $http_response_header = http_get_last_response_headers(); } // Use $http_response_header as before We propose deprecating using the $http_response_header without having previously called http_get_last_response_headers() * Yes * No ===== Backward Incompatible Changes ===== For PHP 8.5 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.