Skip to content

Commit ba76bca

Browse files
committed
Revamped the article about updating bundles for new Symfony majors
1 parent dd39eab commit ba76bca

File tree

1 file changed

+113
-156
lines changed

1 file changed

+113
-156
lines changed

setup/bundles.rst

Lines changed: 113 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,201 +1,158 @@
11
Upgrading a Third-Party Bundle for a Major Symfony Version
22
==========================================================
33

4-
Symfony 3 was released in November 2015. Although this version doesn't contain
5-
any new features, it removes all the backward compatibility layers included in
6-
the previous 2.8 version. If your bundle uses any deprecated feature and it's
7-
published as a third-party bundle, applications upgrading to Symfony 3 will no
8-
longer be able to use it.
4+
According to the `Symfony releases plan`_, Symfony publishes a new major version
5+
every two years. Your third-party bundle can support more than one major version
6+
(e.g. 5.x and 6.x), but you must apply some techniques to do so, as explained in
7+
this article.
98

10-
Allowing to Install Symfony 3 Components
11-
----------------------------------------
9+
Allowing to Install New Symfony Components
10+
------------------------------------------
1211

13-
Most third-party bundles define their Symfony dependencies using the ``~2.N`` or
14-
``^2.N`` constraints in the ``composer.json`` file. For example:
12+
Consider a bundle that requires three Symfony components, locked to version ``5.4``:
1513

1614
.. code-block:: json
1715
1816
{
1917
"require": {
20-
"symfony/framework-bundle": "~2.7",
21-
"symfony/finder": "~2.7",
22-
"symfony/validator": "~2.7"
18+
"symfony/framework-bundle": "^5.4",
19+
"symfony/finder": "^5.4",
20+
"symfony/validator": "^5.4"
2321
}
2422
}
2523
26-
These constraints prevent the bundle from using Symfony 3 components, which
27-
means the bundle cannot be installed in a Symfony 3 based application. Thanks to the
28-
flexibility of Composer dependencies constraints, you can specify more than one
29-
major version by replacing ``~2.N`` by ``~2.N|~3.0`` (or ``^2.N`` by ``^2.N|~3.0``).
30-
31-
The above example can be updated to work with Symfony 3 as follows:
24+
When Symfony releases a new major version (e.g. ``6.4``) and an application uses
25+
it, your bundle will no longer be installable in that application. The first
26+
step is to allow that new major version in your bundle:
3227

3328
.. code-block:: json
3429
3530
{
3631
"require": {
37-
"symfony/framework-bundle": "~2.7|~3.0",
38-
"symfony/finder": "~2.7|~3.0",
39-
"symfony/validator": "~2.7|~3.0"
32+
"symfony/framework-bundle": "^5.4|^6.4",
33+
"symfony/finder": "^5.4|^6.4",
34+
"symfony/validator": "^5.4|^6.4"
4035
}
4136
}
4237
43-
.. tip::
44-
45-
Another common version constraint found on third-party bundles is ``>=2.N``.
46-
You should avoid using that constraint because it's too generic (it means
47-
that your bundle is compatible with any future Symfony version). Use instead
48-
``~2.N|~3.0`` or ``^2.N|~3.0`` to make your bundle future-proof.
49-
5038
Look for Deprecations and Fix Them
5139
----------------------------------
5240

53-
Besides allowing users to use your bundle with Symfony 3, your bundle must stop using
54-
any feature deprecated by the 2.8 version because they are removed in 3.0 (you'll get
55-
exceptions or PHP errors). The easiest way to detect deprecations is to install
56-
the `symfony/phpunit-bridge package`_ and then run the test suite.
57-
58-
First, install the component as a ``dev`` dependency of your bundle:
59-
60-
.. code-block:: terminal
61-
62-
$ composer require --dev symfony/phpunit-bridge
63-
64-
Then, run your test suite and look for the deprecation list displayed after the
65-
PHPUnit test report:
66-
67-
.. code-block:: terminal
68-
69-
# this command is available after running "composer require --dev symfony/phpunit-bridge"
70-
$ ./bin/phpunit
71-
72-
# ... PHPUnit output
73-
74-
Remaining deprecation notices (3)
75-
76-
The "pattern" option in file ... is deprecated since version 2.2 and will be
77-
removed in 3.0. Use the "path" option in the route definition instead ...
78-
79-
Twig Function "form_enctype" is deprecated. Use "form_start" instead in ...
80-
81-
The Symfony\Bundle\SecurityBundle\SecurityContext class is deprecated since
82-
version 2.6 and will be removed in 3.0. Use ...
83-
84-
Fix the reported deprecations, run the test suite again and repeat the process
85-
until no deprecation usage is reported.
41+
After making the changes shown above, your bundle becomes installable in
42+
applications using the new major version. However, it may still fail at runtime.
43+
This happens because Symfony deprecates features in minor versions and removes
44+
them in the next major version. If your code uses deprecated features, it will
45+
break once those features are removed.
8646

87-
Useful Resources
88-
~~~~~~~~~~~~~~~~
47+
You can **detect deprecations** in two ways:
8948

90-
There are several resources that can help you detect, understand and fix the use
91-
of deprecated features:
49+
#. Add the ``--display-deprecations`` option when running your tests with PHPUnit
50+
(``./bin/phpunit --display-deprecations``)
51+
#. Install the :doc:`Symfony PHPUnit Bridge </components/phpunit_bridge>`
52+
(``composer require --dev symfony/phpunit-bridge``) and run your tests using:
53+
``./vendor/bin/simple-phpunit``
9254

93-
`Official Symfony Guide to Upgrade from 2.x to 3.0`_
94-
The full list of changes required to upgrade to Symfony 3.0 and grouped
95-
by component.
96-
`SensioLabs DeprecationDetector`_
97-
It runs a static code analysis against your project's source code to find
98-
usages of deprecated methods, classes and interfaces. It works for any PHP
99-
application, but it includes special detectors for Symfony applications,
100-
where it can also detect usages of deprecated services.
101-
`Symfony Upgrade Fixer`_
102-
It analyzes Symfony projects to find deprecations. In addition it solves
103-
automatically some of them thanks to the growing list of supported "fixers".
55+
Fix the reported deprecations, rerun the test suite, and repeat the process
56+
until no deprecations remain.
10457

105-
Testing your Bundle in Symfony 3
106-
--------------------------------
58+
Fixing Deprecations
59+
~~~~~~~~~~~~~~~~~~~
10760

108-
Now that your bundle has removed all deprecations, it's time to test it for real
109-
in a Symfony 3 application. Assuming that you already have a Symfony 3 application,
110-
you can test the updated bundle locally without having to install it through
111-
Composer.
61+
Sometimes fixing a deprecation simply means using the new API instead of the
62+
deprecated one. However, in some cases, major Symfony versions introduce larger
63+
API changes that require conditional logic.
11264

113-
If your operating system supports symbolic links, instead point the appropriate
114-
vendor directory to your local bundle root directory:
65+
**Avoid relying on the Symfony Kernel version for compatibility checks**, as it
66+
doesn't reflect the version of individual components and leads to fragile,
67+
hard-to-maintain code::
11568

116-
.. code-block:: terminal
117-
118-
$ ln -s /path/to/your/local/bundle/ vendor/you-vendor-name/your-bundle-name
119-
120-
If your operating system doesn't support symbolic links, you'll need to copy
121-
your local bundle directory into the appropriate directory inside ``vendor/``.
122-
123-
Update the Travis CI Configuration
124-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
125-
126-
In addition to running tools locally, it's recommended to set-up Travis CI service
127-
to run the tests of your bundle using different Symfony configurations. Use the
128-
following recommended configuration as the starting point of your own configuration:
129-
130-
.. code-block:: yaml
131-
132-
language: php
133-
php:
134-
- 5.3
135-
- 5.6
136-
- 7.0
137-
138-
matrix:
139-
include:
140-
- php: 5.3.3
141-
env: COMPOSER_FLAGS='--prefer-lowest --prefer-stable' SYMFONY_DEPRECATIONS_HELPER=max[total]=999999
142-
- php: 5.6
143-
env: SYMFONY_VERSION='2.7.*'
144-
- php: 5.6
145-
env: SYMFONY_VERSION='2.8.*'
146-
- php: 5.6
147-
env: SYMFONY_VERSION='3.0.*'
148-
- php: 5.6
149-
env: SYMFONY_VERSION='3.1.*'
150-
- php: 5.6
151-
env: DEPENDENCIES='dev' SYMFONY_VERSION='3.2.*@dev'
152-
153-
before_install:
154-
- composer self-update
155-
- if [ "$DEPENDENCIES" == "dev" ]; then perl -pi -e 's/^}$/,"minimum-stability":"dev"}/' composer.json; fi;
156-
- if [ "$SYMFONY_VERSION" != "" ]; then composer --no-update require symfony/symfony:${SYMFONY_VERSION}; fi;
157-
158-
install: composer update $COMPOSER_FLAGS
159-
160-
script: phpunit
161-
162-
Updating your Code to Support Symfony 2.x and 3.x at the Same Time
163-
------------------------------------------------------------------
164-
165-
The real challenge of adding Symfony 3 support for your bundles is when you want
166-
to support both Symfony 2.x and 3.x simultaneously using the same code. There
167-
are some edge cases where you'll need to deal with the API differences.
168-
169-
Before diving into the specifics of the most common edge cases, the general
170-
recommendation is to **not rely on the Symfony Kernel version** to decide which
171-
code to use::
172-
173-
if (Kernel::VERSION_ID < 20800) {
174-
// code for Symfony 2.x
69+
// ❌ don't do this - resulting code is fragile
70+
if (Kernel::VERSION_ID <= 50400) {
71+
// code for Symfony 5.x
17572
} else {
176-
// code for Symfony 3.x
73+
// code for Symfony 6.x
17774
}
17875

179-
Instead of checking the Symfony Kernel version, check the version of the specific
180-
component. For example, the OptionsResolver API changed in its 2.6 version by
181-
adding a ``setDefined()`` method. The recommended check in this case would be::
76+
Instead, **use feature-based checks**, which are more accurate, robust, and
77+
forward-compatible. For example, if a new method was added to a component in a
78+
given version, check for that feature rather than the kernel version::
18279

18380
use Symfony\Component\OptionsResolver\OptionsResolver;
18481

82+
// ✅ this approach is stable across major versions
18583
if (!method_exists(OptionsResolver::class, 'setDefined')) {
18684
// code for the old OptionsResolver API
18785
} else {
18886
// code for the new OptionsResolver API
18987
}
19088

191-
.. tip::
89+
Testing your Bundle in Symfony Applications
90+
-------------------------------------------
19291

193-
There is one case when you actually can rely on the
194-
``Symfony\Component\HttpKernel\Kernel::VERSION_ID`` constant: when trying
195-
to detect the version of the ``symfony/http-kernel`` component, because it
196-
is the component where this constant is defined.
92+
Before publishing the new version of your bundle, test it locally in a Symfony
93+
application. You have two options:
94+
95+
#. Use the `Composer path repository option`_ to make the application load the
96+
bundle from a local directory.
97+
#. Create a symbolic link from your bundle directory to the corresponding
98+
location inside the application's ``vendor/`` directory:
99+
100+
.. code-block:: terminal
101+
102+
$ ln -s /path/to/your/local/bundle/ vendor/your-vendor-name/your-bundle-name
103+
104+
Updating the GitHub CI Configuration
105+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
106+
107+
In addition to local tests, it's recommended to configure continuous integration
108+
to test your bundle against multiple Symfony versions. Use the following example
109+
as a starting point for your own GitHub CI configuration:
110+
111+
.. code-block:: yaml
197112
198-
.. _`symfony/phpunit-bridge package`: https://github.com/symfony/phpunit-bridge
199-
.. _`Official Symfony Guide to Upgrade from 2.x to 3.0`: https://github.com/symfony/symfony/blob/2.8/UPGRADE-3.0.md
200-
.. _`SensioLabs DeprecationDetector`: https://github.com/sensiolabs-de/deprecation-detector
201-
.. _`Symfony Upgrade Fixer`: https://github.com/umpirsky/Symfony-Upgrade-Fixer
113+
jobs:
114+
phpunit:
115+
strategy:
116+
fail-fast: false
117+
matrix:
118+
include:
119+
- php_version: "7.2"
120+
symfony_version: "5.4"
121+
stability: "stable"
122+
- php_version: "8.1"
123+
symfony_version: "6.4"
124+
stability: "stable"
125+
# ...
126+
runs-on: ubuntu-latest
127+
continue-on-error: ${{ matrix.stability == 'dev' }}
128+
steps:
129+
- uses: actions/checkout@v5
130+
131+
- name: Setup PHP, with composer and extensions
132+
uses: shivammathur/setup-php@v2
133+
with:
134+
php-version: ${{ matrix.php_version }}
135+
coverage: none
136+
extensions: mbstring, intl, pdo
137+
ini-values: date.timezone=UTC
138+
139+
- name: symfony/flex is required to install the correct symfony version
140+
if: ${{ matrix.symfony_version }}
141+
run: |
142+
composer global config --no-plugins allow-plugins.symfony/flex true
143+
composer global require symfony/flex
144+
145+
- name: Configure Composer stability
146+
run: |
147+
composer config minimum-stability ${{ matrix.stability }}
148+
149+
- name: Configure Symfony version for symfony/flex
150+
if: ${{ matrix.symfony_version }}
151+
run: composer config extra.symfony.require "${{ matrix.symfony_version }}.*"
152+
153+
- name: Install dependencies
154+
run: |
155+
composer update ${{ matrix.composer_args }};
156+
157+
.. _`https://symfony.com/releases`: https://symfony.com/releases
158+
.. _`Composer path repository option`: https://getcomposer.org/doc/05-repositories.md#path

0 commit comments

Comments
 (0)