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 yourcomposer.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.
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 :)
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)
Thanks Fabien ! This article is so clear and informative, it should be added to the cookbook.
Any reason not to use "2.3.*" instead of ">=2.3.0,<2.4-dev" ?
If I am not mistaken, the restrictive version can also be: "symfony/symfony": "~2.3.0";
thanks fabien
Nicolas i think you cannot get 2.3.9-dev with 2.3.*?
-- http://getcomposer.org/doc/01-basic-usage.md#package-versions
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?
@Markus: I'm talking about the Symfony Standard Edition.
thx
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.
Another way to use "stable" without adding everywhere @dev is to add to your composer.json root entry: "prefer-stable": true,