Voting open until 2026-10-22 00:00 UTC; not accepted. The official RFC index lists the RFC
(version 0.4.1, dated 2026-08-09, author Weilin Du) as voting, and its target is PHP 8.7. The author
announced the start of voting at 16:14 UTC on
7 October, after a page edit at 16:10 UTC. The vote ends on 2026-10-22 at 00:00 UTC. That is
earlier than the start the author had given: an
intent-to-vote message on 5 October and a voting
section added at 10:02 UTC on 7 October both said 2026-10-08 00:00 UTC. The edited voting section
now gives only the end time. There is one primary question, "Add IntlRelativeDateTimeFormatter as
described to ext/intl?", which needs a 2/3 majority. When the page was checked at about 10:02 UTC on
14 October the poll showed 18 yes, 0 no and 0 abstain (7–0–0 on 7 October). That is an interim
count, not a result.
Page header updated on 14 October: an edit at 07:02 UTC ("update status") changed the header
from Status: Under Discussion to Status: Voting, so the page now agrees with the index. The
header still says Voting thread: TBD. The
discussion thread began in August. Acceptance and any
release are separate events from the vote.
What is proposed
A new final class IntlRelativeDateTimeFormatter in ext/intl, wrapping ICU's
URelativeDateTimeFormatter C API (stable since ICU 57, and ext/intl already requires ICU 57.1
or newer). It would produce strings such as "yesterday", "in 3 days" or "5 minutes ago" for a
locale. The class and three pure (unbacked) enums would live in the global namespace:
IntlRelativeDateTimeFormatterUnit (Year through Second, plus Sunday through Saturday).
The constructor takes ?string $locale (where null means the default intl locale), a style, a
capitalization context and an optional NumberFormatter. The formatter keeps the supplied
NumberFormatter, so later configuration changes to it affect later output. Methods:
format(int|float $offset, $unit) uses a text form when the locale has one ("yesterday",
"last week") and otherwise falls back to a numeric form;
formatNumeric() always uses the numeric form ("1 day ago", "in 1.5 hours");
combineDateAndTime(string $relativeDate, string $time) joins two already formatted strings
with the locale's date-time separator. It does not parse or validate them;
getErrorCode() and getErrorMessage() follow the usual ext/intl error conventions. ICU
failures return false, or throw if intl.use_exceptions is on. A wrong enum argument
always throws TypeError.
The RFC does not change IntlDateFormatter::RELATIVE_*, which formats a concrete date. No
procedural aliases or integer constants are added. The backward-compatibility section notes that
the four new global names could conflict with userland declarations while intl is loaded. The
changelog records changes made in response to feedback from Tim Düsterhus and others, such as
adopting the three pure enums on 2026-09-19.
Limitations
The RFC page is inconsistent about the implementation. Its header links
php-src PR #23514, but "Patches and Tests" says
"TBD". The PR was not inspected in this cycle. The example output strings depend on ICU locale
data and version. Nothing here is available in released PHP. Related:
intl_date_format() proposal, a separate ext/intl draft.
Under discussion; not accepted. The RFC page (version 1.0, dated 2026-10-13, status
Under Discussion) and the official RFC index both list it as under discussion. The proposed
version is PHP 8.7. The page was created on 13 October, after RFC karma was requested and granted
that morning. The RFC change feed lists an edit at 19:24 UTC, and the header now links the discussion thread.
The author opened the discussion at 18:36 UTC. The voting
thread reads "tbd", so there is no vote date.
Proposal
Two new global constants in ext/json, accepted in $flags by json_decode() and json_validate():
JSON_ALLOW_COMMENTS (1 << 23) accepts // line comments and /* … */ block comments.
JSON_ALLOW_TRAILING_COMMAS (1 << 24) accepts one comma after the last element or member of a
nonempty array or object.
The flags are independent. Without them, results, error codes and error locations do not change.
json_encode() does not change and ignores both flags. Before this RFC, json_validate() accepted only
JSON_INVALID_UTF8_IGNORE. Other flags still throw ValueError, and its message would name the
three accepted flags.
Comment rules in the RFC:
A comment can appear where JSON allows whitespace, but not inside a token (tr/**/ue and -/**/1
are syntax errors). Comment markers inside strings are string text.
Block comments do not nest. /*/1 is an unterminated comment, and /**/1 is the value 1.
A line comment ends at LF, CR or CR LF, but not at U+2028/U+2029. # comments are not accepted.
Input that contains only comments is a syntax error, the same as empty input.
A NUL byte in a comment raises JSON_ERROR_CTRL_CHAR, even with the invalid-UTF-8 flags. Invalid
UTF-8 raises JSON_ERROR_UTF8 unless JSON_INVALID_UTF8_IGNORE/_SUBSTITUTE is given.
An unterminated block comment raises JSON_ERROR_SYNTAX at the position of its /*. Error
line numbers count lines inside comments.
Trailing-comma rules: [1, 2, 3,] and {"a": 1,} are accepted, but [,], {,}, [1,,], [1,,2]
and a top-level 1, stay syntax errors. [1,} gives the same state-mismatch error as [1}.
No new error codes are added, nesting depth is unchanged, and a UTF-8 BOM is still a syntax error.
Relation to JSON5, JSONC and JWCC
The RFC does not add JSON5 (single quotes, unquoted keys, hexadecimal numbers, NaN/Infinity).
It says JSON_ALLOW_COMMENTS matches the JSONC draft without trailing commas, and that both flags
together match JWCC, except that in this RFC a CR alone also ends a line comment.
A single JSON_ALLOW_JSONC flag, suggested by Tim Düsterhus in the pre-RFC thread, is listed under
rejected features. The reasons given are that the jsonc.org specification is a draft, it makes
trailing commas optional, and the name JSONC has more than one meaning. Later RFCs could add a
combined constant if a stable standard appears.
Ballot
Two primary votes, one per flag, each needing a 2/3 majority. The results are independent.
Evidence the RFC cites
These are the author's measurements, not independently reproduced here:
A differential test of 120,025 generated inputs (840,175 checks) found the same results, error codes
and messages on master and on the branch without the new flags.
Callgrind instruction counts for a 7.6 MB document show +1.60% for json_decode() and +1.69% for
json_validate() on strict JSON without the new flags. The RFC says two scanner changes, not yet in
the pull request, reduce this to +0.56% and +0.59%.
Demand: Packagist install counts for colinodell/json5 and adhocore/json-comment as of 2026-10-12.
Compatibility and limitations
A user-defined global constant with either name now triggers an "already defined" warning, and the
built-in value wins. Namespaced constants are not affected.
json_decode() currently ignores unknown flag bits, so code that already passes bit 23 or 24 by
mistake could start accepting comments or trailing commas.
The implementation pull request is labelled "ABI break"
because it adds macros to php_json.h. The RFC says no struct or function signature changes.
The RFC says no other JSON_* flag uses bits 23 and 24. The pull request description notes that the
proposed duplicate-key flags in php-src #22473 use the same bits, so whichever is merged second must
move. These two statements don't contradict each other, because #22473 isn't merged.
Discussion
On 13 October Tim Düsterhus replied that he was "not happy, but
the rejected [section] is well-argued" and had no other comments. Larry Garfield
agreed: "I have no remaining objections to it." Both had raised points
in the pre-RFC thread. These are list replies, not votes.
Under discussion; not accepted. The RFC page (version 1.0, dated 2026-10-08) was created as
a Draft at 17:06 UTC on 8 October, the same day the author asked for RFC
karma and got it.
After an edit at 06:16 UTC on 9 October the status line reads Under Discussion. At 06:19 UTC the
author opened the discussion on internals, calling it
"an invitation for discussion, not a call for votes". The official RFC index
collected at 10:01 UTC on 9 October lists it as discussion. At 19:43 UTC on 9 October the author
published version 1.1 (dated 2026-10-09). Its changelog says it was updated after a review of the
implementation (see below). An edit at 11:30 UTC on 11 October made it version 1.2 (dated 2026-10-11),
still Under Discussion. The page's discussion and voting thread fields still read "tbd". The author is
Damian Jóźwiak. The proposed version is the "next minor PHP version (the release after PHP 8.6)",
recorded here as 8.7.
The proposal is based on Gina Peter Banyard's girgias/csv extension (BSD-3-Clause). The RFC
says Gina has not reviewed it and that it is not her endorsement. The announcement adds that parts
of the implementation were developed with LLM assistance and reviewed by the author.
Motivation given by the RFC
The RFC argues that the existing CSV functions (fgetcsv(), fputcsv(), str_getcsv() and the
SplFileObject CSV methods) cannot follow RFC 4180 without breaking compatibility. It names
the proprietary $escape mechanism, locale-dependent parsing, single-byte delimiters and
enclosures, empty lines parsing as [null], and the lack of an inverse of str_getcsv(). It
also says PHP 8.6 deprecated SplFileObject::fgetcsv(), fputcsv(), setCsvControl() and
getCsvControl() with no replacement. The
PHP 8.6 deprecations RFC does list "Deprecate
SplFileObject CSV methods", but the result of that vote was not checked here.
What is proposed
A new, always-enabled ext/csv with six functions and one class in the Csv\ namespace:
Csv\array_to_row() and Csv\row_to_array() format and parse one row.
Csv\collection_to_buffer(), Csv\buffer_to_collection() and
Csv\buffer_to_collection_lax() convert between a string and rows. The strict variants throw
ValueError when rows have different numbers of fields. The _lax variant allows it.
Csv\collection_to_file() writes any iterable of arrays through the streams layer one row at
a time.
final class Csv\LazyLaxCollection (IteratorAggregate) reads rows lazily, with
createFromBuffer() and createFromFile() factories. The RFC says memory use depends on the
longest row, not the file size, and that iteration can restart if the stream is seekable.
Every function takes $delimiter = ',', $enclosure = '"' and $eolSequence = "\r\n". All three
may be multibyte. Parsing is described as binary-safe and locale-independent, with no escape
character. Invalid dialects and malformed input throw ValueError, non-array rows throw
TypeError, I/O failures throw Error, and no function returns false.
Version 1.1 adds a "Strict parsing" section. Whatever the dialect, input that does not follow the
RFC 4180 record grammar throws ValueError instead of being guessed. This covers an enclosure inside
an unenclosed field (including whitespace or a BOM before the opening enclosure), data between a
closing enclosure and the next delimiter or line ending ("a" ,b), an enclosure that is never closed,
and CR or LF outside an enclosed field that is not part of the EOL sequence. As a result, an LF-only
file read with the default "\r\n" is an error, not one long row; it is read by passing "\n".
row_to_array() now accepts exactly one row, with an optional trailing EOL sequence.
Version 1.1 also describes stream handling in more detail. $file accepts anything the streams layer
accepts (paths, php://, compress.zlib://, user wrappers) and is opened with the default stream
context. A collection from createFromFile() owns its stream like SplFileObject does: fclose()
refuses to close it, and it is closed when the object is destroyed. Iteration state belongs to each
iterator, so nested loops over a buffer-backed collection are independent. A second concurrent loop over
a file-backed collection throws Error.
The RFC lists differences from the legacy functions: no escape character, no [null] for empty
lines, an enclosure inside an unenclosed field, data after a closing enclosure and an unterminated
enclosure are errors, whitespace before an enclosure is not silently dropped, and the default line
ending is "\r\n". (This page's earlier summary of version 1.0 said whitespace before an enclosure is kept;
under 1.1's strict parsing it is an error. The 1.0 wording could not be re-read, because the wiki's
revision history requires a login.) Existing functions keep their behavior. The only
backward-compatibility impact named is that the top-level Csv\ namespace becomes reserved.
Version 1.1 listed as an open issue that dialects in which one token is a prefix of another (for example
delimiter :: with EOL :) are ambiguous and could be rejected during argument validation. Version 1.2
replaces that with a "Multibyte and overlapping dialect tokens" section in the proposal. Overlapping tokens
are allowed where rows still round-trip: writers also enclose a field when a token could form across the
boundary with a neighbouring delimiter or EOL sequence (the RFC's example: delimiter --, enclosure aa).
Combinations that cannot represent every row unambiguously throw ValueError in readers and writers, for
example delimiter xy with enclosure xyx. The remaining open issues are whether the extension can be
disabled at build time (the implementation supports --disable-csv; the RFC proposes always enabled), the
name LazyLaxCollection, and whether a strict lazy variant should ship in the first version.
Version 1.2 (whether these parts are new since 1.1 was not checked) names two gaps left by the PHP 8.6 SplFileObject deprecation, raised during that
discussion: SplFileObject::fputcsv() has no drop-in replacement because fputcsv() needs a stream that
SplFileObject does not expose, and SplFileObject::READ_CSV was not deprecated although
setCsvControl(), the only way to configure its dialect, was. Its "Unaffected PHP Functionality" section
says the RFC neither changes nor deprecates fgetcsv(), fputcsv(), str_getcsv() or the SplFileObject
CSV methods. The future scope includes a deprecation timeline for str_getcsv(), fputcsv() and fgetcsv()
("deliberately not part of this RFC"), a separate
RFC to deprecate SplFileObject::READ_CSV, and header/encoding handling. As checked on 9 and 11 October, it also lists a possible
object-oriented Csv\Reader/Csv\Writer API with capability interfaces for a later RFC. None of
these is part of this RFC. The planned vote is one question needing a 2/3 majority.
Discussion so far
The first reply, from Lynn on 9 October (message 132845),
asked for support for already-open streams/resources, for reading and writing, and what "Lax"
means. The author replied at 08:59 UTC:
"Lax" means rows may have different numbers of fields, as in buffer_to_collection_lax(). The name
comes from girgias/csv. The author added a test for it and suggested a LazyCollection with
configurable strictness as a possible alternative name.
The author is "considering" two new entry points, Csv\collection_to_stream($stream, iterable $collection, ...) and Csv\LazyLaxCollection::createFromStream($stream, ...), separate from the
$file parameter. The stream parameter would be untyped (documented as resource) and checked at
runtime with a TypeError. The proposed semantics: parsing starts at the current position, the
stream is borrowed and never closed, repeated iteration seeks back (non-seekable streams iterate
once), and because of read-ahead the stream position after a partial iteration is not defined.
The author said the RFC will be updated after the API discussion. Neither version 1.1 nor version 1.2
(checked at 16:05 UTC on 11 October) contains collection_to_stream() or createFromStream(), so
they are not part of the proposal as written.
On 10 October at 10:37 UTC Artem Ukrainskiy (GitHub user ArtUkrainskiy, whose review the RFC cites) argued for a different
path: improving the existing ext/standard CSV
functions incrementally instead of adding a second parser. They went through the RFC's motivation list:
$escape is, in their words, already addressed by the PHP 8.4 deprecation, with the default
changing to "" in PHP 9.0.
For locale dependence, their php-src PR #24207
("Skip mbrlen() for ASCII bytes in php_mblen()", opened 8 October, open and not merged on 10 October)
removes the per-byte libc lookup for ASCII characters in ASCII-compatible locales. They said the
remaining isspace() check could become an explicit space/tab check. They also argued that mbrlen()
is useful for CJK locales: a Shift_JIS character can contain a | trail byte that is not a delimiter,
and a locale-independent byte parser would split it.
Multibyte delimiters and enclosures currently throw ValueError, so they could be added without
changing valid calls. A new str_putcsv() could be the inverse of str_getcsv().
[null] for empty lines and whitespace before an enclosure could be handled by an opt-in strict
mode. They said enclosing fields that contain spaces on output is allowed by RFC 4180 section 2.5.
Their main concern is the long-term cost of maintaining two CSV parsers, given the reluctance in the
June and August discussions to remove existing APIs without a migration path. They said this is not an
objection to the implementation: the issues they reported were fixed quickly and the parser is now faster
than fgetcsv() in their benchmarks. They published their benchmarks and test scripts in a public GitHub
repository; those benchmarks were not checked here.
The author replied at 11:50 UTC on 11 October, after the
version 1.2 edit:
They agreed that the RFC's statement that the existing behaviour "cannot be fixed without breaking
compatibility" is too broad, since several improvements could be added as new functions or opt-in
behaviour. They also agreed that enclosing fields with spaces is allowed by RFC 4180 and that
locale-independent byte parsing has different implications for encodings such as Shift_JIS. The
version 1.2 page still contains the "cannot be fixed" sentence.
They cited the new DOM API alongside DOMDocument and the URI extension alongside parse_url() as
precedents for a modern API next to an established one, while saying these do not by themselves
justify a second CSV parser.
They proposed three directions: (A) improve the existing ext/standard API, (B) continue with ext/csv,
adding entry points for already-open streams, or (C) rework it as an object-oriented API in Csv\
with file and stream factories. They still prefer a dedicated API but are open to the other routes.
They asked whether a directional vote between these approaches would be useful if discussion does not
settle it. Such a vote would pick a direction for a revised proposal, not approve an API. No such vote
has been announced.
They said the current RFC does not propose deprecating the existing CSV functions, and that any later
consolidation would need its own discussion and a migration path.
Implementation
The implementation is php-src PR #24199, "[RFC] Add
CSV extension (ext/csv)", opened on 8 October 2026 at 17:03 UTC. On 11 October (GitHub API, about
16:05 UTC) it was still open, not a draft and not merged. The RFC calls it a port of girgias/csv 0.6.0.
Version 1.1 says a review of the PR by GitHub user ArtUkrainskiy found a use-after-free (also present
in girgias/csv 0.6.0), a quadratic rescan of long rows in createFromFile() and smaller issues. The RFC says
all of them are fixed, covered by tests and checked with AddressSanitizer and a differential fuzzer. It also
claims six girgias/csv 0.6.0 bugs fixed during the port and 90 phpt tests. In the 11 October message the
author said a remaining writer issue with overlapping tokens was fixed in a later PR commit and that all 92
phpt tests pass locally (the RFC page still says 90). On the reviewer's benchmark it reports
createFromFile() reading rows slightly faster than fgetcsv() (363–1,539 ns against 402–1,742 ns per row,
with the php_mblen() fast path of php-src PR #24207) and writing 10–20% slower than fputcsv(). These
are the author's statements; the review reports, fixes and benchmarks were not checked here.
Limitations
This page summarises a proposal whose list discussion began on 9 October. An alternative of extending the
existing functions has been proposed on the list (10 October), and the author has since asked whether the
direction (A, B or C above) should be settled first (11 October). The full raw page was read on 12 October:
its changelog entry for 1.2 (dated 2026-10-11) matches the overlapping-token policy above, says the
ambiguous-dialect open issue is resolved and links php-src commit b7866156 for the implementation. Its
"Rejected Features" section lists an object-oriented Reader/Writer API in this RFC (left to future work) and
compatibility with the legacy fgetcsv() dialect (escape character, [null] rows, locale dependence). The names, behavior and
target version can change, and nothing here is in released PHP. The API description comes from the
RFC text and was not tested against the PR. girgias/csv can already be installed as a
third-party extension (see PIE 1.5).
Under discussion. The RFC page (version 1.0, dated 2026-09-29, author Jakub Zelenka) reads Under
Discussion, as does the official RFC index collected on 11 October. It follows
the Polling API RFC, which added the Io\Poll namespace to PHP 8.6.
The proposal extends the polling surface with weak handles, socket handles, generic timer/signal/process/notification handles, and priority events.
The weak-handle design addresses event loops unintentionally keeping resources alive.
The broader handle set provides integration points for the related IO hooks work. These
are proposed changes: a description in an RFC is not evidence of release availability.
Discussion
On 11 October Tim Düsterhus posted a 15-point review,
mostly framed as questions. Among them:
They agreed with an earlier suggestion by Ilia to split the RFC into several separately voted parts,
as was done for the PHP 8.6 URI follow-up.
They asked whether separate weak handle classes are needed, or whether something like
new StreamPollHandle(WeakReference::create($stream)) would avoid duplicating every handle.
They questioned the name and namespace of TimerHandle, suggesting a sub-namespace (as with
Random\Engine) or a standalone Io\Timer, and asked whether its timeout and periodic flag should be
readonly properties.
For ProcessHandle, they suggested a proc_get_poll_handle()-style function instead of tying the class
to proc_open() resources, in case a new process API is added later.
They asked for the Context::onWatcherRemoved() callback signature, an ecosystem impact note (for
example ReactPHP), and clarification of whether SignalHandle is limited to the CLI only in thread-safe
builds.
No author reply had been collected by 04:01 UTC on 12 October.
Proposed ballot
As read in full on 12 October, the page proposes a single question, "Should the Polling API additions be
added to PHP?", with Yes/No/Abstain options and a 2/3 majority; the vote dates are placeholders
(2026-XX-XX), so no vote has been scheduled. The split into separately voted parts suggested on the list
is not reflected on the page. The proposed version is "Next minor version, PHP 8.7". The page says a
working implementation is part of the IO hooks pull request
php-src #23997 and will be extracted into its own pull request.
The changelog lists 1.0 (signal handles block signals only while watched; process handles no longer collect
the child) and 0.1 (initial draft).
Recheck the primary page for current scope,
status and ballots. No vote result is inferred here.
Under discussion; not accepted. The RFC page (version 0.2, dated 2026-10-08, author Matthieu
Napoli) and the official RFC index both list it as under discussion. The proposed version is
PHP 8.7. The voting thread reads "tbd". The page's vote widget shows 0/0/0 and "This poll has been
closed". That is the wiki template, and no vote has been opened.
Proposal (version 0.2, 8 October)
Version 0.2 reverses the direction of version 0.1. Instead of linking FPM into php, it links the
CLI SAPI into the php-fpm executable:
there is no configure option. Whenever both the CLI and FPM are built, php-fpm also contains
the CLI. With --disable-cli, or with FPM disabled (the default), nothing changes.
make install still installs two separate executables;
the php executable is unchanged, and there is no --fpm option;
php-fpm runs as the CLI when invoked under the name php, optionally followed by a digit
(for example php8.7 or Alpine's php83) through a symlink, hard link or copy. Under any other
name it is FPM, unchanged. Names such as php-cgi, php-config, phpdbg and phpize are
excluded, and so is php-8.7 (from --program-suffix=-8.7), which therefore runs FPM;
in CLI mode PHP_SAPI is cli, it reads php-cli.ini and accepts the usual CLI options. A
running process cannot switch SAPIs. The RFC says the CLI test suite passes through a php
symlink to php-fpm.
PHP_BINARY is the executable's real path (symlinks are resolved). A distribution that wants a
single executable should therefore install it as /usr/bin/php, with php-fpm as a symlink to it.
Then PHP_BINARY runs the CLI in both modes, while FPM is still started as php-fpm, so service
files, process titles and graceful reload (SIGUSR2, which re-executes through the symlink) keep
working. Tools that read /proc/<pid>/exe see php for FPM processes. The opposite layout
(php as a symlink to php-fpm) makes PHP_BINARY start FPM; the RFC says this will be
documented in UPGRADING.
Listed backward-incompatible changes, only for builds with both SAPIs: php-fpm invoked as
php/php8.7 now runs the CLI, php-fpm grows by about 150 KB in a minimal build, and FPM's C
function zif_apache_request_headers is renamed zif_fpm_request_headers (the PHP functions
apache_request_headers() and getallheaders() are unchanged). There is now a single primary
vote needing a 2/3 majority; the secondary default-on vote of version 0.1 is gone. The RFC lists no
open issues. It cites the PHP 8.6 change that made the CLI available to the embed SAPI
(PR #21385), which FrankenPHP uses, as the model.
The RFC also records two rejected alternatives: a php-fpm --cli option (it would touch FPM's
argument parsing, and some letters such as -F mean different things in the two SAPIs), and a
shared libphp.so (a much larger build and packaging change that adds dynamic linking at start).
Version 0.1 (6 October)
Version 0.1 proposed an --enable-cli-fpm configure option that linked FPM into php, selected
with a leading --fpm argument or a php-fpm* name. It had a secondary simple-majority vote on
whether to enable the option by default. Its implementation,
PR #23558. The RFC now lists that design under
rejected features.
Motivation and evidence
The author measured Bref (PHP on AWS Lambda) builds on PHP 8.5.11, Linux x86-64, by replacing
php-fpm with a symlink to a combined executable. The zipped layer went from 24.3 MB to 19.9 MB
and the unzipped layer from 89.9 MB to 64.5 MB. The median FPM cold start over 50 starts dropped
from 280 ms to 211 ms (difference −70 ms, 95% CI −79 to −59); a control runtime that never starts
FPM showed no difference. The RFC says ARM and other memory sizes were not measured, and that the
gain on hosts with the image on local disk is likely smaller. These are the author's own
benchmarks. The page does not say whether they were repeated with the version 0.2 build.
The new implementation is php-src PR #24192. On
2026-10-08 the GitHub API showed it as open, not a draft and not merged (created 11:39 UTC).
PR #23558 was still open as well.
Discussion
The RFC was announced on 6 October. Replies that
day were broadly positive and raised these points:
Tim Düsterhus (132807) noted that code in FPM
using PHP_BINARY would get a binary that needs --fpm to start FPM, and asked whether the
standalone php-fpm should be kept at all. The author
(132808) preferred removing it in a separate
RFC; Düsterhus (132812) argued for one step.
Ayesh (132811) pointed out
Debian/Ubuntu's separate SAPI packages and that -F means "execute a file" in the CLI but is an
alias of --nodaemonize in FPM, and questioned switching meaning by name or flag.
Another participant raised packaging and service-manager concerns for Linux distributions.
Larry Garfield (132816) asked whether linking
FPM into php privileges FPM over other SAPIs (FrankenPHP, Swoole, CLI-based async servers).
Nicolas Grekas (132819) argued that the FPM
binary should embed the CLI instead, as FrankenPHP does. Jakub Zelenka
(132821) agreed.
On 8 October the author agreed, opened PR #24192
and published version 0.2. Tim Düsterhus disagreed
with the new direction, arguing that selecting a SAPI by executable name is hard to discover and
surprising: a php-like name that does not match the rule (a custom p symlink, or php-8.7)
would start FPM and possibly bind sockets, with no way out except renaming. Düsterhus also rejected the
view that version 0.1 privileged FPM, and preferred an explicit flag on php (--fpm, or for
example --mode=fpm), by analogy with -S for the built-in server.
On 10 October Marc Henderkes agreed with Düsterhus
and gave three reasons:
php --fpm <fpm-args> is clearer than renaming the binary, php-fpm usually lives in /usr/sbin,
which users should not normally call directly, and php-fpm php-cli <cli-args> is awkward.
Henderkes did not share Garfield's concern about privileging FPM, and preferred a top-level
--fpm flag over --mode=fpm.
Later on 10 October (19:07 UTC) Levi Morrison suggested another
alternative, replying to the version 0.1 announcement:
building the Zend engine as a shared library, libzend.so. Morrison said this used to be possible
to build and assumed it was partly removed for performance. It would also let most extensions link
their symbols against libzend.so instead of leaving them undefined for the executable to provide. Morrison said the
performance cost would need measuring, but would personally prefer this solution if it is viable. The
RFC's rejected alternative is a shared libphp.so with small php and php-fpm executables. The
message does not say how libzend.so would relate to that option, and this page does not treat them as
the same proposal.
As of 22:00 UTC on 10 October the author had not replied to either message. The RFC page was still
version 0.2, and PR #24192 was still open, not a draft and not merged (last updated 2026-10-08).
Limitations
This page summarises a version 0.2 proposal that changed fundamentally two days after version 0.1,
with two participants on the list now preferring an explicit --fpm flag on php and a third
suggesting a shared libzend.so instead. The direction, the name-matching rule and the handling of
PHP_BINARY can change before a vote, and nothing here is in released PHP.
Exception for invalid characters in number base functions
Under discussion; not accepted. The RFC page (version 0.3, dated 2026-08-11, last modified
2026-10-09 at 10:29 UTC) and the official RFC index both list it as under discussion. The proposed
version is PHP 8.7, and acceptance needs a 2/3 majority. On 9 October the author
announced that the vote will open on Friday
16 October. The voting thread reads "tbd", so no vote is open yet.
Proposal
bindec(), octdec(), hexdec() and base_convert() would throw when the input contains a
character that is not valid in the given base. Today they ignore such characters and emit
E_DEPRECATED ("Invalid characters passed for attempted conversion, these have been ignored"),
so hexdec('z') returns int(0). For base_convert() validity depends on the source base:
'f' is accepted in base 16 but would throw in base 15.
Version 0.3 changed the exception from ValueError to a plain Exception. The page argues that
ValueError implies a bug in the program, which bad input is not necessarily. More specific
exceptions are listed as rejected for now: the SPL exceptions belong to another extension, and
ext/standard has no suitable exception of its own. Both the official index and the internals
thread still use the older "Throw ValueError" title.
Background and affected versions
The Base Convert Improvements RFC,
implemented in PHP 7.4, added the deprecation notice and named an exception in a later version as
the next step. That step never happened. Since PHP 7.4 invalid characters are ignored with a
deprecation notice; the RFC's example lists this behavior for PHP 7.4 through 8.6. The RFC expects separator
characters such as hexdec('1234-5678') to be the main legitimate use broken by the change.
base64_decode(), intval() and other functions are out of scope.
Evidence and limitations
The implementation is php-src PR #22365, opened on
19 June 2026. GitHub labels it "ABI break". Its description says it changes the behavior to an
Exception, but its first commit message still says ValueError. Whether it is merged or matches
version 0.3 was not checked.
An earlier internals message (132379) also said a
vote would open the following Friday, and called the RFC "not really controversial". The fetched
text has no date. No vote followed it: the page still reads "Voting thread: tbd" and the RFC is
still under discussion.
The page's vote block shows "Final result: 0 0 0" and "This poll has been closed". This is the
unopened widget, not a ballot result.
Related: Filter enums, another PHP 8.7 proposal about handling invalid
input.
Under discussion; not accepted. The RFC page (version 1.0, dated 2026-02-13, last edited
2026-10-06 at 13:08 UTC according to the wiki's change feed) and the official RFC index both list it as Under Discussion. It proposes the
"next minor version after PHP 8.6 (likely PHP 8.7)". Acceptance would need a 2/3 majority.
The proposal was split out of the accepted
ext/uri follow-up RFC and builds on the URI extension
that PHP 8.5 added (Uri\Rfc3986\Uri and
Uri\WhatWg\Url). It would add one immutable class, Uri\QueryParams, that stores query
parameters as an ordered list of name-value pairs, with no global request state. It has:
parsers for RFC 1866 form encoding, RFC 3986 and the WHATWG URL rules, plus fromArray().
Parsing percent-decodes the input. It does not require a syntactically valid query
component; strings are percent-encoded again when the to*String() methods recompose them;
getFirst(), getLast(), getAll() and list() retrieval, and modifiers (append(),
set(), delete(), sort()) that return new instances;
default parsing limits of 10,000 input bytes and 1,000 parameters. These can be configured,
and exceeding them throws Uri\InvalidQueryParamsException;
configurable mappings for boolean values and a Uri\QueryParamNullPolicy enum for null
values (SkipTuple, KeyOnly as the default, EmptyString), and an array API for PHP's
bracket notation. toArray() takes a nesting limit (default 64). The RFC says toArray() is
not a general inverse of fromArray();
getQueryParams() and withQueryParams() methods on Uri\Rfc3986\Uri and Uri\WhatWg\Url;
different empty-field rules per parser. parseRfc1866() and parseRfc3986() keep empty
fields (for example the one after a trailing &) and give a field without = a null
value. parseWhatWg() drops empty fields and gives such a field an empty string. The RFC says
that recomposing and reparsing a list is not guaranteed to reproduce every possible list.
The RFC presents the class as an alternative to $_GET, which it does not propose to change.
It lists the differences someone migrating would see: $_GET replaces dots and spaces in
names with underscores, keeps only the last value of a repeated name, and drops input beyond
max_input_vars with a warning. QueryParams keeps the names as given, keeps all
occurrences, and throws when its own limit is exceeded. The RFC states that the change is
backward compatible.
Evidence and limitations
The implementation, php-src PR #22551, was an
open draft when checked on 2026-10-04. It was last updated 2026-07-05.
The discussion thread opened in February. Its newest
public message was dated 2026-07-16 when checked on 2026-10-04, and no reply mentions the
1, 3 or 4 October page edits. The wiki revision history requires a login, so what those edits
changed is unknown. After the 4 October edit, the status, version, open issues ("None"),
parsing limits and the vote placeholder were unchanged.
The wiki change feed shows another edit on 2026-10-06 at 09:21 UTC; the internals feed has no new
message on the thread. Afterwards the header still read version 1.0, Under Discussion, and the default
parsing limits were still 10,000 bytes and 1,000 parameters. The page and its raw export were
truncated at 18,000 characters, so the open issues and vote block were not re-checked.
The page's vote block still has placeholder dates ("2026-xx-xx") and is marked closed, but
its widget carries a 2026-10-30 close date. This page treats the vote as not opened; a
close date in a template is not a vote announcement.
The opening message noted unresolved points (the array API, and whether the class should be
readonly). The current page lists no open issues and declares the class final readonly.
On 2026-10-06 at 13:37 UTC the author posted a changelog
of the reworked RFC: readonly class, QueryParamNullPolicy replacing a boolean option,
parsing limits applying only during parsing with the nesting limit belonging to toArray(),
documented type conversions, decided array-API questions, the Uri/Url integration methods and
specified handling of empty fields, malformed percent-encoding and invalid UTF-8. The author
wrote that they would like to "bring this proposal to a vote as soon as possible (but not
sooner)". No vote has been announced. The null-policy enum, toArray() nesting default and
Uri/Url methods were checked on the page; the rest of the changelog was not checked
individually because the page is truncated at 18,000 characters.
The wiki change feed shows a further edit on 2026-10-08 at 11:44 UTC, with no edit summary and
no new message on the thread. Afterwards the header still read version 1.0, dated 2026-02-13,
Under Discussion, with PR #22551 as the implementation, and the default parsing limits were
unchanged. What the edit changed is unknown, and no vote has been announced.
On 2026-10-09 at 12:05 UTC Tim Düsterhus posted a 14-point review
of the reworked RFC. They called most points minor and said they are "super happy" with the RFC. The
suggestions include: dropping $maxQueryStringLength (a manual strlen() check is enough) and
perhaps turning the parameter-count limit into a plain argument; passing building options to the
to*() methods instead of storing them; dropping parseRfc3986(), because RFC 3986 does not define
query-parameter handling; renaming parseWhatWg() to something like parseUrlSearchParams();
questioning hasValue(), deleteValue() and withArray(); and noting that the backward
compatibility section should mention the new class name in the Uri namespace. They also called
accepting invalid percent-encoding sequences risky, without describing a concrete issue. No author
reply or page change was seen by 16:02 UTC.
In voting; not accepted. On 2026-10-05 at 13:18 UTC the author
announced on the internals list that voting had opened.
The RFC page (now version 1.0.0, status Voting, last modified 2026-10-05 13:11 UTC) and the
official RFC index both list it as voting. There is one primary question, "Stop endorsing
PEAR?", which needs a 2/3 majority. The poll closes on 2026-10-19 at 13:15 UTC. When the page was
inspected at about 16:05 UTC on 5 October it showed 14 Yes, 0 No and 0 Abstain; at about 04:05 UTC
on 6 October it showed 21 Yes, 0 No and 0 Abstain; at about 04:02 UTC on 7 October it showed
23 Yes, 0 No and 0 Abstain; at about 10:02 UTC on 7 October it showed 24 Yes, 0 No and
0 Abstain. Those are interim counts in an open poll, not a
result.
What is proposed
The RFC would end the PHP project's endorsement of the independent PEAR project in four steps:
Serve the public pages of pear.php.net (package details, downloads, manuals, public bug
reports) as a static archive. The browsable archive, including the endpoints legacy PEAR
CLI clients use, would remain until 2027-12-31 and then be offered only as a download.
Stop installing or bundling the PEAR installer with PHP source, proposed for PHP 8.7. The
RFC formalises an existing php-src pull request
(#20124).
Remove PEAR references from official PHP websites and mention Composer as the community
package manager. The RFC says this does not make Composer a PHP project.
Move the PEAR mailing lists to the inactive lists section.
Taking over governance of the PEAR package ecosystem is a stated non-goal; an independent team
could continue PEAR under domains it controls.
Motivation stated by the RFC
The RFC says the PHP project has no access to PEAR's infrastructure although it is hosted on a
php.net subdomain, cannot guarantee the integrity of the content served there, and that the
site is unmaintained with broken features and spam. It cites PEAR metadata from 2026-06-22:
534 of 603 packages marked unmaintained, and 63 of the remaining 69 not updated for extended
periods. These figures are the RFC's own count and were not independently re-measured here.
Affected users
According to the RFC, users would no longer get PEAR bundled with PHP but could install it
manually, and users of the six packages still publishing on PEAR would need their
Composer-installable versions. During the discussion the author stated that the static archive
would keep serving installer resources (for example go-pear.phar and /rest/ paths) on the
same domain, so the switch need not wait for unbundling.
History and discussion
Two earlier proposals were never brought to a vote:
Deprecate PEAR/PECL & Replace with composer/pickle
(2016, now Inactive in the index) and
Deprecate PEAR and recommend Composer
(2025, now Withdrawn). The RFC changelog for 2026-09-14 records support from a long-time PEAR
Group member for sunsetting the website and removing PEAR from PHP source, and adds the archive
timeline. The discussion thread continued on
2026-10-02. In the thread the author expected that a vote would not end before mid-October and
that nothing would land before November. That was a plan stated before the vote opened.
The vote opened on 2026-10-05; the proposal and BC sections inspected that day match the
summary above. The page's introduction also states that the PEAR web repository was recently
archived. The RFC cites pear/pearweb, but that repository's
archive state was not checked here.
Limitations: the vote is still open, so acceptance is not established, and an accepted RFC would
still need implementation work. The php-src change it formalises,
PR #20124, was re-checked on 2026-10-06. It is titled
"PECL and PEAR removal", which is wider than this RFC's PEAR scope, and it is an open draft that
has not been merged (last updated 2026-09-03). Nothing in released PHP has changed. Related: support windows.
Filter\Filter and Filter\Flag enums proposal (formerly: deprecate invalid Filter options)
Under discussion; not accepted. On 7 October the author rewrote the RFC at the same URL.
Instead of deprecating invalid options, it now proposes adding Filter\Filter and Filter\Flag
enums. The page (last modified 2026-10-07 06:06 UTC) is titled "Add Filter\Filter and
Filter\Flag Enums" and its status is Under Discussion. It still says version 0.9 and dated
2026-10-03. Its changelog says: "2026-10-07: Revised RFC to reflect the enum implementation in
php-src PR #24111." When the official RFC index was collected at 10:01 UTC on 7 October, it listed
the page under that title as discussion. Earlier collections did not list it. At 04:16 UTC the
author asked for feedback on the updated proposal
in the original thread.
Current proposal (7 October revision)
Two int-backed enums in the Filter namespace: Filter\Filter, with cases from DEFAULT and
VALIDATE_BOOL through SANITIZE_ADD_SLASHES and CALLBACK, and Filter\Flag, with cases
such as NONE, ALLOW_OCTAL, ALLOW_HEX, IPV4, IPV6 and GLOBAL_RANGE. Each case is backed
by the value of the existing FILTER_* constant. The listed Filter\Filter cases include
SANITIZE_STRING, whose constant has been
deprecated since PHP 8.1. The RFC does not comment on this.
filter_var(), filter_var_array(), filter_input() and filter_input_array() would accept the
enums as well as integers, including in 'filter'/'flags' array definitions.
To combine flags you use their backing values, for example
Filter\Flag::ALLOW_OCTAL->value | Filter\Flag::ALLOW_HEX->value.
The RFC says no constants are removed or changed and integer arguments stay accepted. The
proposed version is PHP 8.7, and the vote template is a single 2/3 question. No vote is scheduled.
The implementation is php-src PR #24111. Its state
was not re-checked in this cycle.
Some text from the earlier draft remains on the page. "Future Scope" still says the RFC "is limited
to invalid integer-valued filter and flags options". The ecosystem impact section still says
invalid option values "will no longer be silently accepted". Neither statement matches the enum-only
proposal. On 9 October (page last modified 08:03 UTC) the author changed the vote question to "Implement
Filter\Filter and Filter\Flag enums as outlined in the RFC?" (2/3 majority). The widget still shows
0/0/0 with the generic "This poll has been closed" footer, and no ballot has taken place. The
Future Scope and ecosystem text quoted above was unchanged at that time.
Feedback on 9 October
Tim Düsterhus replied that requiring the backing
value to combine flags as a bitmask "is an abuse of enums" that cancels their type safety and makes
them "fancy constants". They also pointed out the unchanged vote template. The author
answered that many filter_var() calls pass only a
filter ID with no flags (citing a Sourcegraph search), that a Filter\Flag enum could still help for
single flags, and that this should be weighed against the loss of type safety when flags are combined.
The author did not announce a change to the flag design.
Tim Düsterhus answered again at 12:22 UTC. They said
enums would help filter_var() calls without flags only slightly, would not work for
filter_var_array(), and would mix a strong enum with a free-form options field when flags are used.
They argued that, if PHP had tagged unions, per-filter
options could be passed with the case (for example Filter\Int(allowOctal: true, allowHex: true)),
and that adding the enums now would likely block that path. The tagged unions RFC (2020) is listed
as inactive in the official index and is not part of PHP.
History: the invalid-option deprecation draft
As first published, the RFC page (version 0.9, dated 2026-10-03, last modified
2026-10-03 05:37 UTC) had the status Draft. Its implementation, discussion and
voting threads were marked "tbd". The author announced it on internals at 05:38 UTC on
3 October (message 132778), describing it as opened
for feedback. When the official RFC index was collected at 10:01 UTC that day, the RFC was
not yet listed, and it was still absent from the index collected at 22:01 UTC. The page contained
an unfilled vote template ("Implement $feature…", 0/0/0). No ballot has taken place.
The first on-list reply, from Calvin Buckley at 20:22 UTC on 3 October
(message 132779), called the proposal reasonable "at
first glance". It suggested possible follow-on RFCs: turning the filter types into an enum and
removing the SAPI-side input filters for superglobals. These are one participant's suggestions,
not part of this RFC.
At 08:38 UTC on 4 October the author, Muhammed Arshid KV, replied
(message 132780). The author agreed that the
filter types "would be better handled as a separate, focused RFC for enums" and wrote: "I'll
close this RFC and pursue the enum proposal separately". The reply links
php-src PR #24111, opened on 4 October. The PR adds
backed enums Filter\Filter (filter IDs) and Filter\Flag (flags), which filter_var(),
filter_input() and related functions would accept alongside the existing integer constants.
Those constants are kept, and alias constants that share a value get only one enum case. As of
4 October no RFC page for the enum proposal was in the official index, and the PR's requested
code-owner reviews were still pending. It is an implementation proposal, not an accepted change. The reply does not say
whether the invalid-option deprecation will return in another form.
On 4 October at 16:11 UTC a php-src maintainer (iluuu1994) commented on PR #24111 and asked the
author to start a mailing-list discussion first. The maintainer added: "I'm not sure it's a
good idea to extend this API further." The author agreed. At 03:29 UTC on 5 October the author
opened a discussion thread for the enum proposal. They
described the enums as type-safe and more discoverable alternatives to the integer constants,
which remain supported. They asked for feedback on the API, naming and approach "before
preparing the RFC". When checked at 04:01 UTC, no enum RFC page was listed in the official
index. The PR was open, had no submitted reviews, and was labelled "RFC" and "ABI break".
On the original thread, Derick Rethans replied to the suggestion of removing SAPI-side input
filters that removing the filter defaults "was a mistake" and that the deprecation "ought to be
undone, before the extension gets refreshed" (quoted in the author's reply). At 15:15 UTC on
6 October the author answered that the enum PR
only aims to modernize the API, and that they will look into the history of the removed
defaults before proceeding. As of 6 October no new RFC page or status change had followed.
That draft targeted the integer-valued filter and flags options of filter_var(),
filter_var_array(), filter_input() and filter_input_array(). The draft said some invalid
values are currently converted to integers silently. Under that draft, PHP 8.7 would emit
E_DEPRECATED and PHP 8.8 would throw ValueError. The examples cover two cases:
a value of the wrong type, such as 'flags' => 'abc' or 'filter' => 'abc' ("must be of
type int, string given");
an integer that matches no known value, such as 10050 ("Unknown flags with ID…" /
"Unknown filter with ID…").
The draft's backward-compatibility section said that code that relies on implicit conversion of
invalid values to integers would need to use the appropriate constants instead.
The deprecation described above was the content of version 0.9 before the 7 October rewrite. The
current page no longer proposes it, and the author has not said whether it will return.
Limitations: the earlier draft's introduction mentioned an array passed as flags, but its
examples used strings and unknown integers. The current enum proposal may still change; its
naming, the flag-combination approach and the API-extension concern raised on the PR are open.
This page does not describe current behavior beyond what the RFC states. Related:
bcrypt length proposal, which uses the same 8.7/8.8
deprecation-then-error schedule.
Under discussion; not accepted. The RFC page (version 0.4; the page keeps the 2026-09-27
date) and the official RFC index both list it as under discussion. Its proposed version is PHP 8.7.
The voting thread reads "tbd", and no vote has been opened.
The proposal would add native terminal primitives to ext/standard, for POSIX terminals and
Windows consoles. Today libraries call tools such as stty or use the Windows Console API
directly.
Proposed API (version 0.4)
Version 0.4 was published on 2026-10-05 (20:37 UTC wiki edit). It replaces version 0.3's
userland-implementable interfaces with concrete classes.
Io\Terminal\Terminal is a single final native class with a private constructor. Instances
come only from Terminal::fromStdio() or Terminal::fromStreams($input, $output = null).
fromStdio() resolves the standard descriptors when an operation runs, while fromStreams()
keeps the supplied streams. Terminal and ModeToken cannot be cloned or serialized.
ModeToken is a final opaque class that userland cannot construct. enableRawMode() returns
one lease token, and restoreMode(ModeToken $mode) releases it. Leases are shared per logical
terminal within a request, and the last release restores the saved mode. Destroying a token
and request shutdown restore the mode only on a best-effort basis. A token inherited after
pcntl_fork() cannot be explicitly restored in the child.
getSize(): ?TerminalSize returns null when no usable size is available. It does not fall
back to COLUMNS/LINES. TerminalSize is a final readonly value object with a public
constructor that throws ValueError for non-positive dimensions.
readKey() returns a Key enum case (arrows, function keys, Resize and so on), a string or
null on timeout. readLine() reads one line and returns null on immediate EOF; it has no
timeout and throws while a raw-mode lease is active on the same native terminal.
readSecret() reads input without echoing it; Escape, Ctrl+C and Ctrl+D cancel it with an
exception. Timeouts are Time\Duration values, from the
Duration class RFC, which the official index lists
as implemented.
Operational failures, and EOF during readKey()/readSecret(), throw
Io\Terminal\TerminalException, declared as extending \Io\IoException. Invalid arguments
such as negative durations or consumed tokens throw ValueError.
The declared API block contains five Io\Terminal\ symbols: TerminalException, Key,
TerminalSize, ModeToken and Terminal. The RFC lists prompts, history, completion, line
editing and the existing readline extension as outside its scope. Code that already declares
those names would conflict with them.
Evidence and history
The author's internals announcement names the
ext-terminal extension (Packagist prateekbhujel/php-terminal) as the reference
implementation and draft php-src PR #23941 as the
core implementation. Version 0.3's discussion status recorded that, after review on the list, the
initial draft's Terminal::create() was replaced by SystemTerminal::fromStdio(), the
interfaces and native classes were renamed, readLine() was added, and failures and timeouts
now use TerminalException and null. Version 0.4 then removed the interfaces and renamed SystemTerminal to
Terminal (see below). Whether PR #23941 already matches version 0.4 was not checked.
Testing approach: interfaces removed
Version 0.3 had Terminal and ModeToken interfaces, added after a request for a way to mock
the terminal. Tim Düsterhus and Bob Weinand argued that
the TTY stream, not the terminal object, is the IO boundary, and that a userland ModeToken
could not be restored by the native terminal. Version 0.4 follows that view. Ordinary PHP
streams passed to fromStreams() now provide scripted input for tests: readKey() decodes the
bytes, readLine() reads through the stream layer, readSecret() does not echo the input to the
output stream, and enableRawMode() returns a no-op lease instead of failing. The RFC states that
such streams do not simulate terminal dimensions, resize events or native mode failures, and that
applications needing replaceable implementations can define their own abstraction. Whether
version 0.4 settles the list discussion is not yet known. The inspector truncated the RFC page at
18,000 characters, so its later sections (including any changelog) were not read.
Stream abstraction question
On 2026-10-06 Jakub Zelenka asked whether the
API should use some stream abstraction. The message notes that streams are not used directly in
Io so far: the underlying file descriptors are abstracted through Poll\Handle
implementations, which IO hooks also use for direct reads and writes. The message calls this
something to consider, not a blocker. The RFC author
replied on 2026-10-07, leaning towards keeping
fromStdio() and fromStreams() in this RFC. The reply says fromStreams() should keep the
supplied stream's buffering and input behaviour, including php://memory, and that the API also
needs the native terminal for mode changes and Windows console input. A factory for a shared
abstraction could be added later, but ownership and buffering rules would still need work. The
reply asks which abstraction was meant; no answer had been collected when this page was updated.
The RFC page (version 0.4) has not changed.
Vote widget added
On 2026-10-09 at 12:40 UTC the author said they had
left the voting widget out of the RFC and had now added it, with the API unchanged. The wiki change
feed records the edit ("Add voting choices.") at 12:30 UTC. The page still reads version 0.4, Under
Discussion, voting thread "tbd". No vote date was announced. The bounded fetch cut the page off
before the vote section, so the question and required majority were not read.
Limitations
This page summarises a proposal under discussion. Names, semantics and the target version can
change before a vote, and nothing here is available in released PHP.
Under discussion. The RFC (version 1.0, dated 2026-09-21) proposes three symbols in the
Time\ namespace: an immutable Time\Instant class for an absolute point on the timeline,
a Time\Clock interface, and a Time\Clock\SystemClock implementation. Its proposed version
is the next minor, PHP 8.7. The page's status line reads "Under Discussion" (it read "Unter
Discussion" when first checked on 2026-10-01); the official RFC index lists it as discussion.
Design points stated in the RFC
An instant carries no timezone or calendar; arithmetic moves it by a Time\Duration.
Leap seconds are ignored, following common language and operating-system practice.
A clock interface lets code obtain "now" through an injectable dependency, which helps testing.
A toInstant() method on DateTimeInterface, DateTime and DateTimeImmutable would
convert an existing object, dropping its timezone but keeping microsecond precision. A
2026-10-06 edit added this method to the backward-compatibility section: it could conflict
with a method of the same name declared in a userland subclass. The RFC notes that adding
symbols is generally not considered a break under policy.
Time\Clock\SystemClock is a singleton. Its constructor is private, and the instance comes
from SystemClock::get(). The authors made this change after list feedback
(message 132825, 7 October). now() returns
values that are not guaranteed to increase monotonically.
Unix-timestamp conversions use a to prefix: toUnixTimestamp() and
toUnixTimestampMilliseconds(). This follows the convention of toIso8601DateTimeString() and
toInstant() (message 132829).
getNanoseconds() keeps its name and returns the sub-second fraction (0 to 999 999 999). The
RFC leaves the instant's epoch unspecified.
Time\Instant will not implement JsonSerializable. In
message 132827 the authors said JSON has no
canonical representation for an instant, and that a method that always throws would make it
look supported. They said json_encode()'s default object output is a problem for ext/json to
solve. Regular PHP serialization is supported, but its format is an implementation detail.
The authors want a configurable-resolution "coarse" clock to be a decorator, not part of
SystemClock, and consider it out of scope, like testing clocks.
The RFC compares Time\Clock with PSR-20's ClockInterface, which returns DateTimeImmutable
instead; ready-made testing clocks are listed as future scope.
It builds on the Duration class RFC, which the
official index lists as implemented. The vote, when opened, is a single 2/3-majority question.
Limitations
This page summarises the proposal only. The page still shows version 1.0 and has no changelog
entry for these edits. The renames and the singleton come from the RFC stubs as checked at
16:01 UTC on 2026-10-07 and from the authors' messages. Other points in the
review thread are still open. On 9 October the reviewer (Marc, who
collected the review points on 6 October) replied:
they accept keeping the coarse clock out of scope, but would prefer a future built-in coarse system
clock over a decorator, because a decorator must read the precise clock first; they cited their own
Linux arm64 measurements of about 13.3 ns for CLOCK_REALTIME and 3.3 ns for
CLOCK_REALTIME_COARSE. They still object to the name getNanoseconds() and propose
getNanoOfSecond() or getNanosecond() (matching DateTime::getMicrosecond()). These are review
comments, not RFC changes. The page's last-modified line was cut off by the bounded fetch on 9 October,
so a later page edit was not ruled out. On 9 October Tim Düsterhus agreed to disagree
with a participant who wanted a clock implementing both Time\Clock and PSR-20's ClockInterface,
and said a short note on this had been added to the rejected features. The wiki change feed summary
for the 12:32 UTC edit instead reads "Add renaming now() to rejected features". The bounded fetch cut
the page off before that section, so neither note was read on the page. Names, methods and the target version can change
before a vote, and nothing here is available in released PHP.
Reject bcrypt passwords longer than 72 bytes proposal
Inactive; not accepted. The status signals conflict. The official RFC index, collected at
10:01 UTC on 5 October, now lists this RFC as inactive. On 4 October it listed it as under
discussion, and before that as a draft. The RFC page itself still says Draft, version 0.1, and
its changelog has only its 2026-09-27 entry. Its discussion-thread field links the internals
thread (news-web.php.net/php.internals/132689). On 5 October the author
wrote that the proposal had drawn "much criticism because
of its backwards incompatibility" and that they did not think it "is worth pursuing further at
the moment". They now propose the opt-in PASSWORD_BCRYPT_SHA256 algorithm
instead. The page does not say the RFC is withdrawn. No vote was opened.
What is proposed
When password_hash() uses PASSWORD_BCRYPT (including PASSWORD_DEFAULT when it resolves
to bcrypt) and the password exceeds 72 bytes:
PHP 8.7 would emit E_DEPRECATED;
PHP 8.8 would throw ValueError.
password_verify() is deliberately left unchanged so that existing hashes made from longer
inputs still verify. The RFC proposes a single 2/3-majority vote. Voting has not started, and the
page's voting-thread and implementation fields read "tbd". Its references section names php-src
pull request #23076 ("Bcrypt 72 byte password handling"). The page's empty vote widget shows
0/0/0 and "This poll has been closed"; that is the template's default rendering, not a ballot.
Discussion so far
The motivation is authentication bypasses caused by silent truncation when applications
prepend nonces, peppers or other hashes to the password (the RFC cites CVE-2025-68402 in
FreshRSS). Early replies on the list are critical: respondents argue that input length is the
application's responsibility and that a runtime error would break users who choose long
passphrases. The outcome is open.
Later in the thread, Derick Rethans said he would vote
against it because of the backward-compatibility break. This is a stated intention; no vote
has been opened. As an alternative, Tim Düsterhus suggested adopting passlib's
bcrypt-sha256 construction, a defined pre-hashing scheme with its own output format. He also
argued that passing a raw binary pre-hash to bcrypt is fragile because the digest can contain
NUL bytes. The RFC author has opened a draft php-src PR,
#24073 "bcrypt_sha256", on 2026-10-02. As of
4 October this RFC page (still version 0.1) does not mention this alternative, and nothing in it
is accepted. At 11:27 UTC on 4 October the same author created a separate draft RFC,
PASSWORD_BCRYPT_SHA256, which proposes the alternative as a new opt-in algorithm.
It describes this RFC as a "previous attempt" but does not say it is withdrawn. That RFC moved to
discussion on 5 October, when the official index moved this one to inactive.
The page has a Discussion section that summarizes the opposition arguments
(backward compatibility, the limit being in bytes, which hits multi-byte scripts, sufficient
entropy below 72 bytes, rehash-on-login exposure) and the support arguments (harm from silent
truncation, NIST guidance against truncation, better API design). It lists no open issues.
Current behavior
Nothing has changed in released PHP. See bcrypt length limits
for today's truncation behavior and mitigations.
PASSWORD_BCRYPT_SHA256 for password_hash() proposal
Under discussion; not accepted. The official RFC index lists this RFC under discussion (it
was listed as a draft on 4 October). At 09:20 UTC on 5 October the author
announced the RFC on the internals list. After a page
edit at 13:13 UTC the RFC page also reads Under discussion (still version 0.1) and links that
announcement as its discussion thread. The earlier conflict between the page header (Draft)
and the index is therefore resolved. The voting thread is "tbd", the changelog still has only the
2026-10-04 entry, and no vote has been opened. The author is Sjoerd Langkemper, who also wrote the
72-byte rejection proposal.
What is proposed
The RFC adds a new, opt-in password_hash() algorithm identifier, PASSWORD_BCRYPT_SHA256
(value "bcrypt-sha256"), plus PASSWORD_BCRYPT_SHA256_DEFAULT_COST = 12. The password is first
run through HMAC-SHA256, keyed with the 22-character bcrypt salt string. The base64-encoded
digest (44 bytes) is then passed to bcrypt. That input can't contain NUL bytes and is far below
bcrypt's 72-byte limit, so long passwords are no longer truncated.
Hashes look like $bcrypt-sha256$v=2,t=2b,r=<cost>$<salt>$<digest>. The RFC says this is
byte-for-byte Passlib's bcrypt_sha256 version 2 format, so PHP and Passlib hashes would be
interchangeable. That interoperability claim comes from the RFC and was not tested here.
password_verify(), password_needs_rehash(), password_get_info() and password_algos()
would recognise the new algorithm. cost keeps bcrypt's 4–31 range, and out-of-range values
throw ValueError.
PASSWORD_BCRYPT and PASSWORD_DEFAULT would not change. Changing the default is listed only
as future scope.
The hashes are longer than plain bcrypt hashes, so stored-hash columns may need widening.
At build level, ext/standard gains a compile-time dependency on ext/hash. The RFC argues
this has no practical effect because hash has been always enabled since PHP 7.4.
The vote, when opened, is planned as one question needing a 2/3 majority. The proposed version
is the next PHP 8.x ("e.g. PHP 8.7").
Relationship to the 72-byte proposal
This RFC follows from the internals discussion of the
72-byte rejection proposal, where Tim Düsterhus suggested
Passlib's construction instead. The new RFC calls that proposal and php-src PR #21675 (a
Blowfish NUL-byte fix) "previous attempts", and rejects changing PASSWORD_BCRYPT itself as too
risky. In the 5 October announcement, the author wrote that the 72-byte proposal had drawn "much
criticism because of its backwards incompatibility" and that they did not think it "is worth
pursuing further at the moment". The official index now lists that RFC as inactive. Neither
proposal is accepted.
Discussion so far
In the thread on 5 October, Tim Düsterhus supported the
proposal, citing Passlib as precedent. He also argued that the RFC overstates the NUL-byte
problem, because PHP already throws ValueError when a new password contains a NUL byte. He
asked separately whether the $2b$ bcrypt identifier should be supported, saying he understood
it to be identical to $2y$. Another participant asked for a separate "rounds" option. Tim
Düsterhus replied that bcrypt's rounds and PHP's cost are the same parameter, and that
keeping the name cost matches PASSWORD_BCRYPT. These are list comments; the RFC text had not
changed in response when it was inspected on 5 October.
Passlib is "not a trusted source for security related issues", and the RFC does not say which
official specification (an IETF RFC or a NIST document) describes bcrypt_sha256.
The scheme differs from the OWASP Password Storage Cheat Sheet, quoted as recommending
bcrypt(base64(hmac-sha384(password, pepper))) with the pepper kept out of the database.
Hashes may not work with other languages, such as C#, that share a credentials database.
The author replied by asking for a standardized
alternative with better interoperability, mentioning PBKDF2, which NIST standardizes but which
they called generally less resistant to offline cracking than bcrypt. A participant who signs as
Andrey objected to the pace. They said the PR had
been written a day after the idea was raised, and asked for time because they have been working on an
alternative for weeks and hope to publish it in a week or two. No alternative RFC page was seen.
The RFC page inspected on 8 October was unchanged (version 0.1, Under discussion, voting thread
"tbd").
On 9 October Rowan Tommins replied to the OWASP
objection. They said the "password shucking" attack it describes needs the same inner hash to exist
elsewhere for that user, which with a per-hash salted HMAC would need the same password and salt.
In their view the salt plays the role of the OWASP pepper. They also noted that OWASP recommends a
72-byte maximum password length, as the inactive length RFC did.
The objector replied that such questions belong with OWASP, and that following standards and
interoperability is what PHP should offer. Rowan Tommins answered
that they had asked no questions. They concluded that the OWASP page describes a weakness in similar
algorithms and that the proposed algorithm, while not identical to the OWASP suggestion, addresses the
same weakness differently, so it is "worth considering further". These are list opinions; the RFC page was
not re-checked in this cycle.
Implementation
The implementation is php-src PR #24073, opened on
2 October 2026 and now titled "ext/standard: bcrypt-sha256 password hash". GitHub labels it
"ABI break", and on 2–3 October a core reviewer requested changes, mostly to the tests. As of
8 October it is still open, not marked as a draft, and not merged (last updated 6 October).
Limitations
This page summarises a draft proposal only. Names, format details and the target version can
change, and nothing here is available in released PHP. For today's behavior, see
bcrypt length limits.
Under discussion; proposed for PHP 8.7. The RFC page (version 1.4, dated 2026-10-03)
specifies :$name in array construction and destructuring. It stands for the explicit
key/value form "name" => $name and applies to [], array() and list(), including
destructuring in foreach.
Only a single directly named variable (T_VARIABLE) qualifies; the key is its exact,
case-sensitive name. Variable variables, array dimensions, object or static properties and
arbitrary expressions are invalid, and so is the function-argument form foo(:$name).
Whitespace and comments may appear between : and the variable. Existing keyed-element rules
still apply: duplicate keys (the later element wins, including with spread), warnings for
missing keys, and the bans on mixing list() with [] and keyed with unkeyed destructuring
elements. Construction copies the value; [:&$name] is invalid, so a reference still needs
'name' => &$name. Destructuring with an ArrayAccess object reads offsets, not properties.
The page says voting has not started. It links a draft implementation,
php-src PR #24034, which was open and marked as
a draft on 3 October.
Spelling history. Version 1.2 (2026-09-30) used =$name, after the original :$name
was dropped because of possible confusion with ternary syntax. In an internals
message on 2 October, the author announced a return to
:$name. Version 1.4 adopts it, lists the equals-sign prefix as a rejected feature, and
documents how ternaries are parsed. The conflict noted on 2 October is resolved on the RFC
page. Until a vote, the spelling can still change in discussion.
This is a proposal, not supported stable PHP syntax. Recheck the
primary RFC before repeating examples or status.
Draft; not accepted. The RFC page (version 1.0, dated and last modified 2026-09-28) lists
its status as Draft and its target as PHP 8.7. It was not present in the collected official
RFC index on 2026-10-02.
The proposal adds a procedural intl_date_format() to ext/intl, wrapping
IntlDateFormatter so that a single call can format a DateTimeInterface, a Unix timestamp
or a date string. Optional parameters cover locale, date/time style constants, timezone,
calendar and an ICU pattern that overrides the style constants. The RFC says it returns a
string on success, never false, and throws TypeError, ValueError or IntlException on
errors. It describes an internal formatter cache and reports no backward-compatibility break.
Limitations: no discussion outcome was reviewed. Today, the same formatting is available through
IntlDateFormatter objects. Related: Time\Instant and Time\Clock.
Draft; not accepted. The RFC page (version 0.2, dated 2026-09-29, last modified
2026-10-01) and the official RFC index both list it as Draft. Its discussion and voting
threads are marked "tbd". The page contains a vote template showing no votes; no ballot has
taken place.
The motivation is that PDO drivers handle binary data inconsistently: SQL Server via PDO_DBLIB
needs special quoting, some ODBC drivers return a binary column bound as a string as hex, and
some databases convert character encodings on string fetches. A binary column is not
necessarily a LOB, and some drivers expect PDO::PARAM_LOB to be a stream.
The RFC offers two alternatives: a dedicated PDO::PARAM_BINARY data type, or a modifier flag
combined with PDO::PARAM_STR or PDO::PARAM_LOB. It says the choice will be settled in
discussion or, if unclear, by a vote. It notes possible effects on emulated prepares,
PDO::ATTR_DEFAULT_STR_PARAM, PDO::ATTR_STRINGIFY_FETCHES and on what drivers report from
getColumnMeta(). An implementation pull request is linked from the page.
Limitations: the reviewed text names no target version. Related:
support windows.
Draft; not accepted. The RFC page (version 0.2, dated 2026-09-29) lists its status as
Draft. It was not present in the collected official RFC index on 2026-10-02, so its status
here comes only from the page itself. No target PHP version was stated in the reviewed text.
The proposal builds on IO hooks and operations. That RFC's poll-based operation
queue can wait for socket or pipe readiness, but cannot help with operations that have no
readiness notion, such as regular-file reads, fsync() or host-name lookups. The Ring
(Io\Ring\Engine, an Io\OperationQueue) would perform such operations itself so that a Fiber
could suspend instead of blocking the process.
It would be implemented on a bundled C library, ior, with three backends: io_uring on Linux,
IO completion ports on Windows, and a thread pool elsewhere. The page names the thread pool as
the Linux fallback when io_uring cannot be used (for example kernels older than 5.19,
io_uring_disabled or restrictive seccomp profiles). The RFC reports that the backend is chosen
automatically and exposed only informationally. It explicitly does not use libuv.
The RFC includes its own benchmark comparisons and makes some modes opt-in on that basis;
these are the author's measurements, not independent results.
Under discussion. The proposal describes a request-local provider interface through
which blocking waits and IO operations can be handled by an event loop or Fiber scheduler.
It aims to let ordinary blocking-style calls cooperate with such a provider.
The RFC separates the integration mechanism from scheduling policy; it does not itself
supply a built-in scheduler. The proposal depends on the related Polling API additions.
The companion Ring API is described as a separate draft.
Do not treat this as available PHP behavior or infer acceptance from the activity around it.
Read the RFC for operation and platform details.