Archives


Master Symfony2 fundamentals

Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).
trainings.sensiolabs.com

Discover the SensioLabs Support

Access to the SensioLabs Competency Center for an exclusive and tailor-made support on Symfony
sensiolabs.com

Fabien Potencier
Upgrading your Symfony Projects the easy Way
by Fabien Potencier – June 12, 2013 – 12 comments

In the old Symfony 2.0.x days, at a time when Composer did not exist, upgrading a project from one minor version of Symfony to the next one was not that easy. You had to merge the changes that were made in the deps and the deps.lock files into yours. The diff was made of changes from Symfony itself, but also from "core" Symfony bundles that were included in the Standard Edition. And of course, if you had other third-party dependencies, you had to upgrade them by yourself (finding the right version to use for each of them). It worked well even if merging things was sometimes a nightmare, but it was unfortunately possible to install dependencies that did not work well together.

Fast forward to the Composer world.

Composer is used by Symfony since version 2.1, but as we were the very first adopter, and because Composer and the Symfony ecosystem were not stable yet, it was still awkward. The recommended minimum stability was dev, and as such, whenever you ran composer update, you got the bleeding edge of your dependencies. In a way to reduce the pain, we provided the composer.lock file for the Standard Edition... which you could use when upgrading your project. But again, if you had other dependencies, merging the locked deps was a pain. We were basically using Composer as the old deps files, except that Composer would ensure that all dependency versions worked well together.

Fast forward to Symfony 2.3.

As of Symfony 2.3, the minimum stability level has been raised to stable. That was possible because the Symfony ecosystem as a whole is much more mature and because almost all good/useful/maintained/popular bundles have stable releases with a good configuration for their dependencies. If you know of a cool bundle or PHP library that still requires a dev minimum stability, talk to the lead developer and convince him to tag a stable release.

Because we are now using stable versions of all dependencies by default, we do not need to keep around a composer.lock file as a template. That's why the diff between two versions of the Symfony Standard Edition can be empty... which is disturbing but totally fine.

So, what is the workflow to upgrade your Symfony 2.3 projects? This is ridiculously simple, so simple that some developers are lost! Let me explain the upgrade workflow for Symfony 2.3+:

1
$ composer update

That's all there is to it. No need to do anything else. And of course, the resulting composer.lock file should be stored in your project repository so that all developers working with you on the project will install the exact same versions of the projects dependencies.

Notice that there is nothing related to Symfony here. So, upgrading from one minor version of Symfony to the next one is no different than upgrading any other dependencies.

You don't need to track changes or upgrades. And because Symfony 2.3 is a Long-Term Support release with a strong "Don't break backward compatibility" motto, you can run composer update with confidence. Symfony is just another dependency of your project, like any other ones.

That's the short explanation and you can stop here. If you want to learn more, keep reading while I show you how it works in practice.

Let's say that you created a new Symfony 2.3.0 project last week with the following command:

1
$ composer create-project -n symfony/framework-standard-edition path/ 2.3.0

When running this command, Composer creates a new path/ directory, where it downloads the Symfony Standard Edition version 2.3.0. It then installs all the dependencies referenced in the composer.json file by running composer install:

Installing symfony/framework-standard-edition (v2.3.0)
  - Installing symfony/framework-standard-edition (v2.3.0)
    Loading from cache

Created project in path/
Loading composer repositories with package information
Installing dependencies (including require-dev)
  - Installing jdorn/sql-formatter (v1.2.9)
    Loading from cache

  - Installing doctrine/lexer (v1.0)
    Loading from cache

  - Installing doctrine/annotations (v1.1.1)
    Loading from cache

  - Installing doctrine/collections (v1.1)
    Loading from cache

  - Installing doctrine/cache (v1.0)
    Loading from cache

  - Installing doctrine/inflector (v1.0)
    Loading from cache

  - Installing doctrine/common (2.4.0-RC3)
    Loading from cache

  - Installing doctrine/dbal (2.3.4)
    Loading from cache

  - Installing psr/log (1.0.0)
    Loading from cache

  - Installing twig/twig (v1.13.1)
    Loading from cache

  - Installing symfony/icu (v1.2.0)
    Loading from cache

  - Installing symfony/symfony (v2.3.0)
    Loading from cache

  - Installing doctrine/doctrine-bundle (v1.2.0)
    Loading from cache

  - Installing twig/extensions (v1.0.0)
    Loading from cache

  - Installing kriswallsmith/assetic (v1.1.1)
    Loading from cache

  - Installing symfony/assetic-bundle (v2.3.0)
    Loading from cache

  - Installing swiftmailer/swiftmailer (v5.0.0)
    Loading from cache

  - Installing symfony/swiftmailer-bundle (v2.3.0)
    Loading from cache

  - Installing monolog/monolog (1.5.0)
    Loading from cache

  - Installing symfony/monolog-bundle (v2.3.0)
    Loading from cache

  - Installing sensio/distribution-bundle (v2.3.0)
    Loading from cache

  - Installing sensio/framework-extra-bundle (v2.3.0)
    Loading from cache

  - Installing sensio/generator-bundle (v2.3.0)
    Loading from cache

  - Installing incenteev/composer-parameter-handler (v2.0.0)
    Loading from cache

  - Installing doctrine/orm (2.3.4)
    Loading from cache

    Skipped installation of bin/doctrine for package doctrine/orm: name conflicts with an existing file
    Skipped installation of bin/doctrine.php for package doctrine/orm: name conflicts with an existing file
kriswallsmith/assetic suggests installing leafo/lessphp (Assetic provides the integration with the lessphp LESS compiler)
kriswallsmith/assetic suggests installing leafo/scssphp (Assetic provides the integration with the scssphp SCSS compiler)
kriswallsmith/assetic suggests installing ptachoire/cssembed (Assetic provides the integration with phpcssembed to embed data uris)
kriswallsmith/assetic suggests installing leafo/scssphp-compass (Assetic provides the integration with the SCSS compass plugin)
monolog/monolog suggests installing mlehner/gelf-php (Allow sending log messages to a GrayLog2 server)
monolog/monolog suggests installing raven/raven (Allow sending log messages to a Sentry server)
monolog/monolog suggests installing doctrine/couchdb (Allow sending log messages to a CouchDB server)
monolog/monolog suggests installing ext-amqp (Allow sending log messages to an AMQP server (1.0+ required))
monolog/monolog suggests installing ext-mongo (Allow sending log messages to a MongoDB server)
Writing lock file
Generating autoload files
Creating the "app/config/parameters.yml" file.
Clearing the cache for the dev environment with debug true
Installing assets using the hard copy option
Installing assets for Symfony\Bundle\FrameworkBundle into web/bundles/framework
Installing assets for Acme\DemoBundle into web/bundles/acmedemo
Installing assets for Sensio\Bundle\DistributionBundle into web/bundles/sensiodistribution

As you can see in the output, Composer installed Symfony version 2.3.0, and it used the same version for all the Symfony bundles lister in the Composer file.

Now, to upgrade to Symfony 2.3.1, you can run the following command:

1
$ composer update symfony/symfony

And the output will be something along the lines of:

Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Removing symfony/symfony (v2.3.0)
  - Installing symfony/symfony (v2.3.1)
    Loading from cache

Writing lock file
Generating autoload files
Updating the "app/config/parameters.yml" file.
Clearing the cache for the dev environment with debug true
Installing assets using the hard copy option
Installing assets for Symfony\Bundle\FrameworkBundle into web/bundles/framework
Installing assets for Acme\DemoBundle into web/bundles/acmedemo
Installing assets for Sensio\Bundle\DistributionBundle into web/bundles/sensiodistribution

As you can see, Composer updated the symfony/symfony package only. That's because the Symfony bundles are configured as being compatible with any version of Symfony 2.3, so updating them to 2.3.1 was not required.

But if I had run the update command without any arguments:

1
$ composer update

Composer would have updated all dependencies to their latest stable versions:

Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Removing symfony/symfony (v2.3.0)
  - Installing symfony/symfony (v2.3.1)
    Loading from cache

  - Removing symfony/swiftmailer-bundle (v2.3.0)
  - Installing symfony/swiftmailer-bundle (v2.3.1)
    Loading from cache

  - Removing sensio/distribution-bundle (v2.3.0)
  - Installing sensio/distribution-bundle (v2.3.1)
    Loading from cache

  - Removing sensio/framework-extra-bundle (v2.3.0)
  - Installing sensio/framework-extra-bundle (v2.3.1)
    Loading from cache

  - Removing sensio/generator-bundle (v2.3.0)
  - Installing sensio/generator-bundle (v2.3.1)
    Loading from cache

Writing lock file
Generating autoload files
Updating the "app/config/parameters.yml" file.
Clearing the cache for the dev environment with debug true
Installing assets using the hard copy option
Installing assets for Symfony\Bundle\FrameworkBundle into web/bundles/framework
Installing assets for Acme\DemoBundle into web/bundles/acmedemo
Installing assets for Sensio\Bundle\DistributionBundle into web/bundles/sensiodistribution

Note

Actually, I cheated a bit when writing this article. When you ask Composer to install the Symfony Standard Edition at version 2.3.0, it downloads the package and the run composer install. But it then installed Symfony version 2.3.1 instead of 2.3.0, why? First, even if the versions are the same, there is no relation between version 2.3.0 of Symfony and version 2.3.0 of the Standard Edition. It just happens that we synchronize our releases for simplicity (something that we might stop doing at some point). Then, because the Standard Edition does not come with a composer.lock file, because I ran this command after version 2.3.1 was released, and because the constraint in the composer.json file is 2.3.*, Composer actually installed Symfony version 2.3.1, which was the latest stable version that matched the constraint in the composer file.

So, when adding a dependency to your project (be it Symfony, a Symfony component, a Symfony bundle, or any other PHP library for that matter), you have several options:

  • Be conservative and force one specific version: "symfony/symfony": "2.3.0";
  • Be restrictive and force a range of versions: "symfony/symfony": ">=2.3.0,<2.4-dev";
  • Be confident and use any future stable versions: "symfony/symfony": "~2.3".

The last option is only recommended if the dependency follows the semantic versioning scheme and if you trust the developer update policy (never break backward compatibility in a minor version).

To sum up, here are my golden rules:

  • Use stable as the minimum stability level in your composer.json file (use the @dev suffix as a suffix if one of your dependency is not stable yet);
  • Define good version constraints in your composer.json file (see the three possible choices above);
  • Run composer update with confidence whenever you want to upgrade your project dependencies to their latest stable version.

Comments RSS

  • Rémi Alvado
    #1 Rémi Alvado said on the 2013/06/12 at 13:56
    I migrate most of my applications from Symfony 2.2 to Symfony 2.3 in just a matter of minutes. Really easy to do !
    Only issue I encountered (but no related to Symfony per se) : I have a depencendy to an internally developed bundle which depends itself on another internally developed bundle (A => B: dev-master@dev => C: dev-master@dev). Composer is not designed to manage this kind of dependency : it add to do this :
    . A => B: dev-master@dev => C: dev-master
    . A => C: @dev (without the version, only the modifier).

    Good to know when you are working with a dependency graph of private modules :)
  • Christophe Coevoet
    #2 Christophe Coevoet said on the 2013/06/12 at 17:33
    I would not recommend the conservative way: it requires editing the composer.json file to get bug fix releases whereas we are keeping BC in them (breaking BC in a bug fix release should be a reason to stop using a library IMO).
    Using such a constraint means you are more likely to keep using a buggy version (and it is even worse when the bugfix release contains a security fix)

    Regarding the restrictive versio, the upper bound should be <2.4-dev (without the period after the 4, otherwise it cannot be parsed by composer)
  • Nicolas Sauveur
    #3 Nicolas Sauveur said on the 2013/06/12 at 22:36
    Thanks Fabien ! This article is so clear and informative, it should be added to the cookbook.
  • Nicolas Sauveur
    #4 Nicolas Sauveur said on the 2013/06/12 at 22:50
    Any reason not to use "2.3.*" instead of ">=2.3.0,<2.4-dev" ?
  • Benoît Merlet
    #5 Benoît Merlet said on the 2013/06/12 at 23:16
    If I am not mistaken, the restrictive version can also be: "symfony/symfony": "~2.3.0";
  • Luis Cordova
    #6 Luis Cordova said on the 2013/06/13 at 09:01
    thanks fabien

    Nicolas i think you cannot get 2.3.9-dev with 2.3.*?
  • Wouter De Jong
    #7 Wouter De Jong said on the 2013/06/14 at 08:55
    >=2.3.0,<2.4-dev can also be 2.3.* and ~2.3.0:

    > "Wildcard: You can specify a pattern with a * wildcard. 1.0.* is the equivalent of >=1.0,<1.1"

    > "Next Significant Release (Tilde Operator): The ~ operator is best explained by example: ~1.2 is equivalent to >=1.2,<2.0, while ~1.2.3 is equivalent to >=1.2.3,<1.3"

    -- http://getcomposer.org/doc/01-basic-usage.md#package-versions
  • Markus Weiland
    #8 Markus Weiland said on the 2013/06/16 at 16:24
    You say that Symfony 2.3 has a minimum-stability of "stable", yet when I look in 2.3's composer.json, it says "dev" ( https://github.com/symfony/symfony/blob/2.3/composer.json#L81 ). How does this fit together with this article?
  • Fabien Potencier
    #9 Fabien Potencier said on the 2013/06/16 at 22:24
    @Markus: I'm talking about the Symfony Standard Edition.
  • Ghazy Ben Ahmed
    #10 Ghazy Ben Ahmed said on the 2013/06/17 at 16:41
    thx
  • Art Hundiak
    #11 Art Hundiak said on the 2013/06/18 at 13:21
    Am I the only one whose app/config/parameters.yml file gets overwritten whenever I run composer install/update? I guess it has something to do with the fairly new code that tries to create one. But I would not expect that an existing file would simply be overwritten. Maybe I got something configured wrong.
  • Audrius Karabanovas
    #12 Audrius Karabanovas said on the 2013/06/18 at 14:07
    Another way to use "stable" without adding everywhere @dev is to add to your composer.json root entry: "prefer-stable": true,