Critical PHP vulnerability (PHP2Shell, 21 October 2026): is your website patched?

Check and fix

Upgrading a Laravel application from PHP 8.1 to 8.3 or 8.4: a practical checklist

PHP 8.1 went end of life on 31 December 2025 and received no PHP2Shell release. A Laravel application still running on it has to move to 8.3 or 8.4. The PHP change itself takes minutes. The time goes into three other places: the framework version, the dependencies, and the server configuration that still points at 8.1 after the switch. This checklist covers all three.

1. Check the framework version first

Each Laravel release supports a range of PHP versions, and the ranges are narrower than people expect:

  • Laravel 9 supports PHP 8.0 to 8.2 and left security support in February 2024.
  • Laravel 10 supports PHP 8.1 to 8.3 and left security support in February 2025.
  • Laravel 11 requires PHP 8.2 or newer and left security support in March 2026.
  • Laravel 12 and 13 are the current targets; 12 requires PHP 8.2 or newer, 13 requires PHP 8.3 or newer.

Check the exact dates on the Laravel release notes before planning, because they move. The practical consequence: an application on Laravel 9 cannot simply be switched to PHP 8.3; it needs a framework upgrade first, which is the larger project. Laravel 10 can run on 8.3, which makes it a workable stepping stone, but it is itself out of support, so plan the next step too.

Find your version with php artisan --version or in composer.lock.

2. Let Composer tell you what blocks the move

Before touching the server:

composer why-not php 8.3
composer why-not php 8.4

Every package listed is one that declares it does not support that PHP version. Some need an update, some have been abandoned and need a replacement. Also look at composer.json for a config.platform.php entry; if it pins an old version, Composer resolves dependencies as if the server still ran 8.1, and you will install packages that cannot run on 8.3.

Then update on a branch, with the target PHP installed locally:

composer update --with-all-dependencies

Review the diff of composer.lock. Major version jumps in packages deserve a read of their upgrade notes.

3. Run the test suite under the new version

If the application has tests, run them on the target PHP before anything else. If it does not, this is the moment to write a handful of smoke tests for the key journeys: login, the main forms, any payment or export, scheduled commands. They will pay for themselves during this upgrade and the next one.

Static analysis helps too. PHPStan or Larastan at a modest level will flag most type-related breakages that only appear at runtime otherwise, and Rector has rule sets for the 8.2 to 8.4 changes.

4. The deprecations that matter

From 8.1 to 8.4 the changes most Laravel codebases trip over:

  • Dynamic properties (deprecated in 8.2). Eloquent models are unaffected because they implement __get and __set, but plain classes that set undeclared properties, often in older service classes or value objects, now emit deprecation notices and will break in a future release. Declare the properties or add the #[\AllowDynamicProperties] attribute as a stopgap.
  • ${var} string interpolation (deprecated in 8.2). Use {$var}.
  • utf8_encode() and utf8_decode() (deprecated in 8.2). Use mb_convert_encoding().
  • Passing null to non-nullable internal function parameters (deprecated since 8.1, still a frequent source of notices). Typical: trim($request->input('name')) when the input is absent.
  • Implicitly nullable parameters (deprecated in 8.4): function f(string $x = null) should become function f(?string $x = null).

Set error_reporting(E_ALL) and log deprecations on staging so they surface before production.

5. The server pieces that still say 8.1

This is where upgrades "succeed" and the site still runs on the old version. Search the server for 8.1:

  • Web server: Nginx fastcgi_pass unix:/run/php/php8.1-fpm.sock or an Apache FPM proxy pointing at the old socket.
  • The php binary on the path: update-alternatives --config php on Debian and Ubuntu, otherwise cron jobs and artisan commands keep running 8.1.
  • Cron: scheduler entries that call /usr/bin/php8.1 artisan schedule:run.
  • Queue workers: Supervisor or systemd units that run php8.1 artisan queue:work, Horizon included. After the change, php artisan queue:restart so running workers pick up the new binary.
  • Octane, FrankenPHP or RoadRunner: the server process has to be restarted with the new runtime.
  • OPcache: restart PHP-FPM after deploying so the opcode cache is cleared.
  • Docker: the base image tag (php:8.1-fpm to php:8.3-fpm or 8.4), plus any extension install lines that reference the version.

Once the old packages are no longer referenced anywhere, remove them, so a future mistake cannot fall back to an unpatched runtime.

6. Switch, verify, watch

Use a maintenance window, take a backup, switch the FPM pool and the web server configuration, restart, and verify with php -v, a phpinfo page and the application's own health endpoint. Then watch the logs for a few days: deprecation notices from rarely used paths tend to appear a week later.

If you would like a second pair of eyes on the inventory or the upgrade itself, send us the Laravel version, the current PHP version and where the application is hosted.

LET’S MAKE YOUR NEXT MOVE

Ready to move your
business forward?

Tell us where you want to go. We can help you shape the website, application or integration to get started.

Let’s talk about your project
Chat on WhatsApp