Recipe Upgrade: symfony/console & bootstrap.php

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

The first recipe I want to update is symfony/console. Let's get some more info about it:

composer recipes symfony/console

Just like before, it shows us some links: a link to what the recipe looked like at the moment we installed it - I'll paste that into my browser - and also a version of what the recipe looks like right now. Let's open up that one too.

Recipe Organization & History

We're not going to always study the history of a recipe like this, but I need to show you something. Because of the way that recipes are organized, if you want see what changed in a recipe since you installed it, it's not always as easy as looking at the history.

This is what the recipe looks like today. Click to look at the commit history. You might think:

Okay, I'll just go back and see what commits have been made to this recipe since my version.

That's a great idea, because sometimes knowing why something changed can be a huge help. But... the installed version of my recipe lives in a symfony/console/3.3/ directory. The new one lives in symfony/console/4.4/.

Let's look at this entire symfony/console directory. Oh, and make sure that you're looking at the master branch - the latest commits. Each recipe is allowed to have multiple versions. If you installed symfony/console version 3.3, Flex would install the 3.3 recipe. If you installed 4.2, you would get the 4.2 recipe. What if you installed symfony/console 4.1? You would get the 3.3 recipe. A new directory is created only when a recipe needs to be updated to show off a feature that's only available in a newer version.

So, it's kind of a strange versioning mechanism. When we installed this recipe, we installed the 3.3 version. Updating it would install the 4.4 version. So if you want to see the full history... it's tricky: you kind of need to look at the history of what commits have been made to the 4.4 branch... and also maybe the history of what's been done to the 4.2 branch.... and also maybe the commit history for the 3.3 branch.

But, it's not as bad as it sounds - I'm trying to deliver the bad news first. Most of the time, the reason a file was updated in a recipe will be pretty obvious. And when it's not, with a little digging, we can find the reason.

Let's update a Recipe

Okay, so how do we actually upgrade a recipe to the latest version? You can see the answer down here: composer recipes:install symfony/console --force -v.

But, it's not really a smart "update" system. That command tells Flex to completely re-install the symfony/console recipe using the latest version. Try it:

composer recipes:install symfony/console --force -v

Nice! Thanks to the -v flag, it tells us what files it worked on. It says:

Created bin/console
Created config/bootstrap.php

Well, really, it modified those files... but at least in the version of Flex I'm using, it always says "created".

Let's see how things look. Run:

git status

Cool! 3 changed files. Just like with the MonologBundle recipe, we need to add these changes carefully: if we had any custom code in the files, the update process just replaced it. Run:

git add -p

The bin/console changes

The first file is bin/console... and it's a namespace change from Debug to ErrorHandler. This is updating our code to use a new ErrorHandler component - some features of the Debug component were moved there. So that's a good change.

It also... I don't know, added some if statement that prints a warning... it looks like it's just making sure we don't try to run this from inside a web server. Enter "y" to add both changes.

Investigating a Change

Next, woh! It removed a huge block of code from the bottom and tweaked an if statement further up. These changes are on config/bootstrap.php.

This... looks like some low-level, edge-case normalization of environment variables. So probably we want this. But let's pretend we don't know: we want to find out why this change was made.

How? By doing some digging! Go back to symfony/recipes/console. Start by looking in the 4.4/ directory - the version we're installing. Find config/bootstrap.php.

Wait... see the config/ directory? What does that little arrow mean? It means that this is actually a symbolic link to another directory: the 4.4/config directory is identical to 4.2/config.

Ok, let's go look there! Head into the 4.2/ directory, then config/. Woh! Another arrow! This time the bootstrap.php file is a symlink - pointing to, wow! A totally different recipe - a bootstrap.php file in framework-bundle.

The bootstrap.php file is the most complex file in the recipe system and it's shared across several recipes. Yep, I'm showing you the ugliest case.

Let's go find that: symfony/framework-bundle/4.2/config/bootstrap.php. Here is the file. To find the change, use the "blame" feature. Ok, the block of code we're looking at - lines 9 through 12 - have two different commits. Let's look at just one of them:

Allow correct environment to be loaded when .env.local.php exists

And we can even click to see the pull request: #647 if you want really dive into the details. In this case, the change fixes a bug if you use the .env.php.local file.

But, really, config/bootstrap.php is a low-level file that should almost always be identical in every project. So unless you're doing something super advanced, you will probably want to accept all of these changes.

This big removal of code? That was because in an earlier version of this recipe, your project may or may not have had this loadEnv() method on DotEnv: it was added in Symfony 4.2. If your app did not have that method, it added a bunch of code to "imitate" its behavior. We don't need that anymore. Thank you recipe update!

The last change is for the symfony.lock file. We don't even need to look at this: always accept these changes. This marks the recipe as updated and sometimes saves extra debugging info that might be useful later.

That may have seemed like a small step. But other than trying to figure out the reasons a file changed, this was a home run! We were able to update two low-level files, which will help make sure our app continues to work like we expect.

Go ahead and commit these changes. Then let's keep going. Next, we'll update the biggest and most important recipe: the one for symfony/framework-bundle.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.3.0",
        "ext-iconv": "*",
        "antishov/doctrine-extensions-bundle": "^1.4", // v1.4.2
        "aws/aws-sdk-php": "^3.87", // 3.110.11
        "doctrine/doctrine-bundle": "^2.0", // 2.0.6
        "doctrine/doctrine-migrations-bundle": "^1.3|^2.0", // 2.1.2
        "doctrine/orm": "^2.5.11", // v2.7.2
        "easycorp/easy-log-handler": "^1.0", // v1.0.9
        "http-interop/http-factory-guzzle": "^1.0", // 1.0.0
        "knplabs/knp-markdown-bundle": "^1.7", // 1.8.1
        "knplabs/knp-paginator-bundle": "^5.0", // v5.0.0
        "knplabs/knp-snappy-bundle": "^1.6", // v1.7.0
        "knplabs/knp-time-bundle": "^1.8", // v1.11.0
        "league/flysystem-aws-s3-v3": "^1.0", // 1.0.23
        "league/flysystem-cached-adapter": "^1.0", // 1.0.9
        "league/html-to-markdown": "^4.8", // 4.8.2
        "liip/imagine-bundle": "^2.1", // 2.3.0
        "nexylan/slack-bundle": "^2.1", // v2.2.1
        "oneup/flysystem-bundle": "^3.0", // 3.3.0
        "php-http/guzzle6-adapter": "^2.0", // v2.0.1
        "sensio/framework-extra-bundle": "^5.1", // v5.5.3
        "symfony/asset": "5.0.*", // v5.0.2
        "symfony/console": "5.0.*", // v5.0.2
        "symfony/dotenv": "5.0.*", // v5.0.2
        "symfony/flex": "^1.0", // v1.6.2
        "symfony/form": "5.0.*", // v5.0.2
        "symfony/framework-bundle": "5.0.*", // v5.0.2
        "symfony/mailer": "5.0.*", // v5.0.2
        "symfony/messenger": "5.0.*", // v5.0.2
        "symfony/monolog-bundle": "^3.5", // v3.5.0
        "symfony/security-bundle": "5.0.*", // v5.0.2
        "symfony/sendgrid-mailer": "5.0.*", // v5.0.2
        "symfony/serializer-pack": "^1.0", // v1.0.2
        "symfony/twig-bundle": "5.0.*", // v5.0.2
        "symfony/twig-pack": "^1.0", // v1.0.0
        "symfony/validator": "5.0.*", // v5.0.2
        "symfony/webpack-encore-bundle": "^1.4", // v1.7.2
        "symfony/yaml": "5.0.*", // v5.0.2
        "twig/cssinliner-extra": "^2.12", // v2.12.0
        "twig/extensions": "^1.5", // v1.5.4
        "twig/inky-extra": "^2.12" // v2.12.0
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.3.0
        "fzaninotto/faker": "^1.7", // v1.8.0
        "symfony/browser-kit": "5.0.*", // v5.0.2
        "symfony/debug-bundle": "5.0.*", // v5.0.2
        "symfony/maker-bundle": "^1.0", // v1.14.3
        "symfony/phpunit-bridge": "5.0.*", // v5.0.2
        "symfony/profiler-pack": "^1.0", // v1.0.4
        "symfony/var-dumper": "5.0.*" // v5.0.2
    }
}