Migrating PHP from an old version to a current one (8.3+) is one of the most profitable modernizations in an old system — it removes a whole class of security risks, speeds the application up without touching business code, and unblocks framework and library upgrades. PHP 5.6 has been without security support since the end of 2018, and PHP 7 support ended in 2022 — systems running on these versions are today without patches for known vulnerabilities. This guide shows how to plan the migration step by step and what to expect.
Key takeaway: A PHP migration is a process, not a version swap on the server. The order: compatibility audit (PHPCompatibility, PHPStan) → safety-net tests → automated refactoring (Rector) → staging → production with monitoring. A safe migration through an intermediate version (e.g. 7.4) is faster than jumping two generations at once, though simple projects can go directly.
Why an old PHP version is a problem
Three costs that grow every year
Security. PHP versions past end of support receive no security fixes. This is not a theoretical risk — known vulnerabilities in the interpreter and standard libraries get publicly documented, and a running system becomes a target. In systems handling payments or personal data, this is a compliance risk, not just a technical one.
Ecosystem compatibility. New versions of libraries and frameworks require new PHP versions. Staying on 5.6 or 7.x cuts you off from updates — and with them from security and performance fixes. Increasingly, hosting providers are retiring old versions too — you end up paying for "legacy add-ons" or hunting for special setups.
Performance and maintenance costs. PHP 8.x is significantly faster than PHP 5.6 — the engine optimizations and JIT alone can speed an application up by tens of percent with no code changes. On top of that comes the cost of people: developers who know PHP 5.6 are increasingly rare, and rates keep rising. The broader picture of these costs is covered in the article on technical debt.
What the migration path looks like
The safest path from PHP 5.6 goes through an intermediate version — 7.4 or 8.0 — and only then to 8.3+. Jumping two generations at once is possible, but it usually means more errors at the same time and harder diagnosis. The intermediate version gives you checkpoints: tests, deployment and observation after every step.
For Laravel applications there is an additional dimension — migrating the framework between versions, with its own sequence and pitfalls. That is a separate journey that can run in parallel, but then the plan must be all the more careful.
Step 1 — compatibility audit
Before changing anything, measure the scale of the problem. Version-compatibility tools show exactly which code fragments will stop working:
- PHPCompatibility — a PHP_CodeSniffer ruleset that scans code for constructs removed or changed in newer versions,
- PHPStan with rules for the target version — catches type and call problems that become errors in the new version,
- extension inventory — whether the system uses extensions that disappeared or changed APIs (e.g.
mysql_*removed in PHP 7), - dependencies — checking which libraries have versions compatible with the target PHP and which are abandoned.
The audit output is a list of blockers split into categories: "auto-fixable," "needs a human decision," "library must be replaced." That is also the migration plan. More on this stage in the legacy code audit guide.
Step 2 — safety-net tests
A PHP version migration changes system behavior in places nobody expects — from type handling, through sorting, to number formatting. Without tests, every such effect shows up in production.
The minimum is characterization tests on critical paths: login, checkout, payment, document generation. Running them in CI on the target PHP version turns migration from a gamble into a process with clear criteria — green tests are the deployment condition.
Step 3 — automated refactoring with Rector
Fixing code by hand for a new PHP version takes weeks. Rector does it automatically: it ships ready-made rulesets for version transitions (PHP 5.6 → 7.x → 8.x) that replace removed functions, update syntax and modernize constructs to current standards.
- The rollout practice: run Rector with the ruleset for the nearest step, review the diff (it can be thousands of changes — that is why you need tests), run the tests, commit. Do it version by version rather than all at once — it is easier to attribute any errors to a specific change.
Rector will not fix everything — logic relying on behaviors that changed between versions (e.g. type casting, sorting functions) needs human decisions. But it eliminates 80–90% of the mechanical work. The refactoring techniques worth combining with the migration are covered in the legacy code refactoring guide.
Step 4 — staging and deployment
You deploy the migration like any other change in the modernization process: staging with real data (on a copy) first, then production with monitoring.
- staging should run the target PHP version with the same extensions as production — configuration differences are the most common source of "it works on my machine,"
- schedule the production rollout outside peak traffic and have a rollback plan — the old PHP version stays on the server until stability is confirmed,
- after deployment, watch error logs and performance for a few days; deprecation warnings in logs are your to-do list for the next iterations,
- only after stabilization, upgrade dependencies and the framework to target versions — the second stage of the same journey.
Automated tests and deployments at every stage are not a luxury but a condition of a safe migration — the configuration is covered in the article on CI/CD and DevOps for Laravel.
What usually breaks during migration
Experience from projects says problems cluster in a few places
- functions removed between versions —
each(),create_function(), themysql_*extension, the oldpreg_replace/emodifier, - types and casting — newer versions are stricter; code that "passed" now throws type errors,
- null and empty values — passing null to functions expecting strings is deprecated in 8.1 and an error in future versions,
- dynamic properties — creating properties on the fly is deprecated since 8.2; code relying on it needs refactoring,
- abandoned libraries — a dependency with no PHP 8-compatible release must be replaced (usually the most time-consuming item),
- edge behaviors — sorting, numeric string comparison, formatting — where language semantics changes only show up in tests.
None of these is a blocker — each is a line in the plan. Only the ones you do not know about are dangerous, which is why the compatibility audit and tests are not optional.
How long it takes and what it costs
A small project (a dozen thousand lines, no exotic dependencies) migrates in a few days. A medium system with a framework and extensive integrations typically takes 2–8 weeks, depending on the number of blockers and test quality. The biggest variable cost is replacing abandoned libraries.
A PHP migration is best planned as part of a wider modernization program — then the costs spread across stages, and each stage delivers value: security, performance, newer libraries. The full plan — from audit to CI/CD — is in Legacy Code Modernization — Step by Step, and the scope of the service on the code maintenance and modernization page.
Frequently asked questions
Can I upgrade PHP without code changes?
In simple projects an upgrade plus tests is sometimes enough, but that is the exception. Code written for PHP 5.6 or 7.x usually uses constructs removed or changed in newer versions. The standard path is a compatibility audit, refactoring with Rector and tests — then deployment.
Does a PHP migration require system downtime?
No. You prepare the migration on staging and deploy like any other change — outside peak hours, with a rollback option. The system does not need to be stopped for the migration; all the work happens outside production, and production only sees the version swap.
Which PHP version should I target if the system runs on 5.6?
The safe route goes through an intermediate version — usually 7.4 — then 8.0/8.1 and finally 8.3 or newer. Intermediate versions give you checkpoints: tests and deployment after every step. A direct jump is possible in simple projects but makes error diagnosis harder.
Will upgrading PHP speed up the application?
Usually yes, and without code changes — newer PHP versions are significantly faster (engine optimizations, JIT since 8.0). The real gain depends on the application profile, but speedups of tens of percent are not uncommon. The full performance effect also requires upgrading dependencies and the framework.