[PHP-DEV] [RFC] Deprecations for PHP 8.5

Hello internals,

It is this time of year again where we proposed a list of deprecations to add in PHP 8.5:

As a reminder, this list has been compiled over the course of the past year by various different people.

And as usual, each deprecation will be voted in isolation.

We still have a bit of time buffer, so if anyone else has any suggestions, they are free to add them to the RFC.

Some should be non-controversial, others a bit more.
If such, they might warrant their own dedicated RFC, or be dropped from the proposal altogether.

Best regards,

Gina P. Banyard

On Wed, Jul 2, 2025, at 2:56 PM, Gina P. Banyard wrote:

Hello internals,

It is this time of year again where we proposed a list of deprecations
to add in PHP 8.5:

PHP: rfc:deprecations_php_8_5

As a reminder, this list has been compiled over the course of the past
year by various different people.

And as usual, each deprecation will be voted in isolation.

We still have a bit of time buffer, so if anyone else has any
suggestions, they are free to add them to the RFC.

Some should be non-controversial, others a bit more.
If such, they might warrant their own dedicated RFC, or be dropped from
the proposal altogether.

Best regards,

Gina P. Banyard

For the DB-specific PDO constants, am I correct that all of those constants and methods already have equivalents in their respective driver classes? (If so, please state that explicitly.)

Everything else seems reasonable to me at the moment.

--Larry Garfield

On Jul 2, 2025, at 4:56 PM, Gina P. Banyard <internals@gpb.moe> wrote:

Hello internals,

It is this time of year again where we proposed a list of deprecations to add in PHP 8.5:

PHP: rfc:deprecations_php_8_5

As a reminder, this list has been compiled over the course of the past year by various different people.

And as usual, each deprecation will be voted in isolation.

We still have a bit of time buffer, so if anyone else has any suggestions, they are free to add them to the RFC.

Some should be non-controversial, others a bit more.
If such, they might warrant their own dedicated RFC, or be dropped from the proposal altogether.

Best regards,

Gina P. Banyard

Thanks for reminding me I should dust off my proposal for cleaning up
ODBC driver support. Might be a good idea to put it to a vote...

On Wed, Jul 2, 2025, 21:58 Gina P. Banyard internals@gpb.moe wrote:

Hello internals,

It is this time of year again where we proposed a list of deprecations to add in PHP 8.5:

https://wiki.php.net/rfc/deprecations_php_8_5

As a reminder, this list has been compiled over the course of the past year by various different people.

And as usual, each deprecation will be voted in isolation.

We still have a bit of time buffer, so if anyone else has any suggestions, they are free to add them to the RFC.

Some should be non-controversial, others a bit more.
If such, they might warrant their own dedicated RFC, or be dropped from the proposal altogether.

Best regards,

Gina P. Banyard

PDO::ERRMODE_WARNING

I am ok with deprecating this mode as I cannot imagine why would anyone use it. However, can I suggest that we deprecate the silent mode too? How useful is the silent mode in PDO?

ext/filter deprecations

As much as I hate this extension, I don’t think deprecating these functions is the right choice. It’s not unheard of for someone to use filter_input without the filter parameter to avoid getting the warning on missing value. See the top comment on https://www.php.net/manual/en/function.filter-input.php#115086

Maybe we should just unbundle that whole extension?

docref_root

Can this still be used for development environment in another language? I.e. point to the manual in localized version?

Deprecate using values of type null and bool as array offsets

I can see the value in getting rid of it, but this behavior can be useful too. For example

$tally[isEnabled($name)]++;

On 2025-07-03 07:56, Gina P. Banyard wrote:

Hello internals,

Some should be non-controversial, others a bit more.
If such, they might warrant their own dedicated RFC, or be dropped from the proposal altogether.

Best regards,

Gina P. Banyard

Just skimming and saw

> Deprecate using values of type null and bool as array offsets and when calling array_key_exists()

The discussion that follows says what is wrong with null (and I agree with that), but not why bool should be deprecated as well (in fact it mentions that bool gets cast to int).

It's just that I've more than once found it self-documenting to use a bool as an array offset because I wanted to partition an array into two according to a predicate - e.g., $partition[true] contains those elements that satisfy the predicate. Especially when the question of which of those two partitions I want is itself a runtime boolean decision.

On 2 July 2025 20:56:12 BST, "Gina P. Banyard" <internals@gpb.moe> wrote:

Hello internals,

It is this time of year again where we proposed a list of deprecations to add in PHP 8.5:

PHP: rfc:deprecations_php_8_5

For FILTER_CALLBACK, I can see it being useful in the extended mode of filter_var_array, where a separate filter and options can be defined for each key in the subject array. This allows you to build up a set of filters in advance, using callbacks for advanced cases; or even to recurse into another filter_var_array call on sub-arrays.

I am not a huge fan of the filter extension, but don't see any particular reason to remove this one feature of it.

Rowan Tommins
[IMSoP]

Hi,

On Wed, Jul 2, 2025 at 10:00 PM Gina P. Banyard internals@gpb.moe wrote:

Hello internals,

It is this time of year again where we proposed a list of deprecations to add in PHP 8.5:

https://wiki.php.net/rfc/deprecations_php_8_5

Here are few notes on the ones that I don’t agree with:

Deprecate backticks as an alias for shell_exec

I think this might be too big BC break that might impact many scripts - would be good to see also if it impacts OSS projects. I guess it will impact even more non public code bases.

Deprecate the __sleep() and __wakeup() magic methods

I’m not sure about this one. I don’t think it’s worth it. It’s just an unnecessary BC break IMHO. I would also consider more ext/standard thing rather than language.

Deprecate the error_prepend_string and error_append_string INI directives

I think it doesn’t have a good enough reason to be deprecated and might be still used. I don’t see any problem with keeping them.

Deprecate the report_memleaks INI directive

I think this should be kept as one might not necessarily have time to fix it but still need to work reasonably with debug build so it makes sense to suppress it.

Deprecate the $http_response_header predefined variable

I think it’s too early for this. I would prefer to not deprecate it at this time because alternative is available only from 8.4…

Deprecate filter_input(), filter_input_array(), and filter_has_var()

I think those functions might be useful in some cases. I would also like to see their current usage. The case of modification of super global is not exactly a usual thing so I wouldn’t worry much about it.

Kind regards

Jakub

Hi everyone

On Wed, Jul 2, 2025 at 9:58 PM Gina P. Banyard <internals@gpb.moe> wrote:

It is this time of year again where we proposed a list of deprecations to add in PHP 8.5:

PHP: rfc:deprecations_php_8_5

Thanks for the bulk RFC. Some thoughts.

Deprecate __construct() and __destruct() in interfaces

While __construct() is tied to the new expression (at least unless
instantiated through
ReflectionClass::newInstanceWithoutConstructor()), factories creating
instances from class names may still need to check for constructor
compatibility. This is much easier achieved through an interface than
it is through reflection.

With an interface:

interface I {
    public function __construct(string $a, string $b);
}
function some_factory(string $class) {
    if (!is_a($class, I::class)) {
        throw new InvalidArgumentException();
    }
    return new $class('foo', 'bar');
}

Without an interface:

function some_factory(string $class) {
    $reflection = new ReflectionClass($class);
    $constructor = $reflection->getConstructor();
    if (!$constructor || !$constructor->isPublic()) {
        throw new InvalidArgumentException();
    }
    $params = $constructor->getParameters();
    if (count($params) !== 2
        || $params[0]->getType()->getName() !== 'string'
        || $params[1]->getType()->getName() !== 'string') {
        throw new InvalidArgumentException();
    }
    return $class('foo', ['bar']);
}

This implementation is much more complex, and incorrect in multiple ways.

1. We could have more than 2 params, with them being optional.
2. We could use variadics, i.e. function __construct(string ...$s),
which is completely valid.
3. Since parameter types are contravariant, they may be substituted
with union types, mixed, or just completely omitted.

Also note that while LSP for __construct() is not enforced for class
inheritance, it is for interfaces, even across multiple class
hierarchy levels. Maybe there is some issue I'm missing. But if not,
I'm not in favor of deprecating/dropping this feature.

Deprecate using values of type null and bool as array offsets and when calling array_key_exists()

The introduction section also lists float as a type to be deprecated
in array offsets:

Deprecate using values of type null, bool, and float as array offsets and when calling array_key_exists()

Floats used as array offsets that lose precision already emit a
warning. Can you confirm that floats used as array offsets that do not
lose precision will not start emitting a deprecation?

Deprecate ReflectionClass::getConstant() for missing constants

I don't have strong opinions on this change. However,
ReflectionProperty::getDefaultValue() suffers from a very similar
issue, except that it returns null rather than false
(Online PHP editor | output for G2R3l). It would make sense to avoid more
inconsistencies. For example, ReflectionParameter::getDefaultValue()
does throw if the parameter has no default value.

Deprecate ArrayObject and ArrayIterator with objects

Just to add another issue to the list: It can also change readonly
properties of internal classes that the engine does not expect to ever
change. For example, Enum::$name and Enum::$value. This can break
internal logic assumptions (e.g. hard-coded switch cases to handle
internal enums by name) and cause memory corruption. The same goes for
non-readonly properties guarded with the internal equivalent of
__set().

Deprecate passing spl_autoload_call() to spl_autoload_unregister()

Is such a check actually useful? We can prevent
spl_autoload_unregister(spl_autoload_call(...)), but we can't prevent
spl_autoload_unregister(fn($c) => spl_autoload_call($c)). It seems
very unlikely for this to happen accidentally, and excluding all
functions that don't make sense to pass is not feasible for obvious
reasons. But I don't care too much.

Ilija

On Wed, 2 Jul 2025, Gina P. Banyard wrote:

Some should be non-controversial, others a bit more. If such, they
might warrant their own dedicated RFC, or be dropped from the proposal
altogether.

The changes to filter continue to undermine what the extension was meant
to do. The filter.default INI setting was deprecated in PHP
8.1, which was already a mistake.

The intention behind the filter extension was that admins can set a
default filter for *all* data coming in through this `filter.default`
setting as a "safe" fallback. That could/should probably even be a
filter that just makes all data ":smiling_face:" for example, to indicate you're
working with unsanitised data. (I don't think there is such a filter
though).

This fallback could then be 'circumvented' by using the
filter_input/input_array() functions, so that each of them can employ
its own unique, and useful, filter on that specific element in the
GET/POST/etc arrays.

Saying that "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. " is
basically documenting the *original intention of these functions*.

If there is anything odd with your example, is that you can modify the
values in GET/POST/etc superglobals to begin with.

"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()."

No. The whole point is that these functions read the raw data, the one
that wasn't filtered by the default filter (which has been inadvisably
deprecated).

I would therefore *undeprecate* filter.default, and allow these filter
functions are they currently are, because they implement the original
design idea behind this extension.

cheers,
Derick

On Wed, Jul 2, 2025 at 10:00 PM Gina P. Banyard internals@gpb.moe wrote:

Hello internals,

It is this time of year again where we proposed a list of deprecations to add in PHP 8.5:

https://wiki.php.net/rfc/deprecations_php_8_5

Looking at it again I think this huge deprecation RFC is not the right approach. I’m not sure what are we trying to do here but I was just checking how many deprecation had few previous versions and

PHP-8.3 - 5 deprecations - https://wiki.php.net/rfc/deprecations_php_8_3
PHP-8.4 - 22 deprecations - https://wiki.php.net/rfc/deprecations_php_8_4

And this one is 46. I know that there were few independent RFC’s but that’s a huge increase anyway and I’m not really sure it’s a good idea to do that many in a single release.

It is also not convenient for discussion in a single thread which we can be seen already. I’m not saying there should be an RFC for each deprecation but maybe having at least RFC for each section would be better as it would allow properly consider each of those depredations. Or somehow split to some logical chunks…

Kind regards,

Jakub

Hi

First of all, that's a huge list of deprecations and I think we should tone down on that.
Especially if a feature still has a purpose and is not harmful or buggy, then we should probably consider not deprecating it.

Secondly, I'm tired of having to deal with useless deprecation messages.
A lot of deprecation messages are completely useless for developers because they do not point to a reason or a replacement.
That leaves you needing to look up the documentation, which is also incomplete.
See Deprecation messages are often not helpful · Issue #14320 · php/php-src · GitHub
Therefore, any deprecation proposed in this RFC that does not explicitly list the deprecation message, I will vote no for.

There are some things in the list I don't care about or I don't have a lot of insight of its uses in, and I will abstain for voting on them.

There are a few things I will vote no for:

* Deprecate semicolon after case in switch statement.
  People seem to use this and it doesn't seem harmful to have. Just because you don't like it doesn't mean we should yeet it.

* Deprecate attributes applying to multiple class properties/constants.
  On the edge, confusing yes, but it might break real code.

* Deprecate using values of type null and bool as array offsets and when calling array_key_exists()
  Deprecating this would make the language more inconsistent by allowing this on array offsets but not on the function.

* Deprecate __debugInfo() returning null
  Weird, especially as the docs say the return type is ": array", but not harmful.

* Deprecate ReflectionParameter::allowsNull()
  This shorthand doesn't hurt anybody, and is convenient, I don't see the point in deprecating it.

* Deprecate passing spl_autoload_call() to spl_autoload_unregister()
  This is very ad hoc, and as Ilija pointed out, we can't prevent workarounds for this. So what's the point really.
  The behaviour may be weird, but notice that people won't do this by accident.

* Deprecate passing null to readdir(), rewinddir(), and closedir()
  Dubious but not really harmful I think.

* Based on Derick's comments I will vote no on the ext/filter deprecations. I was already going to vote no on the filter_* functions though.

* Deprecate driver specific PDO constants and methods
  Too early.

Kind regards
Niels

On Wed, Jul 2, 2025 at 11:00 PM Gina P. Banyard internals@gpb.moe wrote:

Hello internals,

It is this time of year again where we proposed a list of deprecations to add in PHP 8.5:

https://wiki.php.net/rfc/deprecations_php_8_5

On the topic of deprecating __construct() in interfaces:

I’m thinking about constructors usually like this:
They are functions that are invoked statically (using the new keyword) on the class before the object is created, and they execute on instance level, after the object is created.

They are called as static methods, and they are very similar to static factory methods, and for better or worse, static methods are supported in interfaces.

So, in this sense, I think the pattern of defining a constructor in an interface is useful.
This way, a factory that receives a class name implementing a specific interface that defines a constructor will be able to accurately instantiate that class in a simple way.

One could also argue that the deprecation could be solved easily by replacing the constructor in the interface with a static factory method.
I think we should talk more on this, and I see that Ilija also touched on it in his reply while I was drafting mine, with even more details.

Generally, I think that’s what would be good for each deprecation, to have a clear way of solving it noted in the RFC.


Alex

Le 2 juil. 2025 à 21:56, Gina P. Banyard internals@gpb.moe a écrit :

It is this time of year again where we proposed a list of deprecations to add in PHP 8.5:

PHP: rfc:deprecations_php_8_5

Hi,

To reduce noise, I’ll be short.

General remark: For each deprecation, please research first if it’ll break code without easy replacement. Then, please provide an explicit deprecation message that will point to an alternative.

Some cases:

—Claude

Hi

On 7/3/25 18:04, Derick Rethans wrote:

The intention behind the filter extension was that admins can set a
default filter for *all* data coming in through this `filter.default`
setting as a "safe" fallback. That could/should probably even be a

Genuine question: Is that *intention* documented anywhere? PHP: filter_input - Manual only makes factual statements about the behavior, but not how one is supposed to use them. Similarly PHP: Runtime Configuration - Manual also says that a default filter can be configured, but not why one would want to do so.

Best regards
Tim Düsterhus

On 3 July 2025 17:04:59 BST, Derick Rethans <derick@php.net> wrote:

The intention behind the filter extension was that admins can set a
default filter for *all* data coming in through this `filter.default`
setting as a "safe" fallback. That could/should probably even be a
filter that just makes all data ":smiling_face:" for example, to indicate you're
working with unsanitised data. (I don't think there is such a filter
though).

Perhaps the option should be something like "filter.mandatory=1", blanking or poisoning the superglobals and obliging users to go through filter_var?

Rowan Tommins
[IMSoP]

On Wed, 2 Jul 2025 at 22:40, Gina P. Banyard internals@gpb.moe wrote:

Hello internals,

It is this time of year again where we proposed a list of deprecations to add in PHP 8.5:

https://wiki.php.net/rfc/deprecations_php_8_5

As a reminder, this list has been compiled over the course of the past year by various different people.

And as usual, each deprecation will be voted in isolation.

We still have a bit of time buffer, so if anyone else has any suggestions, they are free to add them to the RFC.

Some should be non-controversial, others a bit more.
If such, they might warrant their own dedicated RFC, or be dropped from the proposal altogether.

Best regards,

Gina P. Banyard

I’d also suggest deprecating building ext/readline with the Readline library and
ext/dba with the GDBM library.

These two libraries are released under the GPL-3 license, which is not
compatible with PHP. In practice this means that PHP linked with GPL-3-licensed
software should not be distributed, making it impractical for any server
environment beyond a local testing project. This issue isn’t immediately
obvious, but many packagers need to be aware of it and avoid building with these
libraries.

ext/readline works fine with the Editline library as an alternative,
ext/dba works fine with other handlers.

This suggestion would otherwise delay resolving the issue, as it would require
an RFC vote - something I hoped to avoid in pull requests. But I suppose that’s
how it has to be handled.

See also:

On Fri, Jul 4, 2025 at 3:17 PM Ilija Tovilo <tovilo.ilija@gmail.com> wrote:

Hi everyone

On Wed, Jul 2, 2025 at 9:58 PM Gina P. Banyard internals@gpb.moe wrote:

It is this time of year again where we proposed a list of deprecations to add in PHP 8.5:

https://wiki.php.net/rfc/deprecations_php_8_5

Deprecate ReflectionClass::getConstant() for missing constants

I don’t have strong opinions on this change. However,
ReflectionProperty::getDefaultValue() suffers from a very similar
issue, except that it returns null rather than false
(https://3v4l.org/G2R3l). It would make sense to avoid more
inconsistencies. For example, ReflectionParameter::getDefaultValue()
does throw if the parameter has no default value.

Good catch, I wasn’t aware of this behavior for ReflectionProperty::getDefaultValue(). I agree that we should avoid such inconsistencies, and thus have proposed deprecating ReflectionProperty::getDefaultValue() for properties that have no default value. See https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_reflectionpropertygetdefaultvalue_for_properties_without_default_values

-Daniel

On Jul 4, 2025, at 6:52 PM, Peter Kokot <petk@php.net> wrote:

I'd also suggest deprecating building ext/readline with the Readline library and
ext/dba with the GDBM library.

These two libraries are released under the GPL-3 license, which is not
compatible with PHP. In practice this means that PHP linked with GPL-3-licensed
software should not be distributed, making it impractical for any server
environment beyond a local testing project. This issue isn't immediately
obvious, but many packagers need to be aware of it and avoid building with these
libraries.

ext/readline works fine with the Editline library as an alternative,
ext/dba works fine with other handlers.

This suggestion would otherwise delay resolving the issue, as it would require
an RFC vote - something I hoped to avoid in pull requests. But I suppose that's
how it has to be handled.

To add to this, GDBM also doesn't seem to work with our ext/dba tests;
QDBM did, however.

I ran into this earlier when doing the PPC CI work:

On 2025-07-04 19:38, Alexandru Pătrănescu wrote:

I'm thinking about constructors usually like this:
They are functions that are invoked statically (using the `new` keyword) on the class before the object is created, and they execute on instance level, after the object is created.

They are called as static methods, and they are very similar to static factory methods, and for better or worse, static methods are supported in interfaces.

They're really more instance methods (they need an instance to exist for them to act on, hence $this). The "new" operator creates the instance and then the __construct method is called on that new instance.

One consequence of this is that the __construct method can be called _after_ instantiation (because in the normal course of things it _is_):

class Foo
{
  public function __construct(public int $v=0)
  {
    echo "Constructed {$this->v}!";
  }
}

$t = Foo::__construct(5);

$u = new Foo(17);
$u->__construct(42);

Hi

On 7/4/25 09:01, Niels Dossche wrote:

First of all, that's a huge list of deprecations and I think we should tone down on that.
Especially if a feature still has a purpose and is not harmful or buggy, then we should probably consider not deprecating it.

It's a long list, but many of the deprecations are just obvious follow-ups of a previous RFC or closely related.

As an example, I would consider all the `_close()` / `_free()` / `destroy()` function deprecations to be a single one in practice and then one that just fixes an inconsistency, since others are already deprecated. Similarly Reflection*::setAccessible() is a follow-up for a previous RFC that deferred the deprecation to reduce the impact on existing code.

Defining "harmful" is also complicated. Is making the language harder to learn for newcomers "harmful" (e.g. the case-with-semicolon one, which is just a tiny bit of syntax difference for the same thing)? Is allowing for confusing code (e.g. Reflection::setAccessible(false)) "harmful"?

In both cases, I'd say: Yes, that is harmful.

Secondly, I'm tired of having to deal with useless deprecation messages.
A lot of deprecation messages are completely useless for developers because they do not point to a reason or a replacement.
That leaves you needing to look up the documentation, which is also incomplete.
See Deprecation messages are often not helpful · Issue #14320 · php/php-src · GitHub
Therefore, any deprecation proposed in this RFC that does not explicitly list the deprecation message, I will vote no for.

I agree that having useful deprecation messages is important and I've just added a `trigger_error()` example to the PDO 'uri:' deprecation that I proposed. My other proposals already had the `#[\Deprecated]` attribute including the message.

There are a few things I will vote no for:

* Deprecate semicolon after case in switch statement.
   People seem to use this and it doesn't seem harmful to have. Just because you don't like it doesn't mean we should yeet it.

See above. Also: The RFC seems to indicate that this is not actually used in practice.

* Deprecate attributes applying to multiple class properties/constants.
   On the edge, confusing yes, but it might break real code.

Since this is a recurring thing, I feel compelled to point out terminology: A deprecation in itself is not a breaking change.

* Deprecate __debugInfo() returning null
   Weird, especially as the docs say the return type is ": array", but not harmful.

I consider undocumented behavior to be harmful and in this case deprecating (and removing) the undocumented behavior seems to be better than adding another special case to the language that users need to learn about.

* Deprecate passing spl_autoload_call() to spl_autoload_unregister()
   This is very ad hoc, and as Ilija pointed out, we can't prevent workarounds for this. So what's the point really.
   The behaviour may be weird, but notice that people won't do this by accident.

See above regarding "undocumented behavior". Also to my understanding this is not about "preventing workarounds", but rather removing this branch, which is just surprising behavior:

Best regards
Tim Düsterhus