Buy

Upgrade to Symfony 3.4

Symfony 4: it's a game changer. It's my favorite thing since chocolate milk. Honestly, I've never been so excited to start writing tutorials: you are going to love it!

But first, we need to talk about how you can upgrade to Symfony 4 so that all your projects can enjoy the goodness!

There are two big steps. First, well, of course, we need to actually upgrade our app to Symfony 4! And second, we need to update our project to support Symfony Flex. That is where things get interesting.

Upgrading does take some work. But don't worry: we'll walk through it together and learn a ton on the way.

Download the Course Code

As always, if you really want to get a handle on upgrading... or you just like chocolate milk, you should download the course code from this page and code along. Full disclosure: the download does not contain chocolate milk.

But, after you unzip it, it does contain a start/ directory with the same code you see here. Follow the README.md file for the steps needed to get your project running.

The last step will be to find your terminal and run:

./bin/console server:run

to start the built-in web server. Check it out at http://localhost:8000. Hello AquaNote! This is the Symfony 3.3 project that we've been building in our Symfony series.

Upgrading to Symfony 4

So: what's the first step to upgrading to Symfony 4? It's upgrading to 3.4! And that's delighyfully simple: Open composer.json, change the version of symfony/symfony to ^3.4, find your terminal and run:

68 lines composer.json
... lines 1 - 15
"require": {
... line 17
"symfony/symfony": "^3.4",
... lines 19 - 30
},
... lines 32 - 68

composer update

And celebrate! So easy! By the way, you could just update symfony/symfony. But, honestly, it's just easier to upgrade everything. And since I keep responsible version constraints in composer.json, ahem, no dev-master or * versions, this is pretty safe and also means I get bug fixes, security fixes and new features.

And... hello Symfony 3.4! The best part? Ah, you guys already know it: thanks to Symfony's backwards-compatibility promise, our project will just work... immediately. Refresh! Yep! Welcome to Symfony 3.4.

Symfony 3.4 Versus Symfony 4.0

So why did we do this? Why not just skip directly to Symfony 4? Well, Symfony 3.4 and Symfony 4.0 are identical: they have the exact same features. The only difference is that all deprecated code paths are removed in 4.0.

On the web debug toolbar, you can see that this page contains 9 deprecated code calls. By upgrading to Symfony 3.4 first, we can hunt around and fix these. As soon as they're all gone... well, we'll be ready for Symfony 4!

Deprecation: Kernel::loadClassCache()

Click the icon to go see the full list of deprecations. Check out the first one: Kernel::loadClassCache() is deprecated since version 3.3. You can click "Show Trace" to see where this is coming from, but I already know!

Open web/app.php and web/app_dev.php. There it is! On line 28, remove the $kernel->loadClassCache() line. Do the same thing in app.php. Why is this deprecated? This originally gave your app a performance boost. But thanks to optimizations in PHP 7, it's not needed anymore. Less code, more speed, woo!

Deprecation: GuardAuthenticator::supports()

Close those files. What's next? Hmm, something about AbstractGuardAuthenticator::supports() is deprecated. Oh, and a recommendation! We should implement supports() inside our LoginFormAuthenticator.

Because I obsess over Symfony's development, I know what this is talking about. If you have more of a life than I do and are not already aware of every single little change, you should go to github.com/symfony/symfony and find the UPGRADE-4.0.md file. It's not perfect, but it contains explanations behind a lot of the changes and deprecations you'll see.

Go find the LoginFormAuthenticator in src/AppBundle/Security. We need to add a new method: public function supports() with a Request argument.

Copy the logic from getCredentials() that checks the URL, and just return it. Here's the deal: in Symfony 3, getCredentials() was called on every request. If it returned null, the authenticator was done: no other methods were called on it.

79 lines src/AppBundle/Security/LoginFormAuthenticator.php
... lines 1 - 15
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 18 - 30
public function supports(Request $request)
{
return $request->getPathInfo() == '/login' && $request->isMethod('POST');
}
... line 35
public function getCredentials(Request $request)
{
... lines 38 - 47
}
... lines 49 - 77
}

In Symfony 4, supports() is now called on every request instead. If it returns false, the authenticator is done like before. But if it returns true, then getCredentials() is called. We split the work of getCredentials() into two methods.

So, remove the logic at the top of it: we know this will only be called when the URL is /login and it's a POST request.

Deprecation: Quoting % in YAML

Most of the other deprecations are pretty easy, like the next one:

Not quoting the scalar %cache_type% starting with the "%" indicator character is deprecated since Symfony 3.1.

This, and the next deprecation are in config.yml - and it even tells us the exact lines!

Open up app/config/config.yml and find line 71. Yep! Put quotes around %cache_type%. To more closely follow the official YAML spec, if a value starts with %, it needs to have quotes around it. Do the same around the directory value.

80 lines app/config/config.yml
... lines 1 - 67
doctrine_cache:
providers:
my_markdown_cache:
type: '%cache_type%'
file_system:
directory: '%kernel.cache_dir%/markdown_cache'
... lines 74 - 80

Deprecation: logout_on_user_change

Back on the list, there is one more easy deprecation!

Not setting logout_on_user_change to true on firewall "main" is deprecated as of 3.4.

Copy that key. Then, open app/config/security.yml. Under the main firewall, paste this and set it to true.

41 lines app/config/security.yml
... lines 1 - 14
firewalls:
... lines 16 - 20
main:
... lines 22 - 29
logout_on_user_change: true
... lines 31 - 41

So, what the heck is this? Check it out: suppose you change your password while on your work computer. Previously, doing that did not cause you to be logged out on any other computers, like on your home computer. This was a security flaw, and the behavior was changed in Symfony 4. By turning this on, you can test to make sure your app doesn't have any surprises with that behavior.

Phew! Before we talk about the last deprecations, go back to the homepage and refresh. Yes! In 5 minutes our 9 deprecations are down to 2! Open up the list again. Interesting: it says:

Relying on service auto-registration for Genus is deprecated. Create a service named AppBundle\Entity\Genus instead.

That's weird... and involves changes to autowiring. Let's talk about those next!

Leave a comment!

  • 2018-01-04 Victor Bocharsky

    Hey Nan,

    You're right, there's no config.yml anymore, it was splitted to separate files, that's why you need to create it "doctrine_cache.yaml" file manually inside "config/packages/". Actually, this question is duplicated, so look at my original answer:
    https://knpuniversity.com/s...

    Cheers!

  • 2018-01-03 Nan Zhao

    In the lastest version of symfony3, the config.yml is not generated by using 'composer create-project symfony/skeleton 3.4'. I tried to load the file "doctrine_cache.yaml", but failed. When I type 'php ./bin/console dubug:container markdown_cache', the command line says 'In ContainerDebugCommand.php line 211: No services found that match "markdown_cache". How can I create a custom server? Can you help me ?

  • 2018-01-02 Victor Bocharsky

    Hey Shing,

    I agree, it's not trivial, but that's the price to increase code quality and make development more smooth. However, Symfony devs made all their best to make this upgrading process very smooth, thanks to cool deprecation notices feature.

    Cheers!

  • 2017-12-30 shing

    Hi Victor, Glad I know I'm not mad. I'm rather new at this. I guess documentation might not always be accurate. Also the change from symfony 3 to 4 does not seem trival.

    Thanks for help :)

  • 2017-12-29 Victor Bocharsky

    Hey Shing,

    Good catch! Actually, it's impossible to require symfony/symfony due to this line: https://github.com/symfony/... . As you can see, it forbids installation of symfony/symfony. I'm not sure about the reasons behind, but I think it's kind of bug in Symfony docs. I opened an issue about it here: https://github.com/symfony/...

    So, the only way I see is to install all the deps manually obviously specifying them in composer.json. Actually, it's not a best practice to install all the Symfony components to your project when you don't use them *all*. And probably in most cases you won't use them all :)

    Cheers!

  • 2017-12-28 shing

    Hi weaverryan,

    I install the a fresh symfony 4 with composer create-project symfony/skeleton
    Everything seems to be ok. So I want to install all components. The symfony site (https://symfony.com/doc/cur... says to composer require symfony/symfony

    I get an error
    Your requirements could not be resolved to an installable set of packages.
    Problem 1
    - symfony/symfony v4.0.0 conflicts with __root__[No version set (parsed as 1.0.0)].
    - symfony/symfony v4.0.1 conflicts with __root__[No version set (parsed as 1.0.0)].
    - symfony/symfony v4.0.2 conflicts with __root__[No version set (parsed as 1.0.0)].
    - Installation request for __root__ No version set (parsed as 1.0.0) -> satisfiable by __root__[No version set (parsed as 1.0.0)].
    - Installation request for symfony/symfony ^4.0 -> satisfiable by symfony/symfony[v4.0.0, v4.0.1, v4.0.2].

    My composer is of the latest version 1.5.6

    Am I doing something wrong?
    Thxs.

  • 2017-12-19 weaverryan

    Ah yes, you started a new project - I'm aware of that bug you linked to. But, I'm glad it's working now - I'm not sure if your code contains that fix or not - but I wouldn't worry about it unless it comes up again :).

    Cheers!

  • 2017-12-19 J.R. Jenkins

    So I am apparently crazy or had some other bug that I didn't realize, as I uncommented those lines, cleared my caches, and there is no error... sorry for the red herring.

  • 2017-12-19 J.R. Jenkins

    weaverryan I did get an error, although I will have to look it up when I get back to work in the morning, that actually led me to this discussion in the Symfony issue queue, https://github.com/symfony/.... Basically I had started with a 3.4 install and was slowly developing things out, but when trying to access the site through app.php it would crash with what I believe was the "Cannot redeclare class Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser" error

    Commenting out both the include for bootstrap.php and call to loadClassCache resolved the issue, but that didn't seem right. I was going to dig into when I saw you were releasing this course so I thought I would see what you all had to say :-)

  • 2017-12-19 weaverryan

    Hey J.R. Jenkins!

    Ah, stuck on PHP 5 - I understand it, but bummer! So, if you're stuck on PHP 5, then you're also stuck in Symfony 3.4 for now. This basically means that, until you're ready to upgrade to Symfony 4, you should just ignore the loadClassCache() stuff (i.e. LEAVE them - don't remove them). You mentioned you're having "errors" - you should only have deprecation warnings. Are you getting actual errors with loadClassCache()?

    Cheers!

  • 2017-12-19 J.R. Jenkins

    Besides upgrading to PHP 7, what would you recommend people who are stuck on PHP 5.5.9 do in regards to the ```Kernel::loadClassCache()``` call? For internal reasons we have several apps that are still on Ubuntu 14.04 LTS versions and the systems administrators prefer to install PHP via APT, so we are limited on upgrading. I see that it looks like in a new Symfony 3.4 install they wrap this in a ```if (PHP_VERSION_ID < 70000) {}``` block, but I still had errors when I tried to load with that. Any recommendations?