Every language has a gap between what is currently correct and what has been written about most. For PHP that gap is unusually wide, because the language changed enormously between 5.x and 8.x while the internet kept every tutorial written along the way.
The consequence for AI-assisted development is specific and predictable: generated PHP skews towards the idioms of the era with the most published material, which is not the era you are working in. The output looks conventional — that is precisely the problem — and it occasionally carries assumptions that stopped being safe several major versions ago.
This is not an argument against using these tools. It is an argument for knowing what to look for, which on PHP is a short and stable list.
Why PHP Specifically
Three things compound.
- The volume of old material is enormous. PHP was the default web language for over a decade, during which an immense quantity of tutorials, forum answers and blog posts were written — most of them never updated.
- The language changed more than most. Scalar types, return types, enums, readonly properties, constructor promotion, first-class callables and a dramatically stricter error model all arrived after the bulk of that material was written.
- The old code still runs. Unlike languages where a breaking release forced the ecosystem forward, a great deal of PHP 5-era code still executes today, which keeps the old patterns present in current material as well as historical.
The result is a distribution weighted towards patterns that are syntactically valid, immediately runnable, and no longer how anyone should write PHP.
The Patterns to Watch For
1. String-Built Queries
The most consequential. A decade of tutorials demonstrated database access by concatenating variables into a query string, and that pattern is deeply represented. Generated code will often use a query builder correctly and then drop into raw string interpolation for the one case the builder made awkward — a dynamic ORDER BY, a computed column list, an IN clause.
It works, it passes tests, and it is an injection vector. Review any raw query in generated code as though it were written by a stranger, because it effectively was.
2. Loose Comparison and Type Juggling
PHP 8 fixed the worst of the string-to-number comparison behaviour, but generated code still reaches for == where === belongs, particularly in authentication and authorisation checks where the consequence is highest. Old material used loose comparison freely because it usually worked.
3. Manual Escaping Instead of Framework Facilities
Hand-rolled escaping, manual header setting, bespoke sanitisation functions. These were necessary in a pre-framework era and are now a way of bypassing protections your framework provides correctly. Generated code that escapes output itself, in a codebase whose templating already escapes, is a signal that the model has reached for an older idiom.
4. Outdated Cryptography and Password Handling
Anything involving hashing, tokens or randomness deserves specific attention. Old material demonstrates approaches that were standard advice at the time and are now actively wrong. Modern PHP has correct, simple facilities for all of it; generated code does not reliably reach for them.
5. Unvalidated Superglobal Access
Reading request data directly and using it without validation, in a framework that provides a validation layer. This is the pattern that most reliably indicates the output came from pre-framework material.
6. Untyped Signatures
Not a vulnerability, but a quiet erosion. Generated code frequently omits parameter and return types, and each omission removes a check your static analysis would otherwise perform. Over a few hundred generated functions, a codebase measurably loses type coverage without any single change looking wrong.
Why It Survives Review
The failure is not that the code looks suspicious. It is that it looks ordinary.
- It is stylistically consistent. Generated code is conventional by construction, which is exactly what a reviewer scanning for anomalies is not primed to catch.
- It works. The behaviour is correct for the input the developer tested with.
- The reviewer is reviewing a diff, not a design. An injection risk in a fifteen-line method reads as fifteen ordinary lines.
- Volume outpaces attention. More code is being produced per reviewer-hour than before, and review quality degrades with quantity in the way you would expect.
The compounding risk: this is most severe on old codebases, which are exactly where teams reach for AI assistance most, because the code is unfamiliar. The model produces plausible legacy-style code that matches the surrounding legacy style, and nothing looks out of place at all.
Defending Against It
Almost all of this is machine-detectable, which means the defence should be machine-enforced rather than left to reviewer attention.
- Static analysis at a high level, blocking in CI. PHPStan or Psalm will catch missing types, unsafe assumptions and a good deal of the loose-comparison problem before a human sees it.
- A taint-analysis or security-focused ruleset, which is specifically designed to find the string-built-query pattern.
declare(strict_types=1)everywhere, so type juggling fails loudly rather than silently.- Dependency and vulnerability scanning in CI, since generated code will occasionally suggest a package that is abandoned or a version that is known-vulnerable.
- A short review checklist for generated code — raw queries, comparisons in auth paths, anything cryptographic, anything reading request data directly.
- A second reviewer for security-boundary changes, regardless of who or what wrote them.
And the structural fix, which pays back on everything else too: get the codebase current. A modern PHP version with strict types, a maintained framework and high static-analysis coverage narrows the space of plausible-but-wrong code dramatically, because most of the old idioms will not compile, will not pass analysis, or will fail obviously.
That is the same argument we make about codebase legibility in the AI-assisted Laravel review policy, and it is another reason the version question in is PHP still the right choice matters more than the language question.
The Governance Position
For a CTO or VP of Engineering who needs a defensible position on this:
- Do not ban it. Teams use these tools regardless, and a ban replaces a reviewable practice with an undisclosed one.
- Do require disclosure, so reviewers know which diffs deserve the checklist.
- Do enforce mechanically what you can. Every rule you move from a policy document to a CI job stops depending on attention.
- Do keep humans on the security boundary. Authentication, authorisation, payments, personal data.
- Do fund the modernisation. The most effective single intervention against bad generated code is a codebase where bad code cannot survive the toolchain.
This is work we do through our PHP development practice and our custom PHP modernisation, and it connects directly to the broader question of where AI genuinely earns a place in delivery — our AI integration practice and AI engineering team cover that side.
Frequently asked questions
Does AI-generated PHP contain security vulnerabilities?
It can, and the vulnerabilities are predictable rather than random: string-built queries, loose comparison in authentication paths, outdated cryptographic advice, and unvalidated request data. All of them come from the same cause — a decade of published material that predates modern PHP's safety features.
Why is this worse in PHP than in other languages?
Because PHP changed more than most between 5.x and 8.x while retaining an enormous volume of never-updated published material, and because a great deal of PHP 5-era code still runs today. The distribution of what has been written skews old more heavily than in younger ecosystems.
How do we catch it in review?
Do not rely on review alone — the code looks ordinary, which is exactly what reviewers scanning for anomalies miss. Enforce strict types and high static-analysis levels in CI, add a security ruleset that finds string-built queries, and keep a short checklist for the four or five specific patterns.
Should we stop using AI coding tools on legacy PHP?
No, but treat legacy codebases as the higher-risk case. Generated code that matches the surrounding legacy style is the hardest to spot, so the modernisation work — current version, strict types, static analysis — is what makes AI assistance safe there rather than merely fast.
Does this apply to frameworks like Laravel and Symfony?
Less severely, because framework conventions are strong and well represented, but the same failure appears as idioms from older framework versions — deprecated helpers, outdated validation syntax, patterns replaced by first-party features. The defence is identical: enforce it in the toolchain.
Get the Toolchain Doing the Work
We will set up the static analysis, security rules and CI gates that make this class of problem fail automatically rather than depend on a reviewer's attention. Talk to our team; we reply within a business day.
