Agents Did Not Kill the “Our Team Knows PHP” Argument
There is a particular kind of blog post that starts with a confident thesis and ends with a shrug. The thesis is usually “X is dead”.The shrug is usually “but I would still pick it for this one thing.”
The post making the rounds today follows that pattern beautifully. Its core claim: agents have erased the “our team already knows PHP” advantage, so we should choose languages on raw economics. Faster throughput, lower memory, fewer servers, and Rust wins on the spreadsheet.
I have been reading it with a pen in one hand and a cup of coffee in the other, and I have some objections. Let’s go through them in order, with code, because a language argument without code is just a feeling with a logo.
Syntax Was Never the Hard Part
The argument goes like this: agents write boilerplate in any language, so the learning curve no longer matters. Fair enough. Agents do write boilerplate in any language, and they are very good at it.
But the hard part of a language was never the boilerplate. The hard part is the stuff that bites you at 3 a.m. Take a classic that PHP has been quietly fixing for years. In PHP 7, loose comparison between a number and a non-numeric string was a trap:
<?php
var_dump(0 == "a"); // PHP 7: true, PHP 8: false
var_dump("1" == "01"); // true, still
var_dump(100 == "1e2"); // true, still, and nobody is happy about it
An agent can write == in its sleep. It cannot tell you which of your twelve legacy controllers relies on the PHP 7 behavior, and it cannot know that your ops team has a runbook for the day that bug shows up in production. That knowledge lives in people, in incident reports, and in the one senior developer who remembers why the if is there.
Agents erase the typing. They do not erase the knowing. And the same argument applies to Rust, Go, and Java: the team’s knowledge of its own stack is the asset, and it is not transferable by prompt.
Six Times Faster, on One App, Measured by One Author
The benchmark says 6 to 9 times more requests per second, and 12 times less idle memory. I believe the numbers. I am less convinced they describe your application.
It is one API workload, on two cores, measured by the author who wrote the post. That is a fine experiment. It is not a verdict. A traffic pattern that spikes at lunchtime and sleeps at night behaves differently from a flat synthetic load, and “idle memory” is only interesting if your servers are actually idle.
PHP’s model is also widely misunderstood. The classic setup is PHP-FPM with OPcache, and since PHP 7.4 you can add preloading so the framework’s core classes are compiled once and sit in shared memory:
; php.ini opcache.enable=1 opcache.preload=/var/www/app/preload.php opcache.preload_user=www-data
<?php
// preload.php
foreach (glob('/var/www/app/src/Core/*.php') as $file) {
opcache_compile_file($file);
}
With that in place, a fluctuating request volume is handled by a pool of workers that come and go. Not glamorous, but it works, and it has been running large sites for years.
And then there is “shared-nothing”,which the post treats as a 2009 shared-hosting relic. It is actually a robustness feature. Each request starts with a clean slate: globals, objects, and request state vanish at the end. Nothing leaks from one visitor to the next, and horizontal scaling becomes boring, which is the highest compliment an ops person can pay.
Boring has one exception, and I will admit it is my favorite trap in the whole language. Static variables persist for the lifetime of the worker process, not the request:
<?php
function hit() {
static $n = 0;
return ++$n;
}
echo hit(), hit(), hit(), "\n"; // 1 2 3
Under FPM, that counter keeps counting across visitors as long as the same worker process serves them. So “shared-nothing” is true, with a footnote. Every good architecture has footnotes. The trick is knowing where they are.
Shipping Is a Feature Too
The post concedes that Laravel “still wins on the only axis that matters, which is whether it ships”.Then it prices that advantage at zero.
I am going to be a little rude here. For most new applications, the cost that matters is not the RAM on a $10 VPS. It is:
- how many days until the first login page works;
- how many packages already solve the problem you are about to solve;
- how many answers exist for the error message you are staring at;
- how many people can be hired next month to maintain it.
Those numbers are not in the benchmark, and they dominate the lifetime cost of most apps. Idle memory is a line item. Being able to ship on a Tuesday is the whole budget.
The Plugin Problem Is Not a PHP Problem
The security section focuses on remote code execution in WordPress plugins. Those vulnerabilities are real, and they are ugly. But they live in unaudited third-party code, and that is the story of every ecosystem with a long tail of packages. npm has its incidents. Crates have their incidents. The thing that changes is the size of the tail, not the language.
PHP core, meanwhile, shipped an emergency release when a serious bug turned up. That proves the patching process works, which is not the same as proving the platform is uniquely unsafe. Nobody writes a blog post about the day a Rust crate got a same-day fix, but it happens too.
If you want to know what is actually inside a plugin, stop reading the marketing and start reading the code. A tokenizer is a good first step. Here is a small audit script that walks a directory and lists calls to the usual suspects:
<?php
// audit.php: list the risky calls in a plugin tree
$risky = ['eval', 'exec', 'system', 'shell_exec', 'passthru', 'unserialize'];
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($argv[1]));
foreach ($files as $file) {
if ($file->getExtension() !== 'php') {
continue;
}
$tokens = token_get_all(file_get_contents($file->getPathname()));
foreach ($tokens as $token) {
if (is_array($token) && in_array($token[0], [T_STRING, T_EVAL], true) && in_array($token[1], $risky, true)) {
echo "{$file->getPathname()}:{$token[2]} {$token[1]}()\n";
}
}
}
Run it on a sample tree with a bad.php containing eval($_GET['x']) and unserialize($_POST['data']), and you get:
plugin/bad.php:2 eval()
plugin/bad.php:3 unserialize()
plugin/ok.php:2 exec()
Notice the third line. That exec() is $db->exec(), a perfectly innocent PDO method, and the tokenizer flagged it anyway, because a tokenizer has never heard of your objects. Welcome to static analysis. The tool finds the candidates, and a human decides which ones are guilty. It is still far better than trusting a README that says “secure by design.”
There, I rebuild an entire static code analysis with very rough checks that will spot 90% of the vulnerabilities.
Where the Post Agrees With Itself
My favorite part of the post is its own honesty. The author admits that rewrites are usually mistakes, that Rust hiring is thinner and more expensive, and that models are better at PHP than at Rust, simply because there is more PHP in the training data.
Think a moment about the consequences of those three admissions, and you can draw the actual conclusion. If the strongest objection to PHP is “agents are better at it”, then the rational pick for a new app is closer to “PHP is fine” than to the headline. The post’s own “what I would actually pick” section mostly agrees: the PHP-adjacent choices are fine. The disagreement is about a narrow case, and the title is about everything.
So what is the post really about? I think it is an argument for Rust in throughput-constrained systems, a place where the benchmark matters and the team is ready to invest. That is a respectable position. It is just not an argument against PHP for the blog, the admin panel, the marketing site, or the internal tool that the company needs next quarter.
The Bigger Picture
Agents changed how fast we type. They did not change what a production system is: a pile of decisions, histories, and failure modes, owned by people who have to answer the pager.
So here is the question I would put back to anyone writing “X is dead” this year. When the agent writes the code in whatever language you pick, who will know, six months from now, why the static counter is there, which PHP 7 comparison the old controller depends on, and what the audit flagged on the second line of bad.php?
If the answer is “the team”,then the team’s language is still an asset. And it is a little early to bury the one that already runs a large part of the web.

