DEV Community

Peter Fox
Peter Fox

Posted on • Originally published at articles.peterfox.me on

PHP: 7 tricks to help with upgrading Composer packages


Photo by JESHOOTS.COM on Unsplash

Recently I had the pleasure of taking an enterprise app and upgrading it from Laravel 9 to Laravel 10. Like a lot of applications we also had a good number of dependencies to upgrade. While I won’t go into any specifics of that upgrade I will point out some of the techniques I used to Composer, PHP’s package manager that allowed me to navigate some of the complexities.

Often we think an upgrade is just typing out composer update and then seeing what needs fixing. Personally I think this is fine for an application without many decencies and has been around for a year or two but for many this is just going to be too broad and leave you struggling to debug issues. That’s why the first thing I will talk about is selective update.

Selective updates

When using the update command it is possible to change just the composer.lock file for specific packages. When you do this you specify you only want to update that package. This might throw up some dependencies issues or just do nothing if you try to specify a version that doesn’t play nicely with other packages you have in your lock file.

You’ll notice in the output below that for this Laravel project, we can specify package versions that are required. For any that are sub dependencies we can only allow the package to be updated unless we use the --with option.

composer update \ 
laravel/framework:~10.0.0 \ # allows for specific versions 
spatie/laravel-ignition:^2.0 \ 
monolog/monolog \ # can only be a reference to allow an upgrade 
spatie/ignition \ # we'll use the --with option to set a specific version
symfony/http-foundation \ 
symfony/mailer \ 
nesbot/carbon \ 
symfony/mime \ 
spatie/flare-client-php \ 
spatie/laravel-package-tools \ 
spatie/laravel-activitylog \
--with spatie/ignition:1.11.3 # our specific version for a sub dependency
Enter fullscreen mode Exit fullscreen mode

A good example of why you might want to specify versions and increment them is finding bugs as you go. Myself when upgrading this work project to Laravel 10 found that installing just 10.0.3 there was only one bug but when it came to Laravel 10.43.0 I had multiple others to fix.

I resolved this by simply finding which version of Laravel 10 caused the bug and fixing it by making educated guess from what was in the changelog for that version of the framework. Then I’d increment to the next version that had a bug until I reached the latest version.

Such approaches might seem tedious but they can be pretty beneficial.

Dry run

Something that can be useful is simply to try commands with the dry-run option. This doesn’t help that much in the long run but it allows you to try things out without actually writing the changes to composer.json or composer.lock. This command option works with both require and update.

composer require php:^8.3 --dry-run
Enter fullscreen mode Exit fullscreen mode

Find what depends on a package

If you’re struggling to update a package and the output of that command update is confusing you can absolutely try using the depends command (also known as the whycommand). This can help you see if one of the sub packages is used by another package as a sub dependency.

When using this command you should have a nice out like below that lists any conflicting dependencies.

peterfox@Peters-MBP-2 project % composer depends nesbot/carbon  
laravel/framework v10.38.1 requires nesbot/carbon (^2.67)   
laravel/octane v2.2.4 requires nesbot/carbon (^2.66.0) 
laravel/pulse v1.0.0-beta10 requires nesbot/carbon (^2.67)   
spatie/flare-client-php 1.4.3 requires nesbot/carbon (^2.62.1)
Enter fullscreen mode Exit fullscreen mode

Find packages blocking updates

Another command that you can use before getting started is simply the why-not command. It’s also called prohibits but honestly why-not just seems more memorable!

Now Laravel 11 is literally days away so lets try seeing what it will take to get this current Laravel 10 project update to date.

peterfox@Peters-MBP-2 project % composer why-not laravel/framework 11.0.0
Package "laravel/framework" could not be found with constraint "11.0.0", results below will most likely be incomplete.
laravel/laravel dev-main requires laravel/framework (^10.0)                              
laravel/fortify v1.19.1 requires illuminate/support (^8.82|^9.0|^10.0)                  
laravel/jetstream v4.2.0 requires illuminate/console (^10.17)                            
laravel/jetstream v4.2.0 requires illuminate/support (^10.17)                            
laravel/octane v2.2.4 requires laravel/framework (^10.10.1)                           
laravel/sanctum v3.3.2 requires illuminate/console (^9.21|^10.0)                       
laravel/sanctum v3.3.2 requires illuminate/contracts (^9.21|^10.0)                     
laravel/sanctum v3.3.2 requires illuminate/database (^9.21|^10.0)                      
laravel/sanctum v3.3.2 requires illuminate/support (^9.21|^10.0)                       
laravel/scout v10.6.1 requires illuminate/bus (^9.0|^10.0)                            
laravel/scout v10.6.1 requires illuminate/contracts (^9.0|^10.0)                      
laravel/scout v10.6.1 requires illuminate/database (^9.0|^10.0)                       
laravel/scout v10.6.1 requires illuminate/http (^9.0|^10.0)                           
laravel/scout v10.6.1 requires illuminate/pagination (^9.0|^10.0)                     
laravel/scout v10.6.1 requires illuminate/queue (^9.0|^10.0)                          
laravel/scout v10.6.1 requires illuminate/support (^9.0|^10.0)                        
laravel/slack-notification-channel v3.1.0 requires illuminate/http (^9.0|^10.0)                           
laravel/slack-notification-channel v3.1.0 requires illuminate/notifications (^9.0|^10.0)                  
laravel/slack-notification-channel v3.1.0 requires illuminate/support (^9.0|^10.0)                        
laravel/tinker v2.8.2 requires illuminate/console (^6.0|^7.0|^8.0|^9.0|^10.0)         
laravel/tinker v2.8.2 requires illuminate/contracts (^6.0|^7.0|^8.0|^9.0|^10.0)       
laravel/tinker v2.8.2 requires illuminate/support (^6.0|^7.0|^8.0|^9.0|^10.0)         
livewire/livewire v3.3.3 requires illuminate/database (^10.0)                            
livewire/livewire v3.3.3 requires illuminate/support (^10.0)                             
livewire/livewire v3.3.3 requires illuminate/validation (^10.0)                          
openai-php/laravel v0.4.3 requires laravel/framework (^9.46.0|^10.7.1)                    
spatie/laravel-ignition 2.3.2 requires illuminate/support (^10.0)                             
spatie/laravel-package-tools 1.16.1 requires illuminate/contracts (^9.28|^10.0)                     
spatie/laravel-ray 1.33.0 requires illuminate/contracts (^7.20|^8.19|^9.0|^10.0)          
spatie/laravel-ray 1.33.0 requires illuminate/database (^7.20|^8.19|^9.0|^10.0)           
spatie/laravel-ray 1.33.0 requires illuminate/queue (^7.20|^8.19|^9.0|^10.0)              
spatie/laravel-ray 1.33.0 requires illuminate/support (^7.20|^8.19|^9.0|^10.0)            
spatie/laravel-webhook-client 3.2.0 requires illuminate/bus (^8.50|^9.0|^10.0)                      
spatie/laravel-webhook-client 3.2.0 requires illuminate/database (^8.50|^9.0|^10.0)                 
spatie/laravel-webhook-client 3.2.0 requires illuminate/support (^8.50|^9.0|^10.0)                  
teamtnt/laravel-scout-tntsearch-driver v12.5.0 requires illuminate/bus (~5.4|^6.0|^7.0|^8.0|^9.0|^10.0)        
teamtnt/laravel-scout-tntsearch-driver v12.5.0 requires illuminate/contracts (~5.4|^6.0|^7.0|^8.0|^9.0|^10.0)  
teamtnt/laravel-scout-tntsearch-driver v12.5.0 requires illuminate/pagination (~5.4|^6.0|^7.0|^8.0|^9.0|^10.0) 
teamtnt/laravel-scout-tntsearch-driver v12.5.0 requires illuminate/queue (~5.4|^6.0|^7.0|^8.0|^9.0|^10.0)      
teamtnt/laravel-scout-tntsearch-driver v12.5.0 requires illuminate/support (~5.4|^6.0|^7.0|^8.0|^9.0|^10.0)    
Not finding what you were looking for? Try calling `composer require "laravel/framework:11.0.0" --dry-run` to get another view on the problem.
Enter fullscreen mode Exit fullscreen mode

This gives us a nice list to work with that allows us to sign off which packages we might need to look at. Equally we can try upgrading these one by one until hopefully none of them show up anymore. For instance if I try just a slightly newer version of Laravel 10, here’s what I get:

peterfox@Peters-MBP-2 project % composer why-not laravel/framework 10.43.0
There is no installed package depending on “laravel/framework” in versions not matching 10.43.0
Not finding what you were looking for? Try calling `composer require “laravel/framework:10.43.0” — dry-run` to get another view on the problem.
Enter fullscreen mode Exit fullscreen mode

There’s not issues so we’re ready to upgrade.

Ignore platform dependencies

This is also something worth knowing. We can actually ignore PHP platform dependencies when performing require or update commands. The reason you might want this is to ignore when a certain PHP extension isn’t install on your local machine but might be in production.

This does work as well with PHP as a dependency itself. That’s right, even if you are working with PHP 8.1 but you want to install package that rely on newer PHP versions like 8.2 and 8.3 you can tell composer to just bypass this.

composer update laravel/framework --ignore-platform-reqs=php
Enter fullscreen mode Exit fullscreen mode

There is a level of risk and I probably wouldn’t use this in production but it allows you to at least try out packages early without having to do anything overly hacky like downloading with one version to run with another.

This is also just happy when working with static analysis tools as you can download dependencies for things like PHPStan or Rector when you don’t intend to run the code locally.

Require across versions

This is more of a tip that PHP package developers will know but others might not be aware of and that is you can specify different versions in composer.json allowing you to then run update to switch to a new version.

For instance when upgrading to Laravel 10 from 9, you must also update spatie/laravel-ignition to version 2 or higher. The problem is you can’t do it in one command as one is a dev requirement and one is a production requirement but if you do them in seperate commands they will cause update issues to occur.

Doing this allows you to specify a new version to work with while still keeping the possibility to install older ones using the update command as I mentioned earlier.

composer require “laravel/framework:^9.0|~10.0.0”
composer require "spatie/laravel-ignition:^1.2|^2.0" --dev
Enter fullscreen mode Exit fullscreen mode

Once you’re happy with the upgrade you can now just require those fixed versions. Remember, if you’re using install and you’re keeping the composer.lock file under source control, the version in the lock file is what will be installed.

Use your own forks

You might sometimes hit a hurdle that can be quite annoying. What do you do when a package isn’t updated to the latest framework or PHP version. A quick fix for yourself is to fork the source code. Most PHP packages are likely to be on github or another git based system. In the example below we can point a different repository for Laravel Framework allowing me to try out a fork without setting up anything in Packagist.

Note with the following command I specify the laravel/framework and the url https://github.com/peterfox/laravel-framework which where composer can find the forked code.

composer config repositories.laravel/framework vcs https://github.com/peterfox/laravel-framework
Enter fullscreen mode Exit fullscreen mode

Once we’ve done this, we can easily just require a particular branch. In this case the branch is my-upgrade-branch but to tell Composer it is a branch we put dev- before the name of the branch. With this, we have installed a forked branch of a popular package.

composer require laravel-framework:dev-my-upgrade-branch
Enter fullscreen mode Exit fullscreen mode

We can always remove the repository we added later on with the following command.

composer config — unset repositories.laravel/framework
Enter fullscreen mode Exit fullscreen mode

Conclusion

Thank you again for getting through all of these. I hope it will be useful in your larger projects where upgrading code is a complex mystery to unravel as it has often been for me. Remember you can learn more about these commands available in composer by running composer list.

I’m Peter Fox, a software developer in the UK who works with Laravel. Thank you for reading my article, I’ve got several more available at https://articles.peterfox.me. I’m also now also Sponsorable on GitHub. If you’d like to encourage me to write more articles like this please do consider dropping a small one off donation.

Top comments (0)