Keeping any project which uses a framework or a collection of 3rd party libraries up to date is an exercise in continuous upgrades. In my situation, I have dozens of projects on a variety of internal and 3rd party frameworks, so keeping everything updated – from the PHP version to the framework and library versions – is an important and time consuming process. Here are some strategies and techniques.
Having a Plan
To avoid being caught in a random scattering of updates and partially updated projects, and so we aren’t at the mercy of the release schedule of our chosen technologies… it’s a good idea to have a scheduled timeframe for when we process updates. For me, I try to plan a concerted sweep of my projects every 6 months. I update the PHP version and the framework + libraries at the same time so that I have just one big round of fixing things and removing deprecations.
Keep track of your projects’ version numbers in a way you can easily review. If you just have a few projects, you might want a section in the ReadMe file which outlines the main versions affecting the project – namely the PHP, MySQL and Framework versions. For me, I have dozens of projects so I keep a spreadsheet tracking this. I can also track other technologies that need updates like WordPress.
Checking Versions
The current versions and their lifetimes for PHP are outlined in this page on the PHP website.
For Symfony, you can view the current release as well as previous releases and their lifetimes on the Symfony releases page.
Try to upgrade your project to the latest, stable release.
You can check the PHP version running on your server with php -v
. You’ll see something like this:
$ php -v
PHP 8.0.12 (cli) (built: Oct 22 2021 12:34:48) ( NTS )
Copyright (c) The PHP Group
To check your Symfony framework version you’ll use the ./bin/console --version
command.
$ ./bin/console --version
Symfony 5.3.6 (env: dev, debug: true)
To check the version of a specific composer package you’ll use Composer’s show command like so:
composer show symfony/flex
This will give you the package’s version number and other information about it.
Most packages use the SemVer versioning scheme. Which means that if only the last 2 numbers in a 3 number version are changing, you’ll typically have a smooth upgrade process. For example, I am upgrading Symfony from 5.3.6 to 5.3.10. Only the 3rd number is changing which means the newer version was only fixing bugs and other very minor changes. The bigger the version jump, the more fixing you’ll likely have to do.
Prepare To Upgrade
I like to ensure that my project is stable and bug free first. I run my tests or perform a smoke test of the project.
It’s a good idea to commit any work to your repo and tag that commit so you can easily find and return to it if things go awry.
Jot down some notes about what you were working on – if anything – in relation to that project. So that you can continue where you left off once you’re done upgrading.
If you work with a team on the project, chat with them to see if any urgent work is about to be done for the project and run the plan by your manager if applicable.
Upgrading Symfony
First, if you are using the symfony CLI tool, you should make sure that it is up to date by doing (you may have to run this with sudo):
symfony self:update
Symfony has detailed articles on how to upgrade if it is a major, minor or patch version upgrade. Consult the which document relates to you:
- Upgrade a Major Version (ex 4.0.0 -> 5.0.0)
- Upgrade a Minor Version (ex 5.2.0 -> 5.3.0)
- Upgade a Patch Version (ex 5.3.6 -> 5.3.10)
For a major version the process will involve first making sure your code is deprecation free before upgrading.
For minor version changes you’ll upgrade the version numbers in composer.json before using composer to install the new versions.
For a patch version change you’ll use composer alone. This is because the way the Symfony packages are required in the composer.json file, they have an asterisk for the patch version which means they can be upgraded to new patch versions with no restriction: "symfony/console": "5.3.*",
for example means that you can’t upgrade to a new major or minor version without manually editing the composer.json file.
Example 1: Patch Update 5.3.6 -> 5.3.10
In my first case I’m doing a patch version update so at the root of my project I do:
composer update
This command downloads and installs patch version updates to all my dependencies. Then clears my cache.
I can confirm I have the new version the same way I did above: ./bin/console --version
. This confirms I’m now on v5.3.10.
It is also a good idea to use the Symfony security checker tool which helps you check for packages in your project with known vulnerabilities. Now is a good time to update or replace these:
symfony check:security
Example 2: Minor Update 5.3.10 -> 5.4.0-Beta1
Since I’m getting a jump on updating a project for a version that is not quite released yet, I have to tell composer to allow ‘dev’ releases (the default is ‘stable’.)
composer config minimum-stability dev
(Note: You only have to do this if the version you want is not released yet. Don’t forget to put this back to ‘stable’ once the version you’re using is officially released.)
I then go into my composer.json and update each symfony/*
package to allow "5.4.*"
.
composer update "symfony/*" --with-all-dependencies
The --with-all-dependencies
flag tells composer to also update the packages that my top-level Symfony packages rely on.
I then tested my application and also used the Symfony Debug bar to find deprecations and fix them.
Updating Recipes
Symfony Flex uses recipes which bundle several packages together. Another often-overlooked part of updating a Symfony project is to update the recipes you’ve installed. You can check your recipes and see which ones have an update available with the command:
composer recipes
I highly recommend committing your changes to your repo before and after each recipe update. The reason for this is that the recipes won’t be updated but rather re-installed, and any files you customized, like config files, will be wiped back to their default versions. Yikes! Update a recipe by running this command for each one, one at a time:
composer recipes:install symfony/framework-bundle --force -v
After the install runs you’ll have to do a git diff
and see all the changes that this made to your files. For me, I had to copy many lines of YAML config from the diff, back into my twig, framework and cache config files. Testing my application between each recipe update and committing my work before updating the next recipe.
Side note – this process also left me having to find another way to include my custom config files which I was doing in the kernel.php file (bad) and I now do via imports in the services.yaml config (good).
Updating Other Packages
If you have packages installed which are not part of the official Symfony framework, you will need to check for an update those separately.
composer outdated
The above command will list all your outdated packages with their current and available version numbers. You’ll need to visit the documentation for those packages individually and find out how and when to upgrade them. I suggest making commits to your repo before and after these, in case you update a package that is not compatible with Symfony or other packages in your project.
Test Your Application and Make Updates
Once your updates are installed you now need to perform a full test of your application to fix anything that was broken by the updates. If you have automated tests this is much easier. If you don’t, you’ll want to perform a full manual test of the application and make use of static analysis tools like PHPStan and PHP Code Sniffer. Code Sniffer can also check compatibility with versions of PHP.
Refer to the Symfony docs or package documentation for common issues you might encounter like deprecated packages. There are often notes on how to replace these items.
Once your project is stable and fixed and working with the new versions, commit and tag your repo again so that you now have before and after tags.
Good luck!