DOWNGRADE
and Other Composer Tricks
TO
upgrade
Before WE Begin...
Slides/Code/Resources + Joind.in Link
wkdb.yt/downgrade
wkdb.yt/downgrade
I've spent a year-and-half focused on upgrading a massive monolithic code base with ~300 dependencies from PHP 7.4 to PHP 8.2
GitHub Issue #19015 "Upgrade Php Version"
Surviving
Version
conflicts
wkdb.yt/downgrade
Upgrade Challenges
1. Abandoned Packages And Maintained Packages Not Supporting Latest PHP Version
2. Packages with Incorrect OR Too-Open Version CONSTRAINTS
3. Severely OutDated Packages
4. Lack of "Dependency Discipline" & Documentation
0. Your Own Code
wkdb.yt/downgrade
Upgrade Challenges
5. Packages That Dropped Support For PHP 7.4 Before Adding PHP 8.2 Support
If you have to support both PHP Versions at the same time with the same code, across your infrastructure. How does that work?
wkdb.yt/downgrade
Composer Dependency Management 101
Why Does This Keep Happening To Me?
wkdb.yt/downgrade
Root
Direct Dependency
Transitive Dependency
Composer Dependency Management 101
Transitive Dependencies of Dependencies
wkdb.yt/downgrade
Composer Dependency Management 101
{
"name": "laravel/laravel",
"type": "project",
"description": "The skeleton application for the Laravel framework.",
"keywords": ["laravel", "framework"],
"license": "MIT",
"require": {
"php": "^8.1",
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^10.10",
"laravel/sanctum": "^3.3",
"laravel/tinker": "^2.8"
},
"require-dev": {
"fakerphp/faker": "^1.9.1",
"laravel/pint": "^1.0",
"laravel/sail": "^1.18",
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^7.0",
"phpunit/phpunit": "^10.1",
"spatie/laravel-ignition": "^2.0"
},
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
]
},
"extra": {
"laravel": {
"dont-discover": []
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true
}
},
"minimum-stability": "stable",
"prefer-stable": true
}
Composer Dependency Management 101
wkdb.yt/downgrade
wkdb.yt/downgrade
brick/math
dflydev/dot-access-data
doctrine/inflector
doctrine/lexer
dragonmantank/cron-expression
egulias/email-validator
fakerphp/faker
filp/whoops
fruitcake/php-cors
graham-campbell/result-type
guzzlehttp/guzzle
guzzlehttp/promises
guzzlehttp/psr7
guzzlehttp/uri-template
hamcrest/hamcrest-php
laravel/framework
laravel/pint
laravel/prompts
laravel/sail
laravel/sanctum
laravel/serializable-closure
laravel/tinker
league/commonmark
league/config
league/flysystem
league/flysystem-local
league/mime-type-detection
mockery/mockery
monolog/monolog
myclabs/deep-copy
nesbot/carbon
nette/schema
nette/utils
nikic/php-parser
nunomaduro/collision
nunomaduro/termwind
phar-io/manifest
phar-io/version
phpoption/phpoption
phpunit/php-code-coverage
phpunit/php-file-iterator
phpunit/php-invoker
phpunit/php-text-template
phpunit/php-timer
phpunit/phpunit
psr/clock
psr/container
psr/event-dispatcher
psr/http-client
psr/http-factory
psr/http-message
psr/log
psr/simple-cache
psy/psysh
ralouphie/getallheaders
ramsey/collection
ramsey/uuid
sebastian/cli-parser
sebastian/code-unit
sebastian/code-unit-reverse-lookup
sebastian/comparator
sebastian/complexity
sebastian/diff
sebastian/environment
sebastian/exporter
sebastian/global-state
sebastian/lines-of-code
sebastian/object-enumerator
sebastian/object-reflector
sebastian/recursion-context
sebastian/type
sebastian/version
spatie/backtrace
spatie/flare-client-php
spatie/ignition
spatie/laravel-ignition
symfony/console
symfony/css-selector
symfony/deprecation-contracts
symfony/error-handler
symfony/event-dispatcher
symfony/event-dispatcher-contracts
symfony/finder
symfony/http-foundation
symfony/http-kernel
symfony/mailer
symfony/mime
symfony/polyfill-ctype
symfony/polyfill-intl-grapheme
symfony/polyfill-intl-idn
symfony/polyfill-intl-normalizer
symfony/polyfill-mbstring
symfony/polyfill-php72
symfony/polyfill-php80
symfony/polyfill-php83
symfony/polyfill-uuid
symfony/process
symfony/routing
symfony/service-contracts
symfony/string
symfony/translation
symfony/translation-contracts
symfony/uid
symfony/var-dumper
symfony/yaml
theseer/tokenizer
tijsverkoyen/css-to-inline-styles
vlucas/phpdotenv
voku/portable-ascii
webmozart/assert
1 Root | 12 Direct | 98 Transitive
Composer Dependency Management 101
wkdb.yt/downgrade
Composer Dependency Management 101
wkdb.yt/downgrade
Composer Dependency Management 201
Sometimes, conflict Is Hard To Resolve
wkdb.yt/downgrade
Composer Dependency Management 201
"Please Don't Make Me Install This"
UnWanted
"Please Tag A New Release"
UnReleased
"Please Don't Leave Me Behind!"
Jumping
"Please Merge My Pull Request"
UnWilling
wkdb.yt/downgrade
Composer Dependency Management 201
Four Composer Commands
show
outdated
depends
prohibits
+
wkdb.yt/downgrade
Important
- How large and how "legacy" is the code base?
- How many packages are installed?
- How many of those are outdated by more than one major version?
- How big of a version jump are you making?
- Do you need to run multiple versions simultaneously?
These are tricks and workarounds for dealing with nasty edge cases and difficult upgrade situations -- and probably things you should avoid doing if you there are better upgrade options
wkdb.yt/downgrade
Live Coding Examples
wkdb.yt/downgrade
Avoiding This In The First Place
Prevention Costs Less Than the Cure
But You May Still Need To Convince Management
wkdb.yt/downgrade
Avoiding This In The First Place
1. If you don't need it, don't install it. If You Do Need It, Keep It UpDated
Be mindful not only about what you add to your "composer.json" file and any new transitive dependencies introduced.
If you do require a package, keep it and its dependencies up to date
wkdb.yt/downgrade
Avoiding This In The First Place
2. Embrace The Adapter Pattern
TL;DR: Instead of directly using classes provided by third party packages (other than framework and "universal" packages) throughout your code, isolate the usage to a single place by defining your own interfaces and implementations that "wrap" the third-party code.
wkdb.yt/downgrade
Avoiding This In The First Place
3. "Frameworkless" PHP IS Great, but Pick a FrameWork
But, if your project uses a bunch of components from Symfony, Laravel, Laminas, CakePHP, and WordPress, you're going to have a bad time.
It's fine to have a project based on a framework like Symfony or Laravel that also uses components from open source libraries, or to use components from a framework and other open source libraries.
wkdb.yt/downgrade
Questions?
Email: andy@wickedbyte.com
GitHub/Discord: andysnell
Downgrade to Upgrade and Other Composer Tricks
By Andy Snell
Downgrade to Upgrade and Other Composer Tricks
Thanks to the ubiquity of Composer, a PHP application may depend on dozens of third-party packages, each of which will define their own dependencies. When an application is deployed and working as expected, it can be difficult to prioritize the development time for package updates with breaking changes, and you might find some dependencies have slipped a few major versions behind their current release. Nevertheless, even if all root dependencies are up to date, some packages inevitably become abandoned, drop version constraints, or add an incompatible dependency. The end result: upgrading to the latest PHP version (or other package) is blocked by a tangled web of interdependent version conflicts and incompatible vendor code.
- 295