php-upgrade
PHP Version Upgrade
Upgrade one minor version at a time. Never skip versions -- each step surfaces deprecations that become errors in the next release. Every upgrade follows the same cycle: audit, automate, test, deploy.
Core Principles
| Principle | Meaning |
|---|---|
| Changelog first | Before any upgrade, search the web for the official PHP migration guide (php.net/migration) or ask the user for the changelog -- never rely on static knowledge alone |
| One version at a time | Upgrade 8.1 -> 8.2 -> 8.3 -> 8.4 sequentially -- skipping versions makes it impossible to isolate breakage |
| Fix deprecations before upgrading | Deprecations in version N become errors in version N+1 -- treat them as mandatory fixes |
| Automate first, manual second | Run Rector and PHPCompatibility before touching code by hand -- they catch 80%+ of required changes |
| Prove it with tests | Never consider an upgrade complete without a passing test suite on the target version |
| Pin your platform | Set config.platform.php in composer.json to match your lowest deployment target |
Upgrade Process Overview
Phase 0: Read the Changelog
Before touching any code, obtain the actual changelog for the target PHP version:
- Search the web for
PHP X.Y migration guide(e.g.,PHP 8.4 migration guide php.net) - Or ask the user to provide the changelog / release notes
- Read the official migration page at
https://www.php.net/manual/en/migrationXY.php
This is non-negotiable. Each version has unique changes that static skill knowledge cannot fully capture.
Phase 1: Audit
Before changing any code, understand the scope of the upgrade.
- Run PHPCompatibility against the target version to identify incompatible code
- Run php-parallel-lint with the target PHP binary to catch syntax errors
- Run
composer outdatedto check if all dependencies support the target version - Review the official migration guide at php.net for the target version
- Check PHP extensions for compatibility (
ext-intl,ext-mbstring, etc.)
Phase 2: Automate
Use Rector to handle the bulk of code transformations automatically.
// rector.php
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
->withPhpSets(php84: true); // adjust to target version
Always dry-run first:
vendor/bin/rector process --dry-run
Review changes, then apply:
vendor/bin/rector process
Commit Rector changes separately from manual fixes for clean git history.
Phase 3: Update Dependencies
- Update
composer.jsonwith the new PHP version constraint:"php": ">=8.4" - Update
config.platform.phpto match the target version - Run
composer updateand resolve conflicts - Update PHP extensions as needed
Phase 4: Test and Deploy
- Run the full test suite under the new PHP version
- Run static analysis (PHPStan/Psalm)
- Deploy to staging and verify
- Monitor production logs for deprecation notices after deployment
See Upgrade Process Reference for detailed tool configuration, CI pipeline setup, and Docker considerations.
Tools
| Tool | Purpose | When to Use |
|---|---|---|
| Rector | Automated AST-based code transformation | First step after auditing -- handles most mechanical changes |
| PHPCompatibility | PHP_CodeSniffer ruleset for cross-version compatibility | Audit phase -- identifies all incompatible code before you start |
| php-parallel-lint | Parallel syntax checking (~20x faster than serial) | Audit phase -- catches syntax errors under the new version |
| PHPStan/Psalm | Static analysis | Verification phase -- catches type errors after transformation |
| symfony/phpunit-bridge | Deprecation summary in test output | Ongoing -- monitors deprecation count during upgrades |
Breaking Changes by Version
| Transition | Key Breaking Changes |
|---|---|
| 8.0 -> 8.1 | Fibers introduced, enums added, readonly properties, intersection types, never return type |
| 8.1 -> 8.2 | Dynamic properties deprecated (use #[AllowDynamicProperties] temporarily), $GLOBALS access restrictions, readonly classes, disjunctive normal form types |
| 8.2 -> 8.3 | Typed class constants, json_validate() added, #[Override] attribute, Randomizer additions, date/time exception changes |
| 8.3 -> 8.4 | Implicit nullable types deprecated (function foo(string $bar = null) must become ?string $bar = null), property hooks, asymmetric visibility, new without parentheses deprecated for no-arg constructors, DOM extension namespace changes |
See Version Changes Reference for complete per-version details with code examples.
Common Pitfalls
| Pitfall | Why It Hurts | Prevention |
|---|---|---|
| Skipping versions | Cannot isolate which changes broke what | Always upgrade one version at a time |
| Ignoring deprecation warnings | Deprecations become fatal errors in the next version | Fix all deprecations before upgrading |
| Not checking dependencies | Third-party packages may not support the target version | Run composer outdated and check support before starting |
Using --ignore-platform-reqs |
Bypasses safety checks, causes runtime errors | Never use it -- fix the actual constraints instead |
Not pinning config.platform.php |
Local PHP differs from production, causing install mismatches | Always set it to match production |
| Large unreviewed Rector runs | Rector can make incorrect transformations in edge cases | Always dry-run first, review changes, run on small batches |
| Forgetting PHP extensions | Extensions change behavior or get deprecated between versions | Audit all required extensions before upgrading |
Quick Reference: Upgrade Checklist
- Review php.net migration guide for target version
- Run PHPCompatibility scan against target version
- Run php-parallel-lint with target PHP binary
- Verify all Composer dependencies support target version
- Configure and run Rector with target version set (dry-run first)
- Review and commit Rector changes
- Apply manual fixes for remaining issues
- Update
composer.jsonPHP constraint andconfig.platform.php - Run
composer update - Run full test suite on target PHP version
- Run static analysis (PHPStan/Psalm)
- Deploy to staging and verify
- Deploy to production and monitor logs
Reference Files
| Reference | Contents |
|---|---|
| Upgrade Process | Detailed tool configuration, CI pipeline setup, Docker strategy, and step-by-step commands |
| Version Changes | Per-version breaking changes, new features, and deprecations with code examples (PHP 8.0 through 8.4) |
Integration with Other Skills
| Situation | Recommended Skill |
|---|---|
| Upgrading Symfony alongside PHP | Use the symfony-upgrade skill in frameworks/symfony/ |
| Updating Composer dependencies after PHP upgrade | Use the composer-dependencies playbook skill |
| Modernizing PHP code patterns (DTOs, enums, strict types) | Install php-modernization from dirnbauer/webconsulting-skills or netresearch/php-modernization-skill |
| Running static analysis after upgrade | Install knowledge-virtuoso from krzysztofsurdy/code-virtuoso for testing strategies |