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