Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

_defaults, autowire & autoconfigure

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.

If you started a brand new Symfony 3.3 project, its services.yml file will look like this.

You're actually seeing at least four new features all at once! Wow. All of this is built on top of the existing service configuration system and you need to "opt in" to any of the new features. That means that the traditional way of configuring services that you've been using until now still works and always will. Winning!

But even if you ultimately choose not to use some of these new features, you need to understand how they work, because you'll see them a lot. For example, the Symfony documentation has already been updated to assume you're using these.

First, a word of warning: using the new features is fun. But, upgrading an existing project to use them... well... it's less fun. It takes some work, and when you're done... your project works... the same as before. I'm also going to show you the ugliest parts of the new system so you can handle them in your project. But stick with me! At the end, we'll use the new features to build some new code. And that, is a blast.

_defaults: File-side Service Defaults

Let's look at this _defaults thing first. Open up your app/config/services.yml file. At the top of the services section - though order isn't important - add _defaults. Then below that, autowire: true and autoconfigure: true:

... lines 1 - 5
services:
_defaults:
autowire: true
autoconfigure: true
... lines 10 - 41

Let's unpack this. First, _defaults is a new special keyword that allows you to set default configuration for all services in this file. It's equivalent to adding autowire and autoconfigure keys under every service in this file only. And of course, any config from _defaults can be overridden by a specific service.

Autowiring

Autowiring is not new: we talk about it in our Symfony series. When a service is autowired, it means that its constructor arguments are automatically configured when possible by reading type-hints. For example, the MarkdownExtension is autowired:

... lines 1 - 5
services:
... lines 7 - 10
app.markdown_extension:
class: AppBundle\Twig\MarkdownExtension
tags:
- { name: twig.extension }
#arguments: ['@app.markdown_transformer']
autowire: true
... lines 17 - 43

And its first constructor argument is type-hinted with MarkdownTransformer:

... lines 1 - 2
namespace AppBundle\Twig;
use AppBundle\Service\MarkdownTransformer;
class MarkdownExtension extends \Twig_Extension
{
... lines 9 - 10
public function __construct(MarkdownTransformer $markdownTransformer)
{
... line 13
}
... lines 15 - 33
}

Thanks to that, Symfony determines which service to pass here.

The way that autowiring works has changed in Symfony 3.3. But more on that later.

Since we have autowire under _defaults, we can remove it from everywhere else: it's redundant. And yes, this does mean that some services that were not autowired before are now set to autowire: true. For example, app.markdown_transformer is now being autowired:

... lines 1 - 5
services:
app.markdown_transformer:
class: AppBundle\Service\MarkdownTransformer
arguments: ['@markdown.parser', '@doctrine_cache.providers.my_markdown_cache']
... lines 10 - 43

But... that's no problem! Both of its arguments are being explicitly set:

... lines 1 - 2
namespace AppBundle\Service;
use Doctrine\Common\Cache\Cache;
use Knp\Bundle\MarkdownBundle\MarkdownParserInterface;
class MarkdownTransformer
{
... lines 10 - 12
public function __construct(MarkdownParserInterface $markdownParser, Cache $cache)
{
... lines 15 - 16
}
... lines 18 - 33
}

so autowiring simply doesn't do anything. Setting autowire: true under _defaults is safe to add to an existing project.

Autoconfigure

Next, this autoconfigure key is a brand new feature. When a service is autoconfigured, it means that Symfony will automatically tag it when possible. For example, our MarkdownExtension extends \Twig_Extension:

... lines 1 - 6
class MarkdownExtension extends \Twig_Extension
{
... lines 9 - 33
}

Which implements Twig_ExtensionInterface:

abstract class Twig_Extension implements Twig_ExtensionInterface
{
    // ...
}

That's actually the important part. When a service is autoconfigured and its class implements Twig_ExtensionInterface, the twig.extension tag is automatically added for you:

... lines 1 - 5
services:
... lines 7 - 10
app.markdown_extension:
... line 12
tags:
- { name: twig.extension }
... lines 15 - 43

Basically, Symfony is saying:

Hey! I see you configured a service that implements Twig_ExtensionInterface. Obviously, that's a Twig extension, so let me configure it for you.

... lines 1 - 5
services:
... lines 7 - 14
app.markdown_extension:
class: AppBundle\Twig\MarkdownExtension
#arguments: ['@app.markdown_transformer']
... lines 18 - 41

This works for many - but not all tags. It does not work for doctrine.event_subscriber or form.type_extension:

... lines 1 - 5
services:
... lines 7 - 21
app.doctrine.hash_password_listener:
class: AppBundle\Doctrine\HashPasswordListener
tags:
- { name: doctrine.event_subscriber }
app.form.help_form_extenion:
class: AppBundle\Form\TypeExtension\HelpFormExtension
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType }
... lines 31 - 41

Because it has an extended_type tag option... which the system can't guess for you. When you're developing a feature, the docs will tell you whether or not you need to add the tag manually. If you do add a tag, even though you didn't need to, no problem! Your tag takes precedence.

So, all our services are autowired and autoconfigured! But, it doesn't make any difference, besides shortening our config just a little. And when we refresh, everything still works!

Leave a comment!

9
Login or Register to join the conversation
Geoffrey M. Avatar
Geoffrey M. Avatar Geoffrey M. | posted 4 years ago

Hi !
I have like 40 differents services.yml in my app.
Shall I add these keys in all this 40 files ?

Reply

Hey Geoffrey M.

40 services.yaml files? Can I ask why? In theory you only have one but it's pretty common to have one for prod, one for dev and one for testing

Reply
Geoffrey M. Avatar
Geoffrey M. Avatar Geoffrey M. | MolloKhan | posted 4 years ago | edited

Hey MolloKhan

Because my app got around 40 differents bundles and each bundle has its own services declared in their own Resources/config/services.yml.

Reply

Ohh, I get it now. Then, no, I think you should start heading towards to a non-bundle structure, this will be helpful if you ever decide to upgrade to Symfony4. If that's not your case, then you can have a main services.yaml file in your "app/config" directory and in there set those keys for autowiring and autoconfigure. Or, if you are going to keep adding new bundles into the project, then you can add those keys to the new services.yaml files.

Cheers!

Reply
Geoffrey M. Avatar

Ok, thanks ! Do you have any tips / hints on how to migrate from a bundle structure to a non-bundle structure when you have around 40 bundles living in ./src ?

Reply

Don't worry Geoffrey, we have your back covered with a full course of how to upgrade from Symfony3 to 4
Check it out: https://symfonycasts.com/sc...

Reply

Hello Ryan, i see two new services in the services.yml file :

app.encouraging_message_generator:
class: AppBundle\Service\MessageGenerator
arguments:
- ['You can do it!', 'Dude, sweet!', 'Woot!']
app.discouraging_message_generator:
class: AppBundle\Service\MessageGenerator
arguments:
- ['We are *never* going to figure this out', 'Why even try again?', 'Facepalm']

But i don't find the course where we are created them.
Can you help me ?

Thanks :)

Reply

Hey abdouniabdelkarim

Those services already came with the course code, but don't worry too much about them, they exist purely to demonstrate a couple of examples in the further episodes

Cheers!

Reply

Thanks you MolloKhan
Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.3.0-RC1", // v3.3.0-RC1
        "doctrine/orm": "^2.5", // 2.7.5
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
        "symfony/swiftmailer-bundle": "^2.3", // v2.6.7
        "symfony/monolog-bundle": "^3.1", // v3.2.0
        "symfony/polyfill-apcu": "^1.0", // v1.23.0
        "sensio/distribution-bundle": "^5.0", // v5.0.25
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.29
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.4
        "composer/package-versions-deprecated": "^1.11", // 1.11.99.4
        "knplabs/knp-markdown-bundle": "^1.4", // 1.7.1
        "doctrine/doctrine-migrations-bundle": "^1.1", // v1.3.2
        "stof/doctrine-extensions-bundle": "^1.2" // v1.3.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.7
        "symfony/phpunit-bridge": "^3.0", // v3.4.47
        "nelmio/alice": "^2.1", // v2.3.6
        "doctrine/doctrine-fixtures-bundle": "^2.3" // v2.4.1
    }
}
userVoice