The mass deprecation RFC for PHP 8.6 has passed. Voting closed Monday with 31 proposals accepted and four rejected, which settles what your existing code starts warning about the first time you run it on 8.6.

Gina P. Banyard posted the results to the internals list. The Deprecations for PHP 8.6 RFC carried 35 separate ballots, each needing two thirds on its own, and voting closed at 13:00 UTC on August 10. Everything accepted raises a notice in 8.6 with removal targeted at PHP 9, so you have a full major version to act on it.

Five New Reserved Identifiers

These five are the changes most likely to hit userland code, because they break names that are legal today:

  • Using let as an identifier (24 yay, 11 nay, 9 abstentions, 68.6%)
  • Using namespace as a class constant name (29 yay, 8 nay, 7 abstentions, 78.4%)
  • Using is as an identifier (29 yay, 10 nay, 6 abstentions, 74.4%)
  • Naming a function readonly (39 yay, 1 nay, 2 abstentions, 97.5%)
  • Using _ as a constant and compile time alias (34 yay, 4 nay, 5 abstentions, 89.5%)

is passed despite the loudest objection of the voting period. Rowan Tommins pointed out that it collides with Hamcrest, the matcher library that Mockery depends on, which uses an is() function throughout its README and has more than 500 million installs tracked by Packagist.

I think we should think very carefully whether we can avoid disrupting that much code.

It cleared two thirds anyway, so any package with a function named is() has until PHP 9 to rename it.

Functions and Aliases on the Way Out

Most of the remaining accepted proposals are cleanup, and several ballots were unanimous:

  • The type-check aliases is_double(), is_integer(), is_long(), and doubleval(), all around 95%
  • strcoll() and the SORT_LOCALE_STRING sort flag, both at 95%
  • metaphone() (20 yay, 8 nay, 16 abstentions, 71.4%)
  • spl_classes() at 100%, and spl_object_hash() (26 yay, 9 nay, 11 abstentions, 74.3%)
  • The SplFileObject CSV methods (25 yay, 5 nay, 15 abstentions, 83.3%)
  • mysqli::stmt_init() and mysqli_get_charset(), both at 100%
  • define() with $case_insensitive specified, at 100%
  • is_a() and is_subclass_of() with a string when $allow_string is false, both at 100%

Returning from a finally block passed at 90.7% (39 yay, 3 nay, 4 abstentions). Unlike most of the accepted proposals, this is a behavioral change rather than a naming cleanup.

A handful of ballots also deprecate passing objects where an array is expected, covering array_walk(), deflate_init() and inflate_init(), the zlib and bzip2 stream filters, mb_convert_variables(), and http_build_query().

The metaphone() result comes with a caveat worth knowing. Calvin Buckley relayed a note from Nora during the vote: the replacement named in the RFC, the Linguistics package, calls php-src's own metaphone() internally, so it is affected by the deprecation rather than being a way around it. RFC author Weilin Du agreed the text needs to be clearer, noting that only one bridge method in that package uses it.

The Four That Failed

  • Deprecate the list() construct (23 yay, 23 nay, 1 abstention, 50.0%)
  • Reserve in, out, and inout as identifiers (8 yay, 21 nay, 15 abstentions, 27.6%)
  • Deprecate _ as a function name and the _() alias for gettext() (10 yay, 22 nay, 11 abstentions, 31.3%)
  • Deprecate the dechunk filter (18 yay, 15 nay, 12 abstentions, 54.5%)

list() finished on a flat 23 to 23 tie, which is as close as the ballot could get to the two-thirds bar without clearing it. It was tied at 21 to 21 earlier in the voting period and never pulled ahead.

The dechunk filter drew the most argument on the list. Matteo Beccati voted no and said the deprecation would start triggering notices in symfony/http-client and php-http/message with no replacement offered:

I believe we should provide such an alternative together with the deprecation, rather than expecting projects with 200M+ installations to "find an alternative, such as decoding it using code written in PHP".

Jakub Zelenka went further, noting that php-src has to keep the filter anyway because the HTTP stream wrapper uses it for chunked encoding, so deprecating it for userland removes no code and no maintenance:

What I think is that deprecation should be proposed only if there a deep knowledge of the problem and the context which I'm not sure is the case here (and in some other proposals as well).

RFC contributor Sjoerd Langkemper, who proposed that ballot, said his goal was to prevent the filter's use in filter chain attacks rather than to cut maintenance, and pointed at his pull request aimed at the same problem.

What to Do Now

Nothing breaks yet. Every accepted item raises a deprecation notice in 8.6 and is scheduled for removal in PHP 9, so the work is finding the affected code before then. The keyword reservations are worth grepping for first, since a function or class constant named is, let, readonly, or namespace is the kind of thing static analysis catches in a single pass.

For the full ballot list with vote counts and the reasoning behind each one, read the Deprecations for PHP 8.6 RFC, and Gina's results email has the complete tally.