r/PHP

I tried FrankenPHP on our (externally heavy I/O) workload and it wasn't worth it
▲ 40 r/PHP

I tried FrankenPHP on our (externally heavy I/O) workload and it wasn't worth it

I spent a few hours playing around with FrankenPHP on a fleet of servers that run as a microservice, their sole purpose was getting a request in, making external calls to 3rd party APIs/websites, and returning the result (the kind of microservice that should really be rewritten in something like Golang, but that's for later).

I wanted to test if we could speed things up by moving from PHP-FPM to FrankenPHP, but after intensive testing, it isn't worth it for us. I could've predicted this, but wanted to test it anyway :D

tl;dr: if you only spend < 1% of the PHP lifecycle bootstrapping the framework, and the other 99% waiting on external I/O (whether that's database/cache/api), you're not going to see the gains.

More details are up on my blog: https://ma.ttias.be/laravel-octane-vs-php-fpm-lessons-learned/

u/Mojah — 17 hours ago
▲ 1 r/PHP+2 crossposts

I got tired of copy-pasting the same PHPStan/Pint/Pest setup into every Laravel repo — and my AI agent kept ignoring the rules anyway. So I fixed both.

The problem

Every time I spun up a Laravel project, I'd do the same dance: copy phpstan.neon from the last repo, copy pint.json, wire up Pest, set up a pre-commit hook, write a CLAUDE.md full of engineering rules… and then drift would set in. Repo A was on PHPStan level 5, repo B on 7. One had architecture tests, the others didn't. The "rules" lived in my head.

Then I started leaning on AI agents (Claude Code, Cursor, Copilot) and a new problem showed up: the agent would happily ignore the conventions. It'd write untyped code, skip tests, invent patterns the rest of the repo didn't use. A CLAUDE.md full of rules helps, but rules a model can choose to skip aren't guardrails — they're suggestions.

What I wanted

  • One command to set up a fresh Laravel repo with the same guardrails every time.
  • A quality gate the agent literally cannot skip — not just docs it might read.
  • The same gate running everywhere: at commit time, at the end of every AI turn, and in CI. If it passes locally it passes in CI, no surprises.

What I ended up building

A small Composer dev package. You run:

composer require --dev mohamed-ashraf-elsaed/claude-kit
php artisan claude-kit:install

It detects your frontend stack (Inertia+Vue, Inertia+React, Blade/Livewire, or API-only), asks what you actually want (PHPStan? which level? strict-rules? Pest or PHPUnit? coverage
threshold? arch tests? git hooks?), and scaffolds it — without clobbering existing files (composer.json / package.json get merged, not overwritten).

The part I care about most: the quality gate is one shell script (Pint + PHPStan level 7 + strict-rules + Pest with an 80% coverage gate + frontend lint) that backs three things — the git
pre-commit hook, Claude Code's Stop hook, and the CI workflow. So the AI agent's turn doesn't "finish" until the gate is green. Same script everywhere = no "works on my machine, red in
CI."

There's also a hybrid update model: the machinery (the gate script, the hook) is referenced from vendor/, so a composer update propagates fixes to every project. The content you own
(CLAUDE.md, linter configs, skills) is written into your repo so you can edit it freely.

It's MIT, PHP 8.2–8.4, Laravel 11/12/13. Repo + docs: github.com/mohamed-ashraf-elsaed/claude-kit

The actual question I'm curious about: for those of you using AI agents on real codebases — how are you enforcing conventions? Just prompt/CLAUDE.md, hooks like this, CI-only, or something
else? Genuinely want to hear what's working, because the "agent skips the rules" problem feels underrated.
mohamed-ashraf-elsaed.github.io
u/MissionArea990 — 9 hours ago
▲ 8 r/PHP+2 crossposts

Ran real PHP applications as TypeScript on Bun 1.3.14; migration from Node was mostly a non-event

I’ve been transpiling PHP applications to TypeScript and running the output on Bun, and I’ve now got real apps executing end to end. Sharing some notes in case they’re useful to anyone moving a Node-targeted codebase over.
Coming from Node 22, the runtime side was almost boring, most of the transpiled output just ran on Bun directly, no changes needed.

The one real snag was native code. A few C bindings (PCRE, LibXML) that worked fine on Node 22 didn’t load on Bun 1.3.14. That’s understandable: native addons are compiled against a specific runtime’s ABI/internals, so a binding built for one runtime won’t necessarily load on another. Instead of maintaining runtime-specific builds, I compiled the C bindings to WebAssembly. They’re now version-independent; no ABI coupling, so the same WASM artifact behaves the same regardless of the runtime underneath.

The thing I’m still figuring out: I’d been using Node’s cluster mode to mirror PHP-FPM’s process model (a master plus a pool of workers), and I’m still investigating how that holds up under Bun. If anyone here has run node:cluster workloads on Bun, especially anything resembling a prefork worker pool, I’d like to hear how it went and where the edges are.

reddit.com
u/Typical_Ad_6436 — 22 hours ago
▲ 0 r/PHP

I built a modern, open-source PHP code obfuscator (YakPro alternative)

About four months ago, my release pipeline suddenly broke because the official repository for YakPro Obfuscator simply vanished.

I was looking for a simple, effective way to add a layer of protection to my suite of AI-driven WordPress plugins before distributing them. I didn't want to rely on something like Ioncube because not every hosting provider supports the extension.

Since I couldn’t find a maintained alternative that reliably supported modern PHP syntax, I decided to build one.

I've been using it successfully in my own production pipelines for the last few months. It became pretty stable, so I wanted to announce it to the world.

Features

  • Full Modern PHP Support: Built on nikic/php-parser v5.x, supporting named arguments, enums, match expressions, readonly properties, intersection types, and even PHP 8.5's pipe operator and (void) cast.
  • Deobfuscation Resistance:
    • Opaque Predicates: Hard-to-analyze expressions in control flow flattening.
    • Dead Code Injection: Branches that never execute but confuse static analysis.
    • Per-file XOR Encoding: String literals are encoded using unique random keys.
  • Incremental Processing: Only process changed files since the last run.
  • Multi-pass Analysis: Scrambles symbols consistently across your entire project.
  • Clean Architecture: Modern PSR-4 OOP codebase with 100% test coverage for core components.

Ready for CI/CD, not just local machines

I included a ready-to-use GitHub Action, making it easy to integrate into your existing CI/CD pipeline. Every tagged release or production build can produce consistently obfuscated code without anyone remembering to run another command.

Add it to your release Github Action to obfuscate your code automatically:

steps:
  - uses: iserter/php-obfuscator@v0.1.7
    with:
      source: 'src'
      output: 'dist'

Available on Packagist/Composer too.

Install:

composer require iserter/php-obfuscator --dev

Use:

vendor/bin/obfuscate src/ -o dist/

Or use via Docker

If you’re a hygine freak for your host system, then here’s how you can use via Docker:

docker run --rm -v $(pwd):/app iserter/php-obfuscator src/ -o out/

See it on Github:

Repository:

github.com/iSerter/php-obfuscator

u/iSerter — 2 days ago
▲ 7 r/PHP

I built a static analysis tool that finds require_once statements your Composer autoloader already covers

Working on legacy codebases, I kept running into the same thing: hundreds of

require_once statements that predate Composer, still sitting there years after

autoloading was set up. Deleting them by hand means answering "is this class

actually autoloadable?" for every single line — so nobody does it.

So I wrote depone: https://github.com/lll-lll-lll-lll/require-once-lint

What it does:

- Tokenizes every PHP file and statically evaluates each require_once path

(concatenation, __DIR__, dirname(), define()'d constants)

- Checks the resolved target against your composer.json autoload config

(psr-4, psr-0, classmap, files, autoload-dev)

- Reports what's redundant — and just as importantly, reports what it

*couldn't* resolve and why, so nothing is silently skipped

- `--trace` shows reverse require-paths from entrypoints, for a final sanity

check before you delete anything

PHP 8.4+, MIT licensed, installable via Composer. It deliberately does one

thing only. Would love feedback, especially weird require_once patterns from

real legacy projects that break the evaluator.

▲ 0 r/PHP+1 crossposts

I built a Laravel-inspired framework for structuring WordPress plugins: Nikogin (open source)

Hey everyone,

Quick note before I start: English isn't my first language, so I used AI to help clean up this post.

I've been building WordPress plugins for a while and got tired of the usual mess: giant files full of add_action() calls, no real structure, and no clean way to separate business logic from WordPress's hooks and globals. So I built Nikogin, a small, Laravel-inspired framework for writing structured, maintainable WordPress plugins.

I actually developed it in a private repo over the last 1-2 years, testing it with a few colleagues on real projects along the way. They found it really useful, which is what convinced me to clean it up and release it publicly.

What it gives you:

  • A service container for dependency injection
  • A repository pattern for custom tables, post types, and taxonomies
  • PHP 8 attributes instead of add_action()/add_filter(), with hooks auto-discovered (#[AsListener(...)])
  • Controllers for REST endpoints and admin pages
  • Vite + TypeScript + SCSS for frontend assets, with a manifest-based loader
  • A CLI (php nikogin make:*) to scaffold repositories, listeners, controllers, migrations, and more

It's still early days for the public version, so I'd love feedback, bug reports, or just opinions on the approach, especially from anyone who's tried to bring more structure into WordPress plugin development before.

MIT licensed. Happy to answer any questions in the comments.

reddit.com
u/PhPWellphant — 2 days ago
▲ 34 r/PHP

AI insulted my ancient PHP code 😁

A couple of years ago I posted about an old website that I coded in the early-mid 2000s and wanted to start up again via some ancient unmaintained Docker containers. The main conclusion of that thread was that it might be possible to resurrect it with some work, but that it would be better to just (hu)man up and update the code so it would run in modern PHP versions.

Well, a couple of days of vacation and a whole bunch of free Github Copilot tokens later, it's up and running on the home network under PHP 8.2 and MySQL 8.0. According to Copilot, this was the furthest it could go with minor changes.

When I asked Copilot about how my code looked, it gave me the following opinion

>The main gap is not PHP 8.2 compatibility anymore; it is that the app is still built like a 2000s-era script. [...] In short: the app needs a structural refactor, not just syntax updates.

Darn AI, no one asked for your honest opinion! /s

u/peperazzi74 — 3 days ago
▲ 34 r/PHP

I set out to argue against deprecating PHP's metaphone(). Then I read the RFC.

A while back an RFC landed on internals to deprecate metaphone(). My first reaction was the reflex of an old release master: leave the legacy string functions alone, people depend on them, deprecation churn is its own tax. I was ready to argue against it.

Then I read the reasoning and looked at what phonetic name matching is supposed to do in 2026, and I changed my mind. metaphone() is the original 1990 algorithm: English-only, single-key, tuned for one accent of one language. It was superseded by Double Metaphone in 2000, which emits a primary and an alternate key. Core shipped the first version and never moved. soundex() is older still, a 1918 patent. For the one job these functions exist to do, collapsing names that sound alike but are spelled differently, they are the weakest tools in the drawer. The deprecation is defensible.

Where I part ways with the RFC is the replacement. It points at userland Composer libraries. But phonetic encoding is a hot inner-loop operation, run over every name in a dataset to build a match index. A pure-PHP implementation pays interpreter overhead on every character. The honest replacement for a native C string function is another native C string function. That performance gap is the whole reason this code lived in core to begin with.

So I built the thing the RFC should have recommended: iliaal/phonetic, a native extension with the five encoders core never had, plus a comparison helper per algorithm.

double_metaphone_match("Catherine", "Kathryn");   // 2  (primary keys agree)
dm_soundex_match("Moskowitz", "Moskovitz");        // true  (one surname, two transliterations)
bmpm("Garcia", BMPM_SEPHARDIC, BMPM_EXACT);        // "garsia|gartSa"

Double Metaphone is the fast general default. Beider-Morse (BMPM) is language-aware and matches across transliterations and scripts. Daitch-Mokotoff is the genealogy standard for Eastern-European and Ashkenazi surnames. NYSIIS and Match Rating are cheap English second keys. The helpers matter because each encoder answers "do these sound alike" differently (two keys, a code set, or a threshold), which is exactly where userland code quietly gets it wrong.

BMPM is slow, roughly 60x a Double Metaphone call, so you pick it for recall, not throughput. These are heuristic, culture-bound, Latin-script encoders, not a universal global-name solver.

One aside r/PHP might appreciate: the BMPM rule data is a licensing trap. The canonical PHP reference and the abydos Python port are both GPL-3.0 because of that data. I vendored the identical tables from Apache Commons Codec under Apache-2.0 to keep the extension BSD/Apache-clean.

pie install iliaal/phonetic

Repo: https://github.com/iliaal/phonetic

Happy to answer questions, especially from anyone doing record linkage or dedup across messy person data.

reddit.com
u/Ilia0001 — 3 days ago
▲ 28 r/PHP

what php package do you install in literally every project

for me it's carbon. i know datetime exists but every time i try to use it raw i end up wasting 30 minutes on some timezone edge case. carbon just handles it without me having to think.

second one would be laravel debugbar even on non-laravel projects i find myself missing it.

what's yours?

reddit.com
u/Capedcrusader1923 — 4 days ago
▲ 10 r/PHP+1 crossposts

LLM Provider Fallback in PHP: Automatic Failover in Neuron AI Router

I just released an update to the Router component of Neuron AI. For anyone who hasn't come across it, Neuron is the agentic framework of the PHP ecosystem. It allows you to develop agentic applications, from simple agent with tool calls to complex agentic workflow in pure PHP.

The router component allows you to define a fallback order of providers and if one fails with a transient error, the next is tried transparently. On top of that, you can add routing rules to choose which provider handles each request.

Neuron Router Release: https://inspector.dev/llm-provider-fallback-in-php-automatic-failover-in-neuron-ai-router/

Repository: https://github.com/neuron-core/router

Docs: https://docs.neuron-ai.dev

u/valerione — 3 days ago
▲ 0 r/PHP

How should I speed up the development time further ?

I have already built a enterprise system with capabilities mentioned below. Please read the complete details and suggest me what can I do to speed up the development ? How can I use AI further to create more deterministic codes

[ Do not ask for github link. This is not an open source project. This is my internal system ]

PrestoFox is a business application System with a collection of components needed to build applications of any complexity. These components work together in PrestoFox to create a strong foundation for the application that gets built on top of it. It is based on modular monolithic MVC architecture both in backend and frontend.

It is built with MIT-licensed technology like Symfony (PHP) at the backend and Quasar (Vue Js) at the front end. It uses a PostgreSQL database and makes use of the latest

GraphQL technology to build APIs. Companies can build applications in many flavors

  • SPAs (Single Page App)
  • SSR (Server-side Rendered App) (+ optional PWA client takeover)
  • PWAs (Progressive Web App)
  • BEX (Browser Extension)
  • Mobile Apps (Android, iOS, ...) through Cordova or Capacitor
  • Multi-platform Desktop Apps (using Electron)

It has code generator whose core purpose to create complete application modules end to end. It does the following

  • Generate Bundle / Module
  • Generate Doctrine entity
  • Add Validations when the property is not null
  • Add picklist based relationship using wizard
  • Add relationship like OneToOne , OneToMany , ManyToOne, ManyToMany using wizard
  • Generate Database migration
  • Execute Migration
  • Generate GraphQL schema
  • Generate GraphQL OutputType
  • Generate GraphQL InputType
  • Generate Permission
  • Generate Frontend configuration
  • Generate Admin UI components

It has crud capabilities like

  • Full CRUD (list, create, read, update, delete)
  • Single Record Update Only (update form only)
  • Single Record View and Update Only (read, update)
  • Multiple Record Update Only (list, read, update)
  • View Only (list and show only)

It also has separate generator for

  • Command
  • Job Scheduling
  • Data Populator
  • Notification
  • Picklist

System itself have the following implemented

  • Authentication and Authorization module
  • Attachment module
  • Queue System , Cron Job , Job Scheduling and Job Logs
  • Data History , Data Revision , Data Revert , Data Validation
  • Error Logging
  • HealthChecker module
  • Tax module
  • Export module
  • Locale Data module
  • Notification module with all channels like Email, SMS, WhatsApp, etc
  • Multitenancy
  • Document Sequencing Bundle
  • Invoice module
  • Comment module
  • Address module
  • Employee module
  • Custom Field module
  • Entity Link module
  • Real Time update module based on mercure
  • User Meta Data module
  • User and Organization Preferences
  • Security Related implementations and API Call limiting

Along with it, It has entity field collection can be added to Doctrine entity file via implements and Use ___Trait approach. These are the list of all the entity collections

  • Address Aware
  • Currency Aware
  • Date Aware
  • Single Email Aware
  • Multiple Email Aware
  • Entity Aware
  • Lat Long Aware
  • Meta Data Aware
  • Organization Aware
  • Owner Aware
  • Space Aware
  • Person Aware
  • Single Phone Number Aware
  • Multiple Phone Number Aware
  • Team Aware
  • User Aware
  • User Meta Data Aware
  • Custom Field Aware
  • Tax Aware
  • Sub Total Aware

I also has events built in for each Entity. The events are

  • onPreRemove
  • onPostRemove
  • onPreCreate
  • onPostCreate
  • onPreUpdate
  • onPostUpdate

On the frontend side , It has following implementation

  • UI controls mapped for each datatype of the entity .
  • Well designed minimalist Admin UI.
  • Flexible data grids and filters system.
  • Role based menu management
  • UI for desktop and mobile menu
  • Reusable Context menu Component
  • Reusable Toolbar Component
  • Controls for Single , Multiple Phone Numbers
  • Advance dropdown menu where one can 1) Search records 2) See all records in grid 3) Create new record on fly
  • Translation system
  • Multiple screen layouts like Main,Header Only,Full Screen, Empty , Auth etc
  • Vue Mixins for standard functionality around like 1) CRUD 2) Grid 3) Layout 4) Forms
  • Common Services like Config, Data Encrypt and Decrypt , Display Formats , Notifications , Page loading , permission, utility etc
reddit.com
u/sachingkk — 4 days ago
▲ 5 r/PHP+6 crossposts

Claude Code made me code fast, but announcements still take ages, so I built Shipnote to save myself hours.

Shipnote - turn every commit into an announcement

Connect your repository (read-only, always), and for every release it reads your commits and pull requests, separates customer-facing changes from internal plumbing, and drafts everything for you: changelogs, release notes, plain-English summaries, Discord posts, emails, X posts, blog posts, whatever you need. Everything is editable, you choose where it gets published, and it can even generate drafts automatically on every push.

Full disclosure: I'm the solo developer, so yes, this is a shameless self-plug. But it genuinely came from my own AI coding workflow, and I'd rather get honest feedback than a pile of empty upvotes.

There's a free trial with no credit card required if you want to point it at a real repository and see what it produces. And if the output is rubbish for your project, I genuinely want to know.

https://reddit.com/link/1um10bl/video/ft10zhti6xah1/player

reddit.com
u/titleRivals — 3 days ago
▲ 1 r/PHP+1 crossposts

Dicas para evoluir com PHP

Tenho 3 anos de experiência trabalhando com PHP (7.4-8.3) e Symfony (4.4-7.4)
Estou buscando cursos online para conseguir evoluir como desenvolvedor e tentar atingir o nivel de desenvolvedor Pleno.
Estou buscando cursos de:
Clean Architecture
DDD
Refactoring
Clean Code

Língua dos cursos: Inglês, Português, Espanhol, Francês

reddit.com
u/Sharkal036 — 3 days ago
▲ 96 r/PHP+1 crossposts

You probably don't need a database per tenant. A detailed explainer on multitenancy data isolation across Postgres, MySQL and SQLite

I've been doing Laravel multitenancy for years and kept seeing teams reach straight for a database per tenant, mostly because it's hat most of the popular packages offer by default. So I wrote up the whole spectrum of data isolation approaches. Separate instance, separate database, schemas/prefixes, partitioning, and discriminator column, along with how each behaves across PostgreSQL, MySQL/MariaDB and SQLite, and where row-level security changes the picture.

The short version: for most apps a discriminator column with RLS on PostgreSQL gets you real, database-enforced isolation at the lowest operational cost, and database-per-tenant is the most expensive answer to a question most apps can answer cheaply.

As with everything when it comes to development, the answer is often "it depends", so I'd love to hear from anyone about their experiences with this, whether good or bad.

ollieread.com
u/ollieread — 5 days ago
▲ 3 r/PHP

Can someone explain why this Laravel change was rejected?

I opened a small PR to simplify some logic in Laravel:

https://github.com/laravel/framework/pull/60632

The PR was closed, and I'd like to learn from it. What issues do you see with this change? Is there a hidden edge case, performance concern, or coding standard that I missed?

u/Reasonable-Pass9841 — 5 days ago
▲ 33 r/PHP

There are now over 60 free and open source NativePHP Mobile Plugins 🔥

Since NativePHP Mobile went free and open source back in February, the community have been busy building all sorts of plugins.

Which is really exciting! It shows demand and growth of the tool. And with what we're about to release next month, it's looking set to get even more exciting.

Thanks to every one of you who is building and sharing your work freely with the world 🙏

With some of the funds we're able to raise through our premium offerings, we're going to be sponsoring maintainers of open source NativePHP Plugins - so if you build one, make sure you're set up for sponsorship with GitHub sponsors, OpenCollective, or in some other way so we know how to support you 🙌🏼

https://packagist.org/search/?type=nativephp-plugin

edit: Added 'Mobile' qualifier in the intro. Desktop has always been free and open source

reddit.com
u/nativephp_official — 5 days ago
▲ 11 r/PHP+1 crossposts

plPHP v2.0 released

PL/php is a procedural-language handler that lets you write database functions in PHP, stored and executed inside PostgreSQL. You get the convenience of PHP's standard library with the full power of a native PostgreSQL function — plain functions, set-returning functions, triggers, event triggers, and procedures with transaction control.

github.com
u/linuxhiker — 4 days ago
▲ 24 r/PHP+2 crossposts

This Week in PHP Internals | July 1, 2026

Hello world, it's Canada Day 2026, and here's what happened This Week in PHP Internals.

This week's episode is supported by OurCVEs. Hundreds of CVEs ship every week, and almost none of them are about you — until one is. OurCVEs inventories your entire infrastructure and only surfaces the security risks that actually apply to your team. Free for open source at ourcves.com.

This week's top story is still Gina P. Banyard's Deprecations for PHP 8.6 — the once-a-year housekeeping RFC that gathers a stack of unrelated removals under one roof, where each one stands or falls on its own separate vote. With 8.6's first alpha getting built this very week, the clock is loud. But the fight this week wasn't about any single removal — it was about evidence: does every deprecation owe voters an impact analysis? Tim Düsterhus argued no, at least not as an unconditional rule — a deprecation has years of runway, and a survey of existing code can't measure the upside of a cleanup.

Juliette Reinders Folmer had offered to build those impact analyses, and her exchange with Tim turned personal — an accusation of gaslighting, which Tim rejected. But she was far from alone on the substance. Rowan Tommins came to her defense, and he didn't warm up first. Rowan wrote: "I hate this argument." His point: deprecating something is really a proposal to remove it later, so the impact of that removal is exactly what voters deserve to see — and to leave it out to protect your case, he said, would be dishonest. Larry Garfield backed the same call: put the impact on the table, or read the angry blog posts come December.

And this particular RFC kept growing right up against the freeze. Seifeddine Gmati proposed deprecating list(), and got the room's attention: Ayesh Karunaratne clocked "7 million hits" for list( on GitHub and called the break too big; Rowan noted you can't really retire list() while array() stays — to which Seif said, fine, then maybe deprecate array() too. Nick moved to reserve namespace as a constant name. Ilia Alshanetsky floated finally sunsetting open_basedir — Derick Rethans said "Yes, but for PHP 9," while Jakub Zelenka was firmly against it. Add a careful fight over how narrowly to kill the dechunk filter, plus Tim's pitch to retire gettype() for get_debug_type(), and that's a lot of small knives being sharpened at once.

Carrying over: Tim and Derick's Time\Duration class — the immutable stopwatch value that's meant to be the first brick of a modern date-and-time API. One line of context, then this week's moves: they're dropping the word "period" from the method names (Derick pointed out ISO itself walked away from it); they're lifting the ban on negative durations after Paweł Kraśnicki showed up with a real use case; and the whole thing may slim down for 8.6 — ship addition, subtraction and multiplication now, and let dividing one duration by another wait. Ignace Nyamagana Butera and Nick are already bikeshedding whether that division should hand you back a tuple or a tidy little value object.

The single busiest thread of the week was brand new: Rob Landers formally opened Primary Constructors — the whole constructor hoisted up onto the class line itself. His numbers are the pitch: something like 30 to 40 percent of all constructors are completely empty, and by his count 71 percent of Laravel and 61 percent of Symfony classes could use this. The catch is deliberate — a class with a primary constructor can't also declare a regular __construct, and it can't carry a body.

That no-body rule is the whole fight. Nick pushed for a body — mostly so readonly classes have a way through — and warned this could ship as yet another half-a-feature. But Rob held the line. He wrote: "If you need a body, use a constructor -- that's what they're for." Rowan and Tim are with Rob — Tim called the limitation "a feature," and put it simply: "[To] reduce typing alone is not a sufficiently strong argument in favor of a new feature." Seifeddine went further: "Personally, I really dislike this feature." His read — primary constructors don't remove repetition, they just relocate it. Larry, meanwhile, wants a Kotlin-style init block bolted on. Plenty's still unresolved — visibility, attributes, anonymous classes — so we'll be keeping an eye on this one.

Newcomer Michal Kral floated a spicy pre-RFC: methods on scalars. Write (3)-&gt;pow(2), or " hello "-&gt;trim()-&gt;upper(), with the call rewritten at compile time into a hidden helper — but only when the compiler can already prove the value is a scalar. He was upfront that he built it with an AI assistant. The reception was cool: Seifeddine's core objection is that PHP compiles one file at a time, so "the compiler already knows it's a scalar" covers almost no real code — the same line would work or fail depending on what's autoloaded. His fix, nicely put: "Solve the naming problem with naming, not by blinding the tooling." And Rob warned the whole thing leans on casts, which in PHP are dangerous(int) of the string "123password" is just 123.

Alex Pătrănescu opened a pre-RFC for runtime modules — a way to give each package its own private symbol table so two versions of the same library can finally coexist in one request. Rowan Tommins spent the week reframing it as containers — a boundary you run other people's code inside, Docker-style — and kept circling the genuinely hard part: what happens when an object crosses the wall between two containers? A second contributor, Alexander Egorov, pitched version "tags" instead; Rowan's worry is that tags leak the container's insides right back out. No code yet — but a problem the whole ecosystem feels.

The rawest thread of the week came from a user, not an internals regular. Michael Morris wrote what he openly called a "Disheartening Rant" after Edmond Dantes said his own TrueAsync RFC has, in his words, a 90 percent chance of not being accepted. Morris didn't hold back: "If you want PHP to be the next COBOL, this is how you go about it." His killer example is WebSockets — the thing PHP still can't really do on its own. Ilia Alshanetsky pushed back gently (he's actually written COBOL, and reports it isn't dead): async matters, but its reach in a request-based language is narrower than it looks.

Larry Garfield drew the line that matters — the same one that just sank generics. Larry wrote: "Don't confuse 'this is not the async we're looking for, done in a painful process' with 'we don't want async at all, ever.' The first is what happened. The second is simply untrue." His critique isn't of async — it's of how TrueAsync arrived: enormous, one author, hundreds of far-reaching decisions, feedback taken selectively. His fix is a real working group to design it together. Nobody's against the destination — they're against the map.

To the ballots — and this was a brutal fortnight for the marquee names. Seifeddine's Bound-Erased Generics closed and was declined: 7 in favour, 19 against, 10 abstaining — nowhere near the two-thirds. And Nicolas Grekas's __exists() magic method went down with it, 2 to 13. The two features that would've made the headlines both missed.

But look what did pass — the quiet, careful stuff, every one without a single No. Tim's deprecation of returning values from __construct and __destruct: accepted unanimously, 39 to nothing. Sjoerd Langkemper's cap on php://filter chains — that local-file-inclusion-to-RCE fix — accepted 30 to nothing, 2 abstaining. And Weilin Du's Locale display-keyword additions: in, 18 to nothing, 2 abstaining. And one's still live on the board — Jordi Kroon's move to lift third-party extension docs out of the manual is out in front at 25 to 1, one abstaining, with the vote closing July 3; on the side questions the room is leaning toward a php.net subpath over a separate subdomain, 14 to 8, and toward dropping the old user notes rather than migrating them, 20 to 4. The pattern's getting hard to miss: the bold swings keep getting sent back, while the tidy, well-scoped changes keep sailing home.

Quick hits. That newcomer arc — Sepehr Mahmoudi's grapheme_mask — got RFC karma and a warm discussion, then tried to call a vote for July 3 and was told, kindly, it's not ready: no discussion link, freeze period not met, window too short — the process catching a first-timer before the fall. Khaled Alam's write-to-constants RFC grew to cover class constants, so its vote slipped. Gianfrancesco Aurecchia's DTLS idea converged fast — Jakub steered it from a new class to a simple dtls:// stream, maybe no RFC needed at all. Gina volunteered to review the long-stranded snmp extension work. Seifeddine's Literal Scalar Types reached version 0.3. Jorg Sowa's case-sensitive-PHP revival was withdrawn. And PHP 8.6.0 alpha 1 got built — which is why everyone's sprinting.

So that's the week: a deprecations RFC that turned into a fight about honesty, a stopwatch class quietly going on a diet, a brand-new primary-constructors debate, a heartfelt plea for async — and the two flagship votes falling while the small ones walked right in. Links to every thread are below. We're Artisan Build. See you next week.

youtube.com
u/ProjektGopher — 5 days ago