PHP 7 stopped receiving security updates at the end of 2022, and PHP 8 has been the only supported line since. The difference is larger than a version number suggests: PHP 8 added a real type system, named arguments, enums, match expressions and a JIT, and it turned a long list of silent misbehaviours into errors. Upgrading is worth it for the security alone, but most code needs some adjustment, and this article covers what that adjustment is.
Support status#
| Version | Released | Security fixes ended |
|---|---|---|
| 7.4 | Nov 2019 | Nov 2022 |
| 8.0 | Nov 2020 | Nov 2023 |
| 8.1 | Nov 2021 | Dec 2025 |
| 8.2 | Dec 2022 | Dec 2026 |
| 8.3 | Nov 2023 | Dec 2027 |
| 8.4 | Nov 2024 | Dec 2028 |
| 8.5 | Nov 2025 | Dec 2029 |
Anything on PHP 7 has been unpatched for years. If you are choosing a target, pick 8.3 or 8.4 for the longest runway with the widest library support.
What PHP 8 added#
Named arguments#
// PHP 7: positional, and you must pass everything in between
htmlspecialchars($text, ENT_QUOTES, 'UTF-8', false);
// PHP 8: name what you mean, skip the rest
htmlspecialchars($text, double_encode: false);
Constructor property promotion#
// PHP 7
class Point {
private int $x;
private int $y;
public function __construct(int $x, int $y) {
$this->x = $x;
$this->y = $y;
}
}
// PHP 8
class Point {
public function __construct(private int $x, private int $y) {}
}
Union types, nullsafe operator, match#
function parse(int|string $value): ?array { ... } // union type, nullable return
$city = $user?->address?->city; // null if any link is null
$label = match ($status) { // strict, returns a value, no fallthrough
200, 201 => 'ok',
404 => 'missing',
default => 'error',
};
Enums, readonly, and more#
8.1 added enums and readonly properties; 8.2 added readonly classes; 8.3 typed class constants; 8.4 property hooks and asymmetric visibility; 8.5 the pipe operator. Each release since 8.0 has been substantial. Code written for 8.4 looks like a different language from 7.4 code, in a good way.
What breaks#
This is the part that matters for an upgrade. Roughly in order of how often they bite:
- Warnings became errors. Passing the wrong type to a built-in function, accessing an undefined array key, dividing by zero: these now throw
TypeError,DivisionByZeroError, or emit warnings that were once notices. Code that “worked” by ignoring notices stops. - String-to-number comparison changed.
0 == "abc"was true in PHP 7 and is false in 8. Any code relying on loose comparison with non-numeric strings behaves differently. - Removed functions.
each(),create_function(), themysql_*functions (gone since 7.0 but still found in old code), and theeregfamily. - Deprecations turned into removals. Curly-brace string offsets,
(real)casts,implode()with arguments in the old order. - Passing null to built-in functions that take strings is deprecated from 8.1:
strlen(null)warns. Common in code that trusts database results. - Dynamic properties on plain classes are deprecated from 8.2. Setting
$obj->newPropwithout declaring it warns unless the class uses an attribute to allow it.
Performance#
PHP 8 is faster than 7 for ordinary web code, through interpreter improvements rather than the JIT. The JIT compiler helps CPU-heavy loops — image processing, number crunching — and does little for typical request-response applications, which spend their time waiting on databases. Enable it and measure; do not expect it to transform a WordPress site.
An upgrade path#
- Find your target. Check the minimum PHP version of your framework and every major dependency. Laravel, Symfony, WordPress and most libraries dropped PHP 7 support some time ago, so the latest compatible library versions will tell you.
- Run static analysis. PHPStan or Psalm with the target PHP version set finds removed functions and type errors before you run anything. PHPCompatibility, a ruleset for PHP_CodeSniffer, is built for exactly this job.
- Use Rector. It rewrites deprecated constructs automatically and does most of the mechanical work.
- Turn deprecations into failures in tests. Run the test suite on 8.x with
error_reporting(E_ALL)and a handler that fails onE_DEPRECATED. - Stage with real traffic. Run a copy on PHP 8, replay production requests or run it in parallel, and read the logs for a week.
- Update Composer dependencies in the same change; old versions of libraries are often the source of the errors, not your code.
composer require --dev phpstan/phpstan rector/rector
vendor/bin/rector process src --dry-run
vendor/bin/phpstan analyse src --level 5
Should you upgrade?#
Yes, and the question is only how. A public-facing PHP 7 application is running on an interpreter with known, unpatched vulnerabilities. Hosting providers are removing 7 from their offerings, and libraries no longer test against it. The work is real but bounded, and the tooling above does most of it. A PHP 7 codebase with tests can usually be moved in days; without tests, the staging step is where the time goes.
Questions people ask#
Will my PHP 7 code run on PHP 8 unchanged?
Simple, well-written code often does. Code relying on notices being ignored, loose comparisons, or removed functions will not. The static-analysis tools tell you which you have.
Is PHP 8 backwards compatible?
Largely, with the documented exceptions above. It is a major version, so some breaks were deliberate.
Does WordPress work on PHP 8?
Current WordPress, yes. Old plugins and themes are the usual problem; test each on a staging site.
Which 8.x should I choose?
The newest your dependencies support, which for most projects in 2026 is 8.3 or 8.4. Avoid 8.0 and 8.1; they are already end-of-life or nearly so.
Where to go next#
- What’s new in PHP 8.5 — the latest release.
- How to check which PHP version is running — before and after.
- PHP not working with Apache after an upgrade — the server-side fix.