Upgrading a Third-Party Bundle for a Major Symfony Version

Symfony 3 was released on November 2015. Although this version doesn't contain
any new features, it removes all the backward compatibility layers included in
the previous 2.8 version. If your bundle uses any deprecated feature and it's
published as a third-party bundle, applications upgrading to Symfony 3 will no
longer be able to use it.

These constraints prevent the bundle from using Symfony 3 components, so it makes
it impossible to install it in a Symfony 3 based application. This issue is very
easy to solve thanks to the flexibility of Composer dependencies constraints.
Just replace ~2.N by ~2.N|~3.0 (or ^2.N by ^2.N|~3.0).

Another common version constraint found on third-party bundles is >=2.N.
You should avoid using that constraint because it's too generic (it means
that your bundle is compatible with any future Symfony version). Use instead
~2.N|~3.0 or ^2.N|~3.0 to make your bundle future-proof.

Besides allowing users to use your bundle with Symfony 3, your bundle must stop using
any feature deprecated by the 2.8 version because they are removed in 3.0 (you'll get
exceptions or PHP errors). The easiest way to detect deprecations is to install
the symfony/phpunit-bridge package and then run the test suite.

First, install the component as a dev dependency of your bundle:

1

$ composer require --dev symfony/phpunit-bridge

Then, run your test suite and look for the deprecation list displayed after the
PHPUnit test report:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

# this command is available after running "composer require --dev symfony/phpunit-bridge"$ ./bin/phpunit
# ... PHPUnit outputRemaining deprecation notices (3)The "pattern" option in file ... is deprecated since version 2.2 and will beremoved in 3.0. Use the "path" option in the route definition instead ...Twig Function "form_enctype" is deprecated. Use "form_start" instead in ...The Symfony\Component\Security\Core\SecurityContext class is deprecated sinceversion 2.6 and will be removed in 3.0. Use ...

Fix the reported deprecations, run the test suite again and repeat the process
until no deprecation usage is reported.

It runs a static code analysis against your project's source code to find
usages of deprecated methods, classes and interfaces. It works for any PHP
application, but it includes special detectors for Symfony applications,
where it can also detect usages of deprecated services.

Now that your bundle has removed all deprecations, it's time to test it for real
in a Symfony 3 application. Assuming that you already have a Symfony 3 application,
you can test the updated bundle locally without having to install it through
Composer.

If your operating system supports symbolic links, just point the appropriate
vendor directory to your local bundle root directory:

In addition to running tools locally, it's recommended to set-up Travis CI service
to run the tests of your bundle using different Symfony configurations. Use the
following recommended configuration as the starting point of your own configuration:

The real challenge of adding Symfony 3 support for your bundles is when you want
to support both Symfony 2.x and 3.x simultaneously using the same code. There
are some edge cases where you'll need to deal with the API differences.

Before diving into the specifics of the most common edge cases, the general
recommendation is to not rely on the Symfony Kernel version to decide which
code to use:

Instead of checking the Symfony Kernel version, check the version of the specific
component. For example, the OptionsResolver API changed in its 2.6 version by
adding a setDefined() method. The recommended check in this case would be:

1
2
3
4
5
6
7

useSymfony\Component\OptionsResolver\OptionsResolver;if(!method_exists(OptionsResolver::class,'setDefined')){// code for the old OptionsResolver API}else{// code for the new OptionsResolver API}

Tip

There is one case when you actually can rely on the
Symfony\Component\HttpKernel\Kernel::VERSION_ID constant: when trying
to detect the version of the symfony/http-kernel component, because it
is the component where this constant is defined.