Hello world, it’s Wednesday, September 2, 2026, and here’s what happened This Week in PHP Internals.
11 stories this week, so let’s get into it. But first, Is AI working for your team? You can measure the code it produces, but the number that matters is how much of it survives. Ballast reads your git history — never your code — and gives you stable velocity plus a durability score between 300 and 850. Updated monthly, and it’s free. ballast.now.
This week’s top story starts with a rule most of us never read. Luca Rodenhäuser opened Wednesday with the line in the scanner that defines a PHP identifier — in bytes, not characters. Every byte at or above hex 80 is accepted, so $x followed by a no-break space is a second variable that looks identical. He proposed a per-file declare to pin that down, then scanned the 250 most-installed Packagist packages and found exactly one identifier that would break.
Larry Garfield suggested skipping the opt-in and having PHP 9 enforce it, since Symfony fixes its one class and 99.99% of developers never notice. So Luca reran it against the top 5,000 packages. Half a million files turned up fourteen hundred forty-seven non-ASCII identifiers, 91% of them in math-php, where the variable names spell the formula. He reported the result himself, writing: “So the honest answer to ‘99.99 % won’t notice’ is that one library would notice 888 times, and its author chose that style deliberately and has shipped it for years.” Then came 3 questions. Derick Rethans asked whether 13.7 kilobytes of tables in every PHP process is worth it. Juliette Reinders Folmer asked what it does to variable variables. And Rowan Tommins asked how much of this is rejecting names and how much normalising them. Each sent him back to measure, and he split his proposal into 3: a diagnostic, a well-formedness rule, and a conformance rule. He says he owes the thread a problem statement.
Nick Sdot opened an RFC on Thursday to end PHP’s endorsement of PEAR. He started about three months ago, going back through every previous discussion and every unvoted attempt, and he’s already built a static mirror so the command-line tool keeps working. His case is that PEAR is partly broken, spammed, barely active and now unmaintained. Rowan Tommins backed it. On the argument that PEAR deserves more time to be revived, he wrote: “If that’s not long enough, how long is? If the site stays alive in its current state for 10 years, it will continue to be exploited by spammers and probably worse. That’s not in anyone’s interest.” Nick then put a number on the whole thing. 6 packages are still publishing to PEAR. 3 of them are PEAR’s own infrastructure. 2 more were recently marked unmaintained. Which leaves exactly one independent package still being maintained, and that’s Net_SMTP. Nick gave its maintainer a one-word aside in the thread, and the word was legend.
Sjoerd Langkemper told the list on Friday he intends to open a vote on making the number-base functions throw. His RFC makes octdec, hexdec, bindec and base_convert throw a ValueError on invalid input. His framing was that this isn’t controversial — the list agreed to it in an earlier base_convert proposal — and that the RFC is mostly procedure. Tim Düsterhus disagreed on the substance. He argued that passing untrusted input to these functions is an expected use case, which means developers will want to catch what comes back, and drew the line firmly: “The Error hierarchy is not intended to be caught, though. It should thus use something from the Exception hierarchy.” Sjoerd asked whether that distinction is written down anywhere. It is. Tim pointed him at the throwables section of the coding standards policy, and quoted it: “The Error hierarchy MUST NOT be used for errors that are expected to be thrown (and caught) during normal operation of a PHP program. … a parsing function that is expected to be used with untrusted input must not throw an Error if the input is malformed.” Base conversion, Tim argues, is parsing.
The array-filtering function we covered last week came back on Sunday, renamed array_str_contains and retargeted at 8.7. Sepehr Mahmoudi’s case is that filtering an array by substring is common enough to deserve C, instead of paying for a closure on every element. Seifeddine Gmati went first and went broad. He couldn’t remember ever writing that code, said the same argument would justify array_str_starts_with and a few hundred more combinations, and pointed out that nothing in the name tells you it filters. He called it redundant. Bruce Weirdan turned the performance claim around, asking whether the closure overhead itself should be fixed, since that would speed up every builtin that takes a callable. Kamil Tekiela asked what the numbers actually are, and said he’d never hit it as a bottleneck. Sepehr then walked back his own strongest claim, agreeing that a filter has to read the whole array rather than stopping at the first match. He’s promised static analysis across Packagist to back the frequency claim.
Last week’s top story was the list arguing about machine-written mail in the abstract. This week it stopped being abstract. Juris was the one who did the work, drafting the guideline text he thinks a newcomer should get. It says to write the message yourself rather than rephrase yourself with an LLM, and that there’s no requirement to have perfect English on that list — plenty of productive contributors are more fluent in C and PHP than in English. Then he demonstrated it instead of asserting it. He wrote his next 3 paragraphs in Latvian, machine-translated them, and sent both versions in the same message, arguing the imperfect translation stays closer to what he meant than anything a chatbot would phrase for him. Then Sepehr Mahmoudi acknowledged that he had been having AI write his replies. Weilin Du asked the thread to stop naming people, saying it had become a place to point fingers rather than a place for technical debate. Yuya Hamada apologised for going too hard, and it stopped there. There’s still no written policy.
Théo Attali introduced himself on Saturday with a first contribution and a small, well-argued gap. PHP’s DATE_RFC3339_EXTENDED gives you milliseconds with a numeric offset, but a lot of systems expect the same instant with a Z on the end, which is what JavaScript’s toISOString produces. He proposed a constant for it, and flagged the flaw in his own idea before anyone else could. A format string containing a literal Z can’t force the value into UTC. Andreas Heigl agreed, with unusual standing to do it — he added the extended constants. He wouldn’t add any more now, since a constant only helps people who’ve already upgraded, and pointed Théo at a userland formatter built on one line that has worked since PHP 5.3. Théo revised on the spot, proposing an instance method instead. Then Tim Düsterhus redirected it. He pointed out that PHP 8.6 ships the first piece of a new date and time API, and that the proposed Time\Instant is deliberately timezone-less — which makes a Zulu-format method an obvious thing to add there.
An offer arrived on Saturday from a name the list hadn’t seen before. Riaan de Beer has written libxml-rs, a native-Rust reimplementation of libxml2 that’s compatible at the C ABI level, and he asked whether php-src would be open to a test build against it. He says xmllint and xmlcatalog come out byte-identical against libxml2 2.15.3 across eleven hundred ten tests, and he’s careful about the ask — an experimental alternative provider, not a default. What the list answered was his opening sentence. He’d said libxml2 has been unmaintained since December 2025, and Pierre replied that the repository has had many commits since, and that a mature XML library not cutting frequent releases isn’t an abandoned one. 2 more contributors agreed. One wrote that libxml2 was only briefly unmaintained before new maintainers stepped up, and the other added that one of those maintainers helps php-src out directly. Nobody has answered the actual question yet.
The question of whether RFCs should ship a userland polyfill got 2 substantial answers this week. Nicolas Grekas answered from the Symfony side, which is the side that does the work. Every polyfillable feature ends up in the symfony/polyfill monorepo anyway, and the one that ships is often not the one in the RFC. Polyfills, he concluded, need a separate workflow. Then Tim Düsterhus answered the other argument for them, which was Larry Garfield’s suggestion that a polyfill gives you something to benchmark the C against. Tim wrote: “I believe performance should not be a factor in deciding what should be part of the stdlib and what should not: Performance is a moving target and what might be true today might no longer be true tomorrow… Once we add something to the stdlib we need to maintain it for the next 15+ years. (Broad) usefulness and good API design must be the deciding factors…” He added that PIE has made building a private extension easier than it’s ever been.
Quick hits. 3 releases landed in 3 days. Calvin Buckley put out 8.4.25, a security release, so that one’s worth doing today. Daniel Scherzer released 8.5.10, a bugfix. And Matteo Beccati has 8.6.0beta2 up for testing. Last night Nick Sdot replied to the nameof RFC to say he’d like to see it in 8.7 — and the message he was replying to was posted in May of 2023. 3 years and 3 months is a long time to keep a browser tab open. And on the named parameter lists thread, somebody answered Larry Garfield’s question from a fortnight ago about why people treat a small data structure as unworthy of being a class. The answer wasn’t performance. It’s cognitive cost — returning 2 values as an array and unpacking them at the call site is easier to hold in your head than a dedicated object, and static analysis can describe that array well enough that you don’t lose much.
So that’s the week. No RFC has been in the voting phase for 3 weeks running. Somebody scanned half a million PHP files to work out what a PHP identifier is, and came back having split his own proposal into 3. There’s an RFC to end PHP’s endorsement of PEAR, which has one maintained package left on it. There’s a real disagreement about whether base conversion counts as parsing, which decides which kind of throwable it gets. A new array function has 4 people against it and nobody for it. And the argument about who writes the mail on that list got a concrete answer. 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. Thanks again to Ballast.now for supporting this week’s episode. We’re Artisan Build. See you next week.