This year, FrankenPHP fixed a session ID leaking to other clients, a use-after-free, and a double free. Each one, its creator says, came from state that wasn't reset between two requests. That's because, he says, PHP's engine can't tell when a request ends, if the same script serves the next one. He wants to change that.
Hello world, it's Thursday, November 5, 2026, and here's what happened This Week in PHP Internals. 14 stories this week, so let's get into it. But first,
AI made building software easy, and owning it hard. Scalpels installs open source apps into your own Laravel Cloud or Forge account in one click, so your data never leaves your infrastructure. The Free plan is zero dollars, forever, and new Laravel Cloud accounts get ten dollars in credit. Get started at scalpels.app.
A few corrections from last week, most of them from Tim Düsterhus. On the CSV extension, Tim didn't ask Damian to wait until after the security release. The release had just happened, so people were likely busy patching their servers and hadn't caught up on the list, and he suggested waiting until things calmed down. And on calling extension methods without parentheses, we missed Rowan Tommins's main point. He wrote: "It also seems odd to propose it now, as an extension to something that doesn't even exist yet."
Tim also caught two in last week's top story and quick hits. In Brian Nguyen's quote, "not deprecating integer" means the (integer) cast, which PHP just deprecated. And I said Sjoerd "postponed" openssl_get_channel_binding. He proposed it, and it was brand new. Thanks, Tim.
And a viewer on Bluesky pointed out that Brian's first mail said "I am not proposing freezing features or cosmetic deprecations." That's right. The embargo was in his second thread, the next morning, and our social posts skipped that distinction.
Links: Tim Düsterhus on the CSV extension vote timing · Rowan Tommins on paren-less extension methods · Brian Nguyen's first mail · Brian Nguyen's second mail
This week's top story: Kévin Dunglas, who created FrankenPHP, wants PHP's engine to know when a request ends. In worker mode, one long-running script handles request after request, keeping things like the Composer auto and framework services in memory. Symfony, Laravel and Yii support it natively. But the engine doesn't know, so FrankenPHP resets each request's leftovers by reaching into PHP's internals, which he calls fragile. He wrote: "Each omission results in a cross-request leak or a memory safety bug."
So he proposed two optional hooks, request end and request begin, that a worker-mode server calls between requests, so each extension resets its own state. Servers that don't call them see no change. After talking it through with Tim Düsterhus, he narrowed the rule to state built from the incoming request, and he's writing a short RFC.
Pierre asked whether last week's Scope RFC already covers this, and Larry Garfield asked how the two lifetimes would stay in sync. Kévin's answer: a scope gives a request an identity, but no clear end. So request begin would create the scope, and request end fires when the response is done, even if the scope lives on. He wrote: "The user-space and engine-space lifetimes stay in sync because the SAPI drives both."
Links: Kévin Dunglas, the proposal · Narrowed after review · How it works with Scope · Draft patch
bcrypt only reads the first 72 bytes of a password, and now there are two RFCs to fix it. Andrey Andreev opened the second one this week, the alternative he asked for time to finish. PASSWORD_BCRYPT_WRAP hashes the password first, with cSHAKE256 by default or HMAC-SHA512, then runs bcrypt on the result. The RFC says that removes bcrypt's length and null byte limits.
Kamil Tekiela objected that it shrinks bcrypt's input space and is open to hash shucking. Andrey replied that a prehash doesn't reduce bcrypt's security or input space, and that his salts keep the prehash separate from a plain hash. Tim Düsterhus asked for pseudo-code to settle it, and Andrey added it to the RFC on Monday.
Sjoerd Langkemper, who wrote the other RFC, compared the two. His uses the same algorithm as Python's passlib and adds less code. Andrey's supports two prehashes in the PHC string format. Sjoerd thinks both are secure enough, and that one new algorithm is enough. He wrote: "It is unfortunate that we now have two RFCs that are so similar. Voting on them in isolation could result in an undesirable outcome." Andrey replied: "It just turned out this way, hopefully we don't have to look at it as mine against yours."
Links: RFC: Add PASSWORD_BCRYPT_WRAP · RFC: PASSWORD_BCRYPT_SHA256 · Sjoerd Langkemper's comparison
Larry Garfield is sold on the use case for Frode Børli's Scope RFC, but he's worried about code that doesn't know it's running inside a scope. A new scope starts with empty superglobals, so a legacy function that reads $_GET, called 20 functions deep, quietly stops working. He'd rather a new scope start from a clone of its parent's.
He also expects everyone to hand-write WeakMap code to keep state per scope, and floated a Scoped attribute to automate it. In his own example, he forgot to initialize the WeakMap. He wrote: "So this is a pattern we need to work to make as fool-proof as possible, because we're all fools." No lies detected.
Rowan Tommins welcomed a focused RFC that adds only what userland can't do, shared the WeakMap worry, and asked whether "context" would be a clearer name. Frode is keeping Scope, and says the WeakMap pattern takes getting used to, but isn't hard. He hasn't answered Larry yet.
Links: RFC: Scope · Larry Garfield · Rowan Tommins · Frode Børli
PHP's next date and time class would have no timezone at all. Tim Düsterhus and Derick Rethans's Time\Instant RFC is a point in time, and a clock returns one. Back after the security releases, Tim answered a reviewer's 15 points and kept the core design: the clock returns an Instant, not a date in some timezone. He wrote: "The timezone is a presentation-layer concern and stuff you 'present' to the user is generally expected to be coming from the database, rather than coming from the clock."
He also turned down a raw Unix timestamp shortcut, to keep the API well-typed. The system clock can no longer be serialized, and two ideas moved to future scope: exposing the clock's resolution, and a faster, coarser system clock. There's no implementation pull request yet.
Links: RFC: Time\Instant and Time\Clock · Tim Düsterhus's answers to the review · follow-up
Comments and trailing commas in JSON were about to go to a vote. Otar Chekurishvili's RFC adds two opt-in flags for json_decode and json_validate, strict JSON stays the default, and Larry Garfield had no objections left.
The vote was planned for Monday. Then Jakub Zelenka commented on the pull request: "I personally don't like non standard additions as they might complicate future optimizations in the parser." So Otar postponed the vote, the morning it was due to open.
He replied on the pull request that both flags are opt-in, and a future fast parser could handle strict JSON and fall back when a flag is set. In strict mode, the change costs about 1.6% more CPU instructions, or 0.6% with a small scanner change. He'll answer the concern in the RFC, then announce a new vote.
Links: RFC: JSON comments and trailing commas · Vote postponed · The pull request discussion
Two votes closed this week, and one opened. Sjoerd Langkemper's RFC passed 27 to 1, so in PHP 8.7, hexdec, octdec, bindec and base_convert throw on invalid characters instead of quietly ignoring them. Muhammed Arshid's Filter enums were declined, 2 to 16, with 2 abstentions. And Weilin Du's vote to let ini_get_all accept extension names in any case is at 12 to 0, with 2 abstentions. It closes November 16th.
Links: Throw exception for invalid characters in number base functions (accepted 27–1) · Add Filter\Filter and Filter\Flag Enums (declined 2–16–2) · Case-insensitive extension names in ini_get_all() (open until Nov 16)
Quick hits. Ignace Nyamagana Butera likes the direction of the CSV extension, but says major points are unsolved: the top-level namespace, the lax and strict naming, and leaning on RFC 4180, which isn't an accepted standard. He suggests calling it Character Separated Value.
**Links:** [CSV extension RFC](https://wiki.php.net/rfc/csv_extension) · [Ignace Nyamagana Butera](https://news-web.php.net/php.internals/132957)
For binary parameters in PDO, Ilia Alshanetsky and Calvin Buckley now lean toward a flag instead of a new type, which would break drivers until each one updates.
Links: PDO binary parameters RFC · Ilia Alshanetsky · Calvin Buckley
Sjoerd's openssl_get_channel_binding now throws an exception instead of returning null, answering Tim's question from last week.
Links: openssl_get_channel_binding() RFC · Sjoerd Langkemper's update
Xavier Leune wants SSRF protection in curl first, so it lands in PHP 8.7, and maybe streams in a later RFC.
Links: Xavier Leune on SSRF protection in curl · php-src #23657
Chris Abner Herrera Navarro went from a karma request to an RFC for grapheme_str_pad in under a week. It pads text by the characters you actually see, and Tim said it has exactly what he expected.
Links: grapheme_str_pad() RFC · php-src #25179 On Máté Kocsis's query parameters RFC, Tim Düsterhus checked with the security team, and says a long query string can't make memory grow faster than the input, and the parameter count is already capped, so a separate length limit isn't needed.
Links: Tim Düsterhus on the query string length limit · Query parameters RFC
Artem Ukrainskiy got karma for an RFC on multi-pattern search and replace, with Str\Matcher and Str\Replacer classes.
Links: Str\Matcher and Str\Replacer (draft) · php-src #25122 And Andrey Andreev listed five oddities in the password hashing functions, with a plan to fix them over the next few months.
Links: Password hashing API
So that's the week. Kévin Dunglas proposed hooks so PHP knows when a request ends in worker mode, and showed how they'd work with Scope. There are now two bcrypt fixes side by side, and Sjoerd says one is enough. Larry pushed back on Scope's empty superglobals, Tim kept the core of Time\Instant, and the JSON comments vote is on hold. Number base exceptions passed, and Filter enums didn't. Links below.
The PHP Foundation funds more than half of ongoing php-src commits, so if you use the language, maybe consider donating at opencollective.com/phpfoundation — or try guilting your employer into it.
If you found this useful, a like or a comment helps more people find it. And if you missed last week's episode, where a call to freeze PHP until it has a formal spec got an answer about money, that's a good one to watch next. Thanks again to Scalpels.app for supporting this week's episode. We're Artisan Build. See you next week.