====== PHP RFC: Deprecate functions with overloaded signatures ====== * Date: 2023-01-31 * Author: Máté Kocsis * Target version: PHP 8.3 * Status: Implemented * Implementation: https://github.com/php/php-src/pull/11703 ===== Introduction ===== [[https://www.mygreatlearning.com/blog/method-overloading-in-java/|Method overloading]] may sound very familiar for Java developers, but PHP doesn't natively support this concept. In spite of this fact, due to historical reasons, many internal PHP functions and methods are implemented so that they can only be expressed with multiple signatures - making them overloaded after all. In Java, being able to support multiple parameter types requires overloading function signatures, but it's not the case anymore with PHP since the introduction of union types. However, the predominant type of overloading is caused by functions accepting a varying number of parameters. Usually, these functions don't have clear default value handling semantics. But what does this mean in practice? A function has clear default value handling semantics if it has well-defined parameter default values, meaning that it behaves the same way when: * no optional parameters are explicitly passed to it (so their default values are implicitly used) * any of their optional parameters is explicitly passed its default value function foo($bar = null) { ... } foo(); foo(null); // The two foo() invocations must behave the same way Besides, PHP has a few unconventionally overloaded methods whose behavior depend on whether they are accessed statically or as an instance method. From year to year, PHP has less and less inconsistencies between internal and userland code, so in order to continue the effort, we should either add native support for signature overloading, or phase out the overloaded internal function signatures. As the former solution is widely known to be [[https://github.com/Danack/RfcCodex/blob/master/method_overloading.md|impossible to implement in an effective way]], the latter solution is the preferred choice of this RFC. Maintaining consistency is not the only motivation why we should get rid of this long standing issue though. Before PHP 8.0, overloaded signatures didn't cause a big harm, except for making the usability of the functions in question worse as well as their behavior more surprising due to the fact that these functions usually don't have meaningful parameter names and clear default value handling semantics. However, as of PHP 8.0, these have become ever more important due to the following reasons: * Since PHP 8.0, ''ReflectionParameter'' supports retrieving the default parameter values of internal functions, but only when the default value exists * In case of some overloaded functions, some parameters cannot be named properly, since they do completely different things in case of the different signatures (see [[https://www.php.net/manual/en/function.session-set-save-handler.php|session_set_save_handler()]] for example). Even though this imposes some problems for the documentation, it's mainly an issue when using [[rfc:named_params|Named Arguments]], because a parameter can only have a single name, so it's not possible to convey their true meaning by referring to them by their name. * Furthermore, [[rfc:named_params|Named Arguments]] supports skipping optional parameters (as in implicitly passing their default value), but only when the default value exists. * For ~2 years now, the online manual at [[https://php.net|php.net]] is semi-automatically updated based on the exact signatures known by php-src, which are collected via the [[https://externals.io/message/105970|so-called stubs]]. Unfortunately, overloaded functions cannot be updated with our tooling, possibly leaving these signatures outdated, displaying the wrong parameter names and types, as well as missing proper default values. After the named arguments RFC was accepted, dozens (if not hundreds) of functions were fixed in PHP 8.0 so that they started to behave according to clear default parameter value handling semantics. Usually, this required us to make the parameters nullable, but sometimes it was enough to make a few small adjustments to the parameter parsing code. As all the easier cases are fixed for quite a long time, this RFC tries to tackle the ones with some BC impact. ===== Proposal ===== This RFC consists of multiple votes to phase out the below mentioned overloaded function signatures over the course of either the 8.x or the 9.x version series, depending on the outcome of each vote. In order to reduce the BC impact and ease gradual migration, PHP 8.3 would only add the suggested replacements first if one is needed, then the next minor version (referred to as PHP 8.4 for simplicity) would actually deprecate the affected signatures. Finally, either the subsequent major PHP version (referred to as PHP 9.0 for simplicity) or the next after the next one (referred to as PHP 10.0 for simplicity) would completely remove support for them. If a suggested replacement for a signature is already available, then PHP 8.3 would deprecate it directly. In some cases (mostly, when a signature is rare), only PHP 9.0 is proposed as the target version of the scheduled removal. Most votes about phasing out an overloaded signature have three choices: * **Short path**: a signature becomes deprecated in PHP 8.4 (or in PHP 8.3 when there is no need for introducing an alternative), and it will be removed //preferably// in PHP 9.0, or in 10.0 at last. * **Long path**: a signature becomes deprecated in PHP 8.4 (or in PHP 8.3 when there is no need for introducing an alternative), and it will be removed in PHP 10.0. * **No**: the signature should be neither deprecated, nor removed. A few votes only include the "Short path" and "No" options. Please refer to the the first paragraph in the section for more details. **When will a signature be deprecated?** When the "Short path" combined with the "Long path" get 2/3 majority against the "No" votes. Otherwise, the signatures in question won't be deprecated. **When will a signature be removed?** * **in PHP 9.0:** when the "Short path" gets 2/3 majority against the other options * **in PHP 10.0:** when the "Short path" combined with the "Long path" get 2/3 majority against the "No" votes Otherwise the signatures in question won't be removed. Please refer to [[https://externals.io/message/120146#120238|https://externals.io/message/120146#120238]] for the reasoning behind the voting system and for an example scenario. ==== array_keys() ==== ''array_keys()'' currently supports two signatures: the former one returns all keys of ''$array'', while the latter one returns the keys for those items where the value matches the ''$filter_value'' criteria. function array_keys(array $array): array {} function array_keys(array $array, mixed $filter_value, bool $strict = false): array {} This RFC proposes to add a new function as a replacement for the 2nd signature in PHP 8.3, and deprecate calling ''array_keys()'' with 2 or more arguments in PHP 8.4. Finally, the deprecated signature would become unsupported either in PHP 9.0 or 10.0, resulting in the following functions: function array_keys(array $array): array {} function array_keys_filter(array $array, mixed $filter_value, bool $strict = false): array {} **Suggested backward compatible alternative for PHP <8.3:** code which calls ''array_keys()'' with 2 or more arguments should either: * declare ''array_keys_filter()'' via a [[https://github.com/symfony/polyfill|polyfill]], and use the new function from that point forward * use the combination of ''array_filter()'' and ''array_keys()'' to retain the original behavior: array_keys( array_filter( $array, function ($value, $key) { return $value == ARRAY_VALUE_TO_BE_FILTERED; }, ARRAY_FILTER_USE_BOTH ) ); **Impact analysis:** Impact analysis was not performed due to implementation difficulties and the perceived low overall effect of the deprecation: even though the 1 parameter version of ''array_keys()'' is one of the most widespread functions, the deprecation only affects its 2+ parameter signature which is way less known and used. * Yes, deprecate in 8.4, remove in 9.0 or 10.0 * Yes, deprecate in 8.4, remove in 10.0 * No ==== DatePeriod::__construct() ==== ''DatePeriod::%%__construct()%%'' currently supports three signatures, all of which instantiate the ''DatePeriod'' class in a slightly different way. class DatePeriod { ... public function __construct(DateTimeInterface $start, DateInterval $interval, int $recurrences, int $options = 0) {} public function __construct(DateTimeInterface $start, DateInterval $interval, DateTimeInterface $end, int $options = 0) {} public function __construct(string $isostr, int $options = 0) {} ... } This RFC proposes to add a new factory method as a replacement for the 3rd signature in PHP 8.3 and deprecate calling the constructor with 1 or 2 arguments in PHP 8.4. Finally, the deprecated signature would become unsupported either in PHP 9.0 or 10.0, resulting in the following methods: class DatePeriod { ... public function __construct(DateTimeInterface $start, DateInterval $interval, DateTimeInterface|int $end, int $options = 0) {} public static function createFromISO8601String(string $specification, int $options = 0): static {} ... } **Suggested backward compatible alternative for PHP <8.3:** code which calls ''DatePeriod::%%__construct()%%'' with 1 or 2 arguments should be modified to conditionally use the appropriate method based on the PHP version. Using a wrapper class is preferable when possible. **Impact analysis:** None of the 2000 most popular PHP packages rely on instantiating ''DatePeriod'' with 1 or 2 arguments. * Yes, deprecate in 8.4, remove in 9.0 or 10.0 * Yes, deprecate in 8.4, remove in 10.0 * No ==== dba_fetch() ==== ''dba_fetch()'' serves for fetching data from Berkeley DB style databases, and currently supports two signatures: function dba_fetch(string|array $key, resource $dba, int $skip = 0): string|false {} function dba_fetch(string|array $key, int $skip, resource $dba): string|false {} Oddly, the ''$dba'' and ''$skip'' parameters flip depending on the number of arguments passed. This RFC proposes to deprecate the latter signature in PHP 8.3, and then calling ''dba_fetch()'' with the ''$dba'' parameter at the 3rd position would become unsupported in PHP 9.0, resulting in the following function: function dba_fetch(string|array $key, resource $dba, int $skip = 0): string|false {} **Suggested backward compatible alternative for * Yes, deprecate in 8.3, remove in 9.0 * No ==== FFI::cast(), FFI::new(), and FFI::type() ==== ''FFI::cast()'', ''FFI::new()'', and ''FFI::type()'' currently support two signatures each: the static ones operate on predefined types (e.g. ''int''), while their instance method signatures //also// support cdef types (e.g.: ''$x = FFI::cdef(...); $x->new();''). class FFI { ... public static function cast(FFI\CType|string $type, FFI\CData|int|float|bool|null &$ptr): ?FFI\CData {} public function cast(FFI\CType|string $type, FFI\CData|int|float|bool|null &$ptr): ?FFI\CData {} public static function new(FFI\CType|string $type, bool $owned = true, bool $persistent = false): ?FFI\CData {} public function new(FFI\CType|string $type, bool $owned = true, bool $persistent = false): ?FFI\CData {} public static function type(string $type): ?FFI\CType {} public function type(string $type): ?FFI\CType {} ... } Since invoking ''FFI::cast()'', ''FFI::new()'', and ''FFI::type()'' as instance methods is more universally usable, this RFC proposes to deprecate their static counterparts in PHP 8.3. Calling these methods statically would become unsupported either in PHP 9.0 or 10.0, resulting in the following methods: class FFI { ... public function cast(FFI\CType|string $type, FFI\CData|int|float|bool|null &$ptr): ?FFI\CData {} public function new(FFI\CType|string $type, bool $owned = true, bool $persistent = false): ?FFI\CData {} public function type(string $type): ?FFI\CType {} ... } **Suggested backward compatible alternative:** code which invokes the affected methods statically should be modified to call them as instance methods. **Impact analysis:** [[https://gist.github.com/kocsismate/4592eecdd7ad82e0d00e49e5514d32eb|1 out of the 2000]] most popular PHP packages relies on calling any of ''FFI::cast()'', ''FFI::new()'', and ''FFI::type()'' statically. * Yes, deprecate in 8.3, remove in 9.0 or 10.0 * Yes, deprecate in 8.3, remove in 10.0 * No ==== get_class() and get_parent_class() ==== ''get_class()'' currently supports two signatures: the first one retrieves the class name of the provided object, while the latter retrieves the class name of the current class context. function get_class(object $object): string {} function get_class(): string {} These are due to the [[rfc:get_class_disallow_null_parameter|get_class() disallow null parameter]] RFC which was implemented in PHP 7.2. As it can be seen from the RFC, the parameter of ''get_class'' doesn't have a well-defined default value anymore. Additionally, the ''get_parent_class()'' function behaves the same way: function get_parent_class(object|string $object_or_class): string {} function get_parent_class(): string {} This RFC proposes to deprecate the signatures without parameters in PHP 8.3, and then calling ''get_class()'' as well as ''get_parent_class()'' without parameters would become unsupported either in PHP 9.0 or 10.0, resulting in the following functions: function get_class(object $object): string {} function get_parent_class(object|string $object_or_string): string {} **Suggested backward compatible alternative:** code which invokes ''get_class()'' without parameters should be modified to use ''%%self::class%%'' or ''get_class($this)'' instead, while code which invokes ''get_parent_class()'' without parameters should be modified to use ''%%parent::class%%'' or ''ReflectionClass::getParentClass()'' instead. **Impact analysis:** [[https://gist.github.com/kocsismate/cc41145f6cf96854608de042f8a3048b|10 out of the 2000]] most popular PHP packages rely on calling ''get_class()'' with 0 arguments, while none of the projects rely on calling ''get_parent_class()'' with 0 arguments. * Yes, deprecate in 8.3, remove in 9.0 or 10.0 * Yes, deprecate in 8.3, remove in 10.0 * No ==== IntlCalendar::set() ==== ''IntlCalendar::set()'' currently supports the following signatures: the first one modifies a [[https://www.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants|given field]], while the latter ones modify several of them at once. class IntlCalendar { ... public function set(int $field, int $value): true {} public function set(int $year, int $month, int $dayOfMonth): true {} public function set(int $year, int $month, int $dayOfMonth, int $hour, int $minute): true {} public function set(int $year, int $month, int $dayOfMonth, int $hour, int $minute, int $second): true {} ... } It's important to note that it's not possible call ''IntlCalendar::set()'' with 4 arguments, as the ICU library which is triggered under the hood [[https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1Calendar.html#afb3dbfabf06d5cc6f6b0127061c09b61|neither supports it]]. This RFC proposes to add two new methods as a replacement for the latter three signatures in PHP 8.3 and deprecate calling the ''set()'' method with 3 or more arguments in PHP 8.4. Finally, the deprecated signatures would become unsupported either in PHP 9.0 or 10.0, resulting in the following methods: class IntlCalendar { ... public function set(int $field, int $value): true {} public function setDate(int $year, int $month, int $dayOfMonth): void {} public function setDateTime(int $year, int $month, int $dayOfMonth, int $hour, int $minute, ?int $second = null): void {} ... } Please note that the newly added methods have a ''void'' return type. This is possible because ''IntlCalendar::set()'' always returns ''true''. As ''IntlCalendar::set()'' is an alias of ''intlcal_set()'', the deprecation and then the removal would apply to the function as well. Since the procedural-style API of the intl extension is [[https://externals.io/message/114473#114673|considered legacy]], this RFC proposes to deprecate ''intlcal_set()'' as of PHP 8.4, and remove it either in PHP 9.0 or 10.0. **Suggested backward compatible alternative for PHP <8.3:** code which calls ''IntlCalendar::set()'' with 3 or more arguments should be modified to either: * set the different fields one by one, as this is what the underlying library [[https://github.com/unicode-org/icu/blob/maint/maint-73/icu4c/source/i18n/calendar.cpp#L1237|does under the hood]] * conditionally invoke the appropriate signature based on the PHP version. Using a wrapper class is preferable when possible. **Impact analysis:** None of the 2000 most popular PHP packages rely on calling ''IntlCalendar::set()'' or ''intlcal_set()''. * Yes, deprecate in 8.4, remove in 9.0 or 10.0 * Yes, deprecate in 8.4, remove in 10.0 * No ==== IntlGregorianCalendar::__construct() ==== ''IntlGregorianCalendar::%%__construct()%%'' currently supports three signatures, all of which instantiate the ''IntlGregorianCalendar'' class based on different data. class IntlGregorianCalendar { ... public function __construct(IntlTimeZone|DateTimeZone|string|null $timezone = null, ?string $locale = null) {} public function __construct(int $year, int $month, int $dayOfMonth) {} public function __construct(int $year, int $month, int $dayOfMonth, int $hour, int $minute, ?int second = null) {} ... } It's important to note that it's not possible call ''%%IntlGregorianCalendar::__construct()%%'' with 4 arguments, as the ICU library which is triggered under the hood [[https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1GregorianCalendar.html#aef388a4067f3c53f3fa38068c8be012d|neither supports it]]. This RFC proposes to add two new static methods as a replacement for the latter two signatures in PHP 8.3 and deprecate calling the constructor with more than two arguments in PHP 8.4. Finally, the deprecated signatures would become unsupported either in PHP 9.0 or 10.0, resulting in the following methods: class IntlGregorianCalendar { ... public function __construct(IntlTimeZone|DateTimeZone|string|null $timezone = null, ?string $locale = null) {} public static function createFromDate(int $year, int $month, int $dayOfMonth): static {} public static function createFromDateTime(int $year, int $month, int $dayOfMonth, int $hour, int $minute, ?int second = null): static {} ... } As ''IntlGregorianCalendar::%%__construct()%%'' shares the same implementation with ''intlgregcal_create_instance()'', they have the same issues. Since the procedural-style API of the intl extension is [[https://externals.io/message/114473#114673|considered legacy]], this RFC proposes to deprecate ''intlgregcal_create_instance()'' as of PHP 8.4, and remove it either in PHP 9.0 or 10.0. **Suggested backward compatible alternative for PHP <8.3:** code which calls ''IntlGregorianCalendar::%%__construct()%%'' with 3 or more arguments should be modified to conditionally invoke the appropriate signature based on the PHP version. Using a wrapper class is preferable when possible. **Impact analysis:** None of the 2000 most popular PHP packages rely on calling ''%%IntlGregorianCalendar::__construct()%%'' or ''intlgregcal_create_instance()''. * Yes, deprecate in 8.4, remove in 9.0 or 10.0 * Yes, deprecate in 8.4, remove in 10.0 * No ==== ldap_connect() ==== ''ldap_connect()'' currently supports two signatures depending on the platform: by default, the first one is available, except when PHP is compiled with OracleLDAP. function ldap_connect(?string $uri = null, int $port = 389): LDAP\Connection|false {} function ldap_connect( ?string $uri, int $port, string $wallet, string $password, int $auth_mode ): LDAP\Connection|false {} This RFC proposes to add a new function as a replacement for the 2nd signature (when PHP is compiled with OracleLDAP) in PHP 8.3, and deprecate calling ''ldap_connect()'' with 3 or more arguments in PHP 8.4. Finally, the deprecated signature would become unsupported in PHP 9.0, resulting in the following functions: function ldap_connect(?string $uri = null, int $port = 389): LDAP\Connection|false {} function ldap_connect_wallet(?string $uri, string $wallet, string $password, int $auth_mode): LDAP\Connection|false {} The new function doesn't have the ''$port'' parameter because it's currently being deprecated by [[rfc:deprecations_php_8_3|Deprecations for PHP 8.3]]. **Suggested backward compatible alternative for PHP <8.3:** code which calls ''ldap_connect()'' with 3 or more arguments should declare ''ldap_connect_wallet()'' via a [[https://github.com/symfony/polyfill|polyfill]], and use the new function from that point forward. **Impact analysis:** None of the 2000 most popular PHP packages rely on calling ''ldap_connect()'' with 3 or more arguments. * Yes, deprecate in 8.4, remove in 9.0 * No ==== ldap_exop() ==== ''ldap_exop()'' currently supports two signatures: the former one performs an extended LDAP operation asynchronously, while the latter one performs it synchronously depending on whether the ''$response_data'' parameter is provided. function ldap_exop( LDAP\Connection $ldap, string $request_oid, ?string $request_data = null, ?array $controls = null ): LDAP\Result|false {} /** * @param string $response_data * @param string $response_oid */ function ldap_exop( LDAP\Connection $ldap, string $request_oid, ?string $request_data = null, ?array $controls = null, &$response_data, &$response_oid ): bool {} This RFC proposes to add a new function as a replacement for the 2nd signature, and deprecate calling ''ldap_exop()'' with 5 or more arguments in PHP 8.4. Finally, the deprecated signature would become unsupported in PHP 9.0, resulting in the following functions: function ldap_exop( LDAP\Connection $ldap, string $request_oid, ?string $request_data = null, ?array $controls = null ): LDAP\Result|false {} /** * @param string $response_data * @param string $response_oid */ function ldap_exop_sync( LDAP\Connection $ldap, string $request_oid, ?string $request_data = null, ?array $controls = null, &$response_data = null, &$response_oid = null ): bool {} **Suggested backward compatible alternative for PHP <8.3:** code which calls ''ldap_exop()'' with 5 or more arguments should either declare ''ldap_exop_sync()'' via a [[https://github.com/symfony/polyfill|polyfill]], and use the new function from that point forward, or the async API should be used instead. **Impact analysis:** [[https://gist.github.com/kocsismate/5d44051be4d2b571f05b3dec044e0303|1 out of the 2000]] most popular PHP packages rely on calling ''ldap_exop()'' with 5 or more arguments. * Yes, deprecate in 8.4, remove in 9.0 * No ==== pg_fetch_result(), pg_field_prtlen(), and pg_field_is_null() ==== ''pg_fetch_result()'', ''pg_field_prtlen()'', and ''pg_field_is_null()'' currently support two signatures each: function pg_fetch_result(PgSql\Result $result, mixed $field): string|false|null {} function pg_fetch_result(PgSql\Result $result, int $row, mixed $field): string|false|null {} function pg_field_prtlen(PgSql\Result $result, string|int $field): int|false {} function pg_field_prtlen(PgSql\Result $result, int $row, string|int $field): int|false {} function pg_field_is_null(PgSql\Result $result, string|int $field): int|false {} function pg_field_is_null(PgSql\Result $result, int $row, string|int $field): int|false {} This RFC proposes to make the ''$row'' parameters nullable in PHP 8.3, and deprecate the 2 parameter signatures (which don't have ''$row'') in PHP 8.4. Finally, calling ''pg_fetch_result()'', ''pg_field_prtlen()'', and ''pg_field_is_null()'' without passing either the ''$row'' or the 3rd parameter would become unsupported either in PHP 9.0 or 10.0, resulting in the following functions: function pg_fetch_result(PgSql\Result $result, ?int $row, mixed $field): string|false|null {} function pg_field_prtlen(PgSql\Result $result, ?int $row, string|int $field): int|false {} function pg_field_is_null(PgSql\Result $result, ?int $row, string|int $field): int|false {} Since all these functions have an alias (''pg_result()'', ''pg_fieldprtlen()'', and ''pg_fieldisnull'' respectively), the deprecations would affect them as well. However, as these aliases have already been deprecated, there is no use to change them in any way. That's why this RFC proposes not to implement the above mentioned changes in case of the aliases in question. **Suggested backward compatible alternative for PHP <8.3:** code which calls the affected functions without providing the ''$row'' parameter should be modified to conditionally invoke the appropriate signature based on the PHP version. Using a wrapper function is preferable when possible. **Impact analysis:** [[https://gist.github.com/kocsismate/1fa5f1c6a83fdbdfb016eeade5ae4128|1 out of the 2000]] most popular PHP packages relies on calling ''pg_field_prtlen()'' and ''pg_field_is_null()'' with 2 arguments. None of the 2000 most popular PHP packages relies on calling ''pg_fetch_result()''. * Yes, deprecate in 8.4, remove in 9.0 or 10.0 * Yes, deprecate in 8.4, remove in 10.0 * No ==== Phar::setStub() ==== ''Phar::setStub()'' currently supports two signatures which create a [[https://www.php.net/manual/en/phar.fileformat.stub.php|stub file]] based on either a string or a resource. class Phar { ... public function setStub(string $string) {} public function setStub(resource $resource, int $length) {} ... } This RFC proposes to deprecate the latter signature in PHP 8.3 and then remove its support in PHP 9.0, resulting in the following method: class Phar { ... public function setStub(string $string) {} ... } ''Phar::setStub($resource, $length)'' reads the input resource into memory as a string and then continues evaluation just like if a string was passed. That's why this signature is trivial to replace with ''$phar->setStub(stream_get_contents($resource));'' As the implementation of ''Phar::setStub()'' is "aliased" to ''PharData::setStub()'', this RFC proposes to implement the above mentioned changes in case of ''PharData::setStub()'' as well. **Suggested backward compatible alternative:** code which calls ''Phar::setStub()'' or ''PharData::setStub()'' with a resource argument should rather pass ''stream_get_contents($resource)''. **Impact analysis:** None of the 2000 most popular PHP packages rely on calling ''Phar::setStub()'' or ''PharData::setStub()'' with a resource argument. * Yes, deprecate in 8.3, remove in 9.0 * No ==== ReflectionMethod::__construct() ==== ''ReflectionMethod::%%__construct()%%'' currently supports two signatures: the former one instantiates the ''ReflectionMethod'' class based on an object/class name and a method name, while the latter one uses a string consisting of a class name and a method name separated by "::" (e.g.: ''Foo::bar''). class ReflectionMethod { ... public function __construct(object|string $objectOrClass, string $method) {} public function __construct(string $classMethod) {} ... } This RFC proposes to add the following new factory method in PHP 8.3 and deprecate the second constructor signature in PHP 8.4. Finally, the deprecated signature would become unsupported in either PHP 9.0 or 10.0, resulting in the following methods: class ReflectionMethod { ... public function __construct(object|string $objectOrClass, string $method) {} public static function createFromMethodName(string $method): static {} ... } **Suggested backward compatible alternative for PHP <8.3:** code which calls ''ReflectionMethod::%%__construct()%%'' with a single string argument should explode the string into an array (''$rm = new ReflectionMethod(%%...%%explode("::", $string));''). **Impact analysis:** [[https://gist.github.com/kocsismate/b457f8fef074039b58fbee9121d05e92|10 out of the 2000]] most popular PHP packages rely on calling ''ReflectionMethod::%%__construct()%%'' with 1 argument. * Yes, deprecate in 8.4, remove in 9.0 or 10.0 * Yes, deprecate in 8.4, remove in 10.0 * No ==== ReflectionProperty::setValue() ==== ''ReflectionProperty::setValue()'' currently supports three signatures: class ReflectionProperty { ... public function setValue(object $object, mixed $value): void {} public function setValue(mixed $unused, mixed $value): void {} public function setValue(mixed $value): void {} ... } The latter two signatures are exclusive to static properties for which an object instance is unnecessary to operate on. The ''$unused'' parameter is actually completely disregarded when the property is static. This RFC proposes to make the ''$object'' parameter nullable in PHP 8.3, and deprecate the latter two signatures of ''ReflectionProperty::setValue()''. Finally, the deprecated signatures would become unsupported in PHP 9.0, resulting in the following method: class ReflectionProperty { ... public function setValue(?object $object, mixed $value): void {} ... } The ''$object'' parameter must be ''object'' for instance properties. Conversely, it can be either ''null'' or ''object'' for static properties. **Suggested backward compatible alternative:** code which invokes ''ReflectionProperty::setValue()'' for a static property with a single argument should be modified to pass ''null'' as the first parameter, code which invokes ''ReflectionProperty::setValue()'' for a static property with an incorrect first argument (which is neither ''object'', nor ''null'') should pass ''null'' instead. **Impact analysis:** Impact analysis was not performed due to implementation difficulties and the perceived low overall effect of the deprecation: it only affects static properties where the ''setValue()'' method either passed one argument, or the first argument is incorrect (which should be very rare). Also, the suggested backward compatible alternative is available at least since PHP 5.2, so the "fix" is trivially backward compatible. * Yes, deprecate in 8.3, remove in 9.0 * No ==== session_set_save_handler() ==== ''session_set_save_handler()'' currently supports two signatures: function session_set_save_handler(SessionHandlerInterface $session_handler, bool $register_shutdown = true): bool {} function session_set_save_handler( callable $open, callable $close, callable $read, callable $write, callable $destroy, callable $gc, ?callable $create_sid = null, ?callable $validate_sid = null, ?callable $update_timestamp = null): bool {} This RFC proposes to deprecate calling ''session_set_save_handler()'' with 6 or more arguments in PHP 8.4. Finally, the deprecated signature would become unsupported either in PHP 9.0 or 10.0, resulting in the following signature: function session_set_save_handler(SessionHandlerInterface $session_handler, bool $register_shutdown = true): bool {} **Suggested backward compatible alternative:** code which calls ''session_set_save_handler()'' with 6 or more arguments should wrap the callbacks into a ''SessionHandlerInterface'' implementation. **Impact analysis:** [[https://gist.github.com/kocsismate/6ea902ada1eb959b5642e274844b26c0|1 out of the 2000]] most popular PHP packages rely on calling ''session_set_save_handler()'' with 6 or more arguments. * Yes, deprecate in 8.4, remove in 9.0 or 10.0 * Yes, deprecate in 8.4, remove in 10.0 * No ==== stream_context_set_option() ==== ''stream_context_set_option()'' currently supports two signatures: the former one sets a single config option for a stream wrapper, while the latter one sets an array of stream context options. /** @param resource $context */ function stream_context_set_option( $context, string $wrapper, string $option_name, mixed $value ): true {} /** @param resource $context */ function stream_context_set_option($context, array $options): bool {} This RFC proposes to add a new function as a replacement for the 2nd signature in PHP 8.3, and deprecate calling ''stream_context_set_option()'' with 2 arguments in PHP 8.4. Finally, the deprecated signature would become unsupported either in PHP 9.0 or 10.0, resulting in the following functions: function stream_context_set_option( $context, string $wrapper, string $option_name, mixed $value ): true {} /** @param resource $context */ function stream_context_set_options($context, array $options): bool {} **Suggested backward compatible alternative for PHP <8.3:** code which calls ''stream_context_set_option()'' with 2 arguments should either: * declare ''stream_context_set_options()'' via a [[https://github.com/symfony/polyfill|polyfill]], and use the new function from that point forward * use the already available ''stream_context_set_parameters()'' function instead with a slightly modified ''$params'' argument which has the following shape: ''array{notification?: mixed, options?: array}'' **Impact analysis:** [[https://gist.github.com/kocsismate/2e0625f9a95ad1e3e29a8660461de425|10 out of the 2000]] most popular PHP packages rely on calling ''stream_context_set_option()'' with 2 arguments. * Yes, deprecate in 8.4, remove in 9.0 or 10.0 * Yes, deprecate in 8.4, remove in 10.0 * No ==== Policy about new functions ==== Newly added internal functions and methods must not have overloaded signatures. Existing, not overloaded ones cannot be modified to become overloaded. * Yes * No ===== Backwards incompatible changes ===== Additional deprecation notices will start to appear in PHP 8.3 and 8.4. In PHP 9.0 and/or PHP 10.0, the previously deprecated functionality will be removed. ===== Impact on extensions ===== None. ===== Future scope ===== Unfortunately, the removal of a few overloaded functions and methods is not covered by this or any other RFCs. These should be tackled later in order to completely get rid of overloaded signatures. The list of overloaded functions and methods with ill-defined default values (denoted by ''UNKNOWN'') not yet deprecated by any RFCs: function array_walk(array|object &$array, callable $callback, mixed $arg = UNKNOWN): true {} function array_walk_recursive(array|object &$array, callable $callback, mixed $arg = UNKNOWN): true {} function mt_rand(int $min = UNKNOWN, int $max = UNKNOWN): int {} function rand(int $min = UNKNOWN, int $max = UNKNOWN): int {} class SoapHeader { public function __construct( string $namespace, string $name, mixed $data = UNKNOWN, bool $mustUnderstand = false, string|int|null $actor = null ) {} } function stream_filter_append($stream, string $filter_name, int $mode = 0, mixed $params = UNKNOWN) {} function stream_filter_prepend($stream, string $filter_name, int $mode = 0, mixed $params = UNKNOWN) {} class ReflectionClass implements Reflector { public function getStaticPropertyValue(string $name, mixed $default = UNKNOWN): mixed {} } In most cases, these functions have a parameter with the ''mixed'' type which gets passed to something else (i.e.: ''array_walk'' passes ''$arg'' to the ''$callback'' callable). The problem with these parameters is two-fold: * As an optimization, we generally want to avoid passing these values to their target when no meaningful value is provided. * They accept any values due to the ''mixed'' type, so there is no way to signal unmeaningful values The list of overloaded functions and methods with well-defined default parameter values which are not yet deprecated by any RFCs: /* Parameter #1 is overloaded */ function session_set_cookie_params( array|int $lifetime_or_options, ?string $path = null, ?string $domain = null, ?bool $secure = null, ?bool $httponly = null ): bool {} /* Parameter #3 is overloaded */ function setcookie( string $name, string $value = "", array|int $expires_or_options = 0, string $path = "", string $domain = "", bool $secure = false, bool $httponly = false ): bool {} /* Parameter #3 is overloaded */ function setrawcookie( string $name, string $value = ?, array|int $expires_or_options = 0, string $path = ?, string $domain = ?, bool $secure = false, bool $httponly = false ): bool {}