Development Tips

Upgrading PHP in 2026: What to Fix Before You Touch the Version Number

By Ken Key Mar 16, 2024 4 min read Updated Oct 16, 2026

Upgrading PHP in 2026: What to Fix Before You Touch the Version Number
The short answer

Before you bump the PHP version, audit your dependencies and extensions. Run composer outdated --direct and php -m, then fix abandoned packages, dynamic property warnings, and loose type juggling first. The version number is the easy part. The code around it is what breaks.

Part of the complete web development guide.

On this page

Most PHP upgrades don’t fail because of the language. They fail because of the stuff around it: abandoned packages, sloppy type juggling, and code that was written for PHP 5.6 and never revisited. If you’re maintaining a site that’s still on 7.4 or 8.0, the plan matters more than the version you land on.

Here’s how I approach a real upgrade on a production site, in the order I actually do it.

Know where you’re starting

Before anything else, find out what you’re running and what depends on it. On the command line:

php -v
composer show --direct
composer outdated --direct

That last one is the important one. If you have direct dependencies that haven’t had a release in three years, that’s your real project. The PHP version bump is the easy part.

Also check your extensions. A lot of older code relies on things like mcrypt (gone since 7.2) or ext/redis versions that don’t match. Run php -m and compare against what your code actually calls.

The things that break in practice

I’ve done enough of these to know the usual suspects. They’re rarely exotic.

Dynamic properties are deprecated

Since PHP 8.2, writing to an undeclared property on a class throws a deprecation notice. In 9.0 it becomes an error. If you have old models or DTOs that just assign whatever they want, this will bite you.

class User {
 public string $name;
}

$user = new User();
$user->name = 'Ken'; // fine
$user->nickname = 'kenny'; // deprecated in 8.2+, error later

Fix it properly with a declared property, or if you genuinely need dynamic behavior, add #[AllowDynamicProperties]. Don’t slap the attribute on everything to silence the warning. That’s how you end up doing this again next year.

String-to-number comparison changed

PHP 8 changed how loose comparisons work with numeric strings. This one causes silent logic bugs, not crashes, which is worse.

// PHP 7: true
// PHP 8: false
var_dump(0 == "abc");

If your code has == comparisons against user input, audit them. Use === where the types should match, and intval() or filter_var() when you actually want a number.

Null to non-nullable parameters

Passing null to a function that declares a non-nullable scalar type now throws a TypeError. This shows up constantly in older code that used null as a “no value” sentinel.

function formatDate(string $date): string { /* ... */ }

formatDate(null); // TypeError in PHP 8+

Make the parameter nullable (?string $date) if null is a legitimate input, or handle it before the call. Don’t just cast it to an empty string and move on.

Fatal errors became exceptions

A lot of code relied on warnings and notices being non-fatal. In PHP 8, many of those are now Error exceptions. If you have a global error handler that swallows notices, it won’t catch these. Check your error handling paths.

Do the upgrade on a copy, not production

This should go without saying, but I’ve seen it skipped. Spin up a staging environment that mirrors production as closely as possible: same PHP version target, same extensions, same database. Then:

  • Run your test suite if you have one. If you don’t, this is the moment to write a few smoke tests for the critical paths.
  • Enable full error reporting and log deprecations. error_reporting(E_ALL) and watch the logs, not the screen.
  • Click through the site manually. Checkout flows, login, form submissions, anything that touches the database.
  • Run static analysis. PHPStan or Psalm at a reasonable level will catch a lot of the type issues before you hit them at runtime.
composer require --dev phpstan/phpstan
vendor/bin/phpstan analyse src --level=5

Level 5 is a sane starting point for legacy code. Don’t chase level 9 on a codebase you didn’t write. Fix the real problems first.

Then bump the version, one step at a time

Don’t jump from 7.4 to 8.4 in one move. Go 7.4 to 8.0, test, then 8.0 to 8.1, and so on. Each minor version has its own deprecations, and doing them one at a time means when something breaks, you know exactly which change caused it.

Update your composer.json platform constraint to match, and re-run composer update on the dependencies that need newer versions. Read the changelogs. Some packages have breaking changes between majors that have nothing to do with PHP itself.

When it’s time to rewrite instead of patch

Sometimes the upgrade reveals that the codebase is held together with hope. If you’re spending more time working around a framework that stopped getting updates in 2019 than actually shipping features, patching the PHP version is treating a symptom. That’s the point where a rebuild starts making more sense than another round of band-aids. If you’re local and weighing that decision, this is the kind of work I do as custom web development for businesses that have outgrown their original site.

The short version

  • Find your real dependencies before you touch anything.
  • Fix dynamic properties, loose comparisons, and null handling first.
  • Upgrade on staging, one minor version at a time.
  • Add static analysis so the next upgrade is easier than this one.

None of this is glamorous. It’s just the difference between a site that keeps running and one that goes down on a Tuesday afternoon because nobody checked the deprecated code paths.

FAQ

Can I just upgrade PHP and see what happens?

On a throwaway dev box, sure. On a live site with customers, no. Put it on staging first and turn on full error logging, because the failures you don’t see are the ones that cost you money.

Do I need to rewrite everything for PHP 8.4?

Usually no. Most well-maintained code upgrades with minor fixes. The rewrite conversation starts when your dependencies are abandoned and you’re spending more time working around them than building.

How long does a typical upgrade take?

Depends entirely on the codebase. A clean modern app might be a day. A legacy site with no tests and five abandoned packages can take a week or more. The dependency audit tells you which one you’re dealing with.

Have a project like this?

I do custom web development for Long Island businesses. If you need this done right, let’s talk.