Legacy PHP · MySQL

It works fine.
That is the problem.

A PHP application from 2012 does not degrade the way other systems do. It runs, quietly, for years — right up to the morning your hosting provider removes the runtime on a date nobody at your business chose. The migration happens either way. The only question is who picks the day.

The deadline you did not set

Somebody else's calendar is the one that matters.

These dates are public and they have already passed. Unsupported does not mean broken — it means no security patches, and a hosting provider free to withdraw the runtime whenever it suits them.

PHP end-of-life, as published by the project
VersionSupport endedWhat we usually find
PHP 5.6December 2018Nearly eight years unsupported. The last version before mysql_* was removed.
PHP 7.0 – 7.32019 – 2021The versions most "we upgraded once" applications are stuck on.
PHP 7.4November 2022Still extremely common, and the most frequent starting point we see.
PHP 8.0November 2023Recent enough that owners are surprised to learn it is already out of support.

MySQL matters here too: a 5.5 or 5.6 database is usually the same age as the application and moves with it.

What actually blocks the move

Seven things, and the first one is most of the cost.

A runtime upgrade is rarely the hard part. What makes it expensive is everything the application was allowed to do when it was written — and a good estimate depends on counting those, not guessing at them.

01

mysql_* was removed in PHP 7

The single biggest cost in moving off 5.x, and the one that is actually measurable before you start. Every mysql_query, mysql_fetch_assoc and mysql_real_escape_string has to become mysqli or PDO. We count the call sites in the first day, which turns "how long is a piece of string" into a number.

02

The deployed code is the only copy

Uploaded by FTP, edited on the live server, never in version control. Production and whatever repository exists have quietly diverged, and only production is real. Reconciling them is often the first thing worth doing.

03

Queries built by string concatenation

Not a style opinion. An application from this era typically has SQL assembled from request data in dozens of places, which is injection whether or not anyone has found it yet.

04

Passwords as unsalted md5 or sha1

Standard practice when the application was written, and trivially reversible now. Fixable without a rewrite: rehash on next successful login and the old column empties itself over a few weeks.

05

No Composer, or a lock file that no longer resolves

Libraries pasted into a vendor folder years ago, with no record of what version or what has been patched since. Sometimes there is no dependency manifest at all.

06

A framework that is itself end-of-life

CodeIgniter 2, Zend 1, an early CakePHP, or a bespoke MVC written in-house. The application is not the only thing that stopped being supported.

07

Encoding that was never right

latin1 tables serving utf-8 pages, or the reverse. It shows up as mangled accented characters — which matters more here than in most markets, because half the data is in French.

What usually happens next

Almost never a rewrite.

Recover the source first. Get the deployed tree into version control, reproduce a build on a clean machine, and reconcile it with whatever repository exists. Until that is true nothing else can be done safely, and it is usually a week rather than a project.

Then migrate the runtime, in a copy. The application runs on the new version alongside the old one until it behaves identically. Nobody switches on a Friday. This is where the mysql_* call count from day one turns into a schedule.

Close the security gaps as you go. Parameterised queries, password rehashing on login, secrets out of the tree. These are done during the migration rather than as a second project, because the code is already open.

Where a piece genuinely cannot be touched, route around it. Occasionally one capability is too central to edit safely — pricing, invoicing, anything every transaction passes through. That one moves out to a small service beside the application, reading through a seam, with nothing inside the original changed. See the bypass.

Before the host picks the date

One day, $1,500, and no access required.

Triage tells you whether this is a three-week migration or a nine-month one, from a conversation and what is visible from outside. The full health check is $3,500 and counts the things that actually drive the number.