Switch from deprecated --set
option to rector.php
config.
A month ago, Symfony 5 has been released. Upgrading of such a small web as our community website must be easy, right?
Well, that's what we thought. Were we right or wrong?
This post is based on the real problems we faced when we upgraded our website. It is full of experience with pieces of explanation, real code snippets in diffs, painful frustration of Symfony ecosystem and bright light at the end of the tunnel.
Are you ready? Let's dive in ↓
Before, you could connect for
with if
like this:
{% for post in posts if post.isPublic() %}
{{ post.title }}
{% endfor %}
Now the filter
has to be used:
{% for post in posts|filter(post => post.isPublic()) %}
{{ post.title }}
{% endfor %}
Thanks Patrik for the tip
Do you want to know, what is needed for the upgrade to Symfony 5? Just read upgrade notes in Symfony repository.
# install Rector
composer require rector/rector --dev
# or in case of conflicts
composer require rector/rector-prefixed --dev
Rector has minimal sets, meaning each minor version is standalone and independent. What does that mean? For upgrading from Symfony 4 to 5, you need to run all the minor version sets, one by one.
rector.php
configuse Rector\Symfony\Set\SymfonySetList;
use Rector\Config\RectorConfig;
return function (RectorConfig $rectorConfig): void {
$rectorConfig->import(SymfonySetList::SYMFONY_41);
// take it 1 set at a time, so next set works with output of the previous set; I do 1 set per pull-request
// $rectorConfig->import(SymfonySetList::SYMFONY_42);
// $rectorConfig->import(SymfonySetList::SYMFONY_43);
// $rectorConfig->import(SymfonySetList::SYMFONY_44);
// $rectorConfig->import(SymfonySetList::SYMFONY_50);
};
vendor/bin/rector process app src tests
Verify, check that CI passes and then continue with next Symfony minor version.
composer.json
before composer update
{
"require": {
- "alterphp/easyadmin-extension-bundle": "^2.1",
+ "alterphp/easyadmin-extension-bundle": "^3.0",
}
}
{
"require": {
- "doctrine/cache": "^1.8",
+ "doctrine/cache": "^1.10",
- "doctrine/doctrine-bundle": "^1.11",
+ "doctrine/doctrine-bundle": "^2.0",
- "doctrine/orm": "^2.6",
+ "doctrine/orm": "^2.7",
}
}
{
"require": {
- "stof/doctrine-extensions-bundle": "^1.3",
- "knplabs/doctrine-behaviors": "^1.6"
+ "knplabs/doctrine-behaviors": "^2.0"
}
}
{
"require": {
- "sentry/sentry-symfony": "^3.2",
+ "sentry/sentry-symfony": "^3.4",
}
}
{
"require": {
- "twig/extensions": "^1.5"
}
}
This might be scary at first, depends on how many of those functions have you used.
Look at the README on Github to find out more:
composer.json
Do you use Flex and config *
version?
{
"require": {
"symfony/console": "*",
"symfony/event-disptacher": "*"
},
"extra": {
"symfony": {
"require": "^4.4"
}
}
}
Not sure why, but in some cases, it failed and blocked from the upgrading. I had to switch to explicit version per package, to resolve it:
{
"require": {
- "symfony/console": "*",
+ "symfony/console": "^4.4",
- "symfony/event-disptacher": "*"
+ "symfony/event-disptacher": "^4.4"
- },
+ }
- "extra": {
- "symfony": {
- "require": "^4.4"
- }
- }
}
Then switch to Symfony 5:
-"symfony/asset": "^4.4",
+"symfony/asset": "^5.0",
-"symfony/console": "^4.4",
+"symfony/console": "^5.0",
// etc.
But some packages are released out of monorepo cycle:
-"symfony/maker-bundle": "^1.14",
+"symfony/maker-bundle": "^1.13",
All right, now you run...
composer update
...and get new packages with Symfony 5... or probably a lot of composer version conflicts.
In Symfony 5, some packages were removed:
-"symfony/error-renderer": "^4.4",
-"symfony/web-server-bundle": "^4.4",
Some packages were replaced by new ones:
-"symfony/error-debug": "^4.4",
+"symfony/error-handler": "^5.0",
And some package were split into more smaller ones:
-"symfony/security": "^4.4",
+"symfony/security-core": "^5.0",
+"symfony/security-http": "^5.0",
+"symfony/security-csrf": "^5.0",
+"symfony/security-guard": "^5.0",
These were production dependencies, but what about dev ones? Both have the same rules - they need to allow Symfony 5 installation.
The safest way is to use prefixed versions, which don't care about a Symfony version:
-"phpstan/phpstan": "^0.11",
+"phpstan/phpstan": "^0.12",
-"rector/rector": "^0.5",
+"rector/rector-prefixed": "^0.6",
-"symplify/easy-coding-standard": "^0.11",
+"symplify/easy-coding-standard": "^0.12",
Update your composer.json
to include a package that you need.
Then run:
composer update
Still conflicts?
If you don't do open-source, you probably don't use the git tag
feature. It seems that the tagging of a package is a very difficult process. Even packages with million downloads/month had the latest 15 months ago.
Let's say you want to use symplify/easy-coding-standard
that supports Symfony 5. Here is the deal:
symplify/easy-coding-standard
version 6 doesn't support itsymplify/easy-coding-standard
dev-master (~= what you see on GitHub) supports it{
"require-dev": {
"symplify/easy-coding-standard": "dev-master"
},
"prefer-stable": true,
"minimum-stability": "dev"
}
Now imagine one of your package you require requires some other package, that requires another package, that doesn't allow Symfony 5 in tagged version, but in master
. Well, you've just finished.
That's why it's very important to know to tag a package regularly:
git tag v2.0.0
git push --tags
That's all! Still, many packages support Symfony 5 in the master but are not tagged yet... to be true, not once for the last 2 years. Why? The human factor, maintainers are afraid of negative feedback, of back-compatibility breaks, lack of test coverage takes their confidence, etc.
These packages block Symfony 5 upgrade for months:
This will be resolved in the future by an open-source monorepo approach, but we're not there yet.
In the meantime, please complain at issues, ask for help and offer to become maintainer until it changes (or until somebody forks it and tags the fork).
One good example for all - I complained and offered help at knplabs/doctrine-behaviors
, got maintainer rights in 3 hours and made + merged 30 pull-request in the last month.
You see, it works :)
Ok, so you have the right version of packages, everything is stable and allows Symfony 5. Yet still, the composer says "conflicts, cannot install x/y".
To my surprise, the composer is very bad at solving complex conflicts. Composer reports false positive and blocks your install, because of installing packages in /vendor
or overly strict composer.lock
. I spent 30-60 minutes trying to figure out what the heck is conflicting on every Symfony training I had in the last 2 months. Now I'm able to do it in 3 minutes.
How?
/vendor
composer.lock
composer update
It works so well I do it more often than resolving conflicts manually.
bundles.php
return [
- Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true],
];
Switch the dead gedmo/stof doctrine extensions for the maintained KnpLabs/DoctrineBehaviors. I'll write a standalone post about this migration, once a stable version is out (check me, pls :)).
return [
- Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true],
];
We also had some troubles with Switfmailer Bundle:
return [
- Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true],
]
The Mailer component will take over Swiftmailer in the future, so this is just a start.
config/packages
# config/packages/framework.yaml
framework:
...
- templating:
- engines: ["twig"]
Don't forget to remove all extension configs. In our case it was:
-config/packages/stof_doctrine_extensions.yaml
-config/packages/swiftmailer.yaml
-config/packages/dev/swiftmailer.yaml
-config/packages/test/swiftmailer.yaml
-config/packages/twig_extensions.yaml
-config/routes/dev/twig.yaml
Small update of the EasyAdmin bundle:
# config/routes/easy_admin.yaml
easy_admin_bundle:
- resource: '@EasyAdminBundle/Controller/AdminController.php'
+ resource: '@EasyAdminBundle/Controller/EasyAdminController.php'
And that's all folks!
Got any questions?
<br>
<img src="/assets/images/posts/2019/symfony5_pr.png" class="img-thumbnail">
<br>
Feel free to explore it, ask, read comments or share your problems.
<br>
<a href="https://github.com/pehapkari/pehapkari.cz/pull/243/files" class="btn btn-dark mb-5 mt-3">
Check the PR on Github
</a>
Of course, we did! Every application has a different set of blocking dependencies and different sets of used Symfony features that might have changed.
Share your issues in comments or edit this post on Github to make list complete!
Happy coding!
Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors.
I'd really appreciate it!