Skip to content

Upgrading a Third-Party Bundle for a Major Symfony Version

Edit this page

According to the Symfony releases plan, Symfony publishes a new major version every two years. Your third-party bundle can support more than one major version (e.g. 5.x and 6.x), but you must apply some techniques to do so, as explained in this article.

Allowing to Install New Symfony Components

Consider a bundle that requires three Symfony components, locked to version 5.4:

1
2
3
4
5
6
7
{
    "require": {
        "symfony/framework-bundle": "^5.4",
        "symfony/finder": "^5.4",
        "symfony/validator": "^5.4"
    }
}

When Symfony releases a new major version (e.g. 6.4) and an application uses it, your bundle will no longer be installable in that application. The first step is to allow that new major version in your bundle:

1
2
3
4
5
6
7
{
    "require": {
        "symfony/framework-bundle": "^5.4|^6.4",
        "symfony/finder": "^5.4|^6.4",
        "symfony/validator": "^5.4|^6.4"
    }
}

Look for Deprecations and Fix Them

After making the changes shown above, your bundle becomes installable in applications using the new major version. However, it may still fail at runtime. This happens because Symfony deprecates features in minor versions and removes them in the next major version. If your code uses deprecated features, it will break once those features are removed.

You can detect deprecations in two ways:

  1. Add the --display-deprecations option when running your tests with PHPUnit (./bin/phpunit --display-deprecations)
  2. Install the Symfony PHPUnit Bridge (composer require --dev symfony/phpunit-bridge) and run your tests using: ./vendor/bin/simple-phpunit

Fix the reported deprecations, rerun the test suite, and repeat the process until no deprecations remain.

Fixing Deprecations

Sometimes fixing a deprecation simply means using the new API instead of the deprecated one. However, in some cases, major Symfony versions introduce larger API changes that require conditional logic.

Avoid relying on the Symfony Kernel version for compatibility checks, as it doesn't reflect the version of individual components and leads to fragile, hard-to-maintain code:

1
2
3
4
5
6
// ❌ don't do this - resulting code is fragile
if (Kernel::VERSION_ID <= 50400) {
    // code for Symfony 5.x
} else {
    // code for Symfony 6.x
}

Instead, use feature-based checks, which are more accurate, robust, and forward-compatible. For example, if a new method was added to a component in a given version, check for that feature rather than the kernel version:

1
2
3
4
5
6
7
8
use Symfony\Component\OptionsResolver\OptionsResolver;

// ✅ this approach is stable across major versions
if (!method_exists(OptionsResolver::class, 'setDefined')) {
    // code for the old OptionsResolver API
} else {
    // code for the new OptionsResolver API
}

7.4

Symfony 7.4 deprecated the XML configuration format, which was the recommended format for bundles in previous versions. Consider using the gromnan/symfony-config-xml-to-php tool to automatically convert XML configuration files to PHP.

Testing your Bundle in Symfony Applications

Before publishing the new version of your bundle, test it locally in a Symfony application. You have two options:

  1. Use the Composer path repository option to make the application load the bundle from a local directory.
  2. Create a symbolic link from your bundle directory to the corresponding location inside the application's vendor/ directory:

    1
    $ ln -s /path/to/your/local/bundle/ vendor/your-vendor-name/your-bundle-name

Updating the GitHub CI Configuration

In addition to local tests, it's recommended to configure continuous integration to test your bundle against multiple Symfony versions. Use the following example as a starting point for your own GitHub CI configuration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
jobs:
    phpunit:
        strategy:
            fail-fast: false
            matrix:
                include:
                    - php_version: "7.2"
                      symfony_version: "5.4"
                      stability: "stable"
                    - php_version: "8.1"
                      symfony_version: "6.4"
                      stability: "stable"
                    # ...
        runs-on: ubuntu-latest
        continue-on-error: ${{ matrix.stability == 'dev' }}
        steps:
            - uses: actions/checkout@v5

            - name: Setup PHP, with composer and extensions
              uses: shivammathur/setup-php@v2
              with:
                  php-version: ${{ matrix.php_version }}
                  coverage: none
                  extensions: mbstring, intl, pdo
                  ini-values: date.timezone=UTC

            - name: symfony/flex is required to install the correct symfony version
              if: ${{ matrix.symfony_version }}
              run: |
                  composer global config --no-plugins allow-plugins.symfony/flex true
                  composer global require symfony/flex

            - name: Configure Composer stability
              run: |
                  composer config minimum-stability ${{ matrix.stability }}

            - name: Configure Symfony version for symfony/flex
              if: ${{ matrix.symfony_version }}
              run: composer config extra.symfony.require "${{ matrix.symfony_version }}.*"

            - name: Install dependencies
              run: |
                  composer update ${{ matrix.composer_args }};
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version