Do you Autowire Services in Symfony?
You can Autowire Parameters Too

I love how Symfony caught up late autowiring integration in since Symfony 2.8. Then set a trend in Symfony 3.3 with service autoregistration.

That opens new possibilities to almost config-less registration, doesn't it?

Do you still have these old-school Symfony 2.7- configs?

# services.yml
services:
    first_service:
        class: 'OpenProject\FirstClass'

    second_service:
        class: 'OpenProject\SecondClass'
        arguments:
            - '@first_service'

You have to register every service manually and set service arguments manually.

Honestly, I envy you. I can't imagine more wet PHP dream than refactoring to autowiring.


Are running on a newer Symfony? You'll be more familiar with this syntax:

# services.yml
services:
    OpenProject\FirstClass: ~

    OpenProject\SecondClass:
        arguments:
            - '@OpenProject\FirstClass'

...or even...

# services.yml
services:
    _defaults:
        autowire: true

    OpenProject\FirstClass: ~
    OpenProject\SecondClass: ~

...or even on the final one - autodiscovery:

# services.yml
services:
    _defaults:
        autowire: true

    OpenProject\:
        resource: "../src"

This allows you to forget, that you actually have any config with services.

Why? Because creating and using new service = creating a PHP class. No YAML, no XML, no whatever memory-locking configuration.


Programmers used DI containers, config was getting into the shape of Miss World, autowiring and autodiscovery became status-quo and life was good.

Suddenly...

...You Want to Use Parameters

No problem! Since you already know the clean-way, you'll use constructor:

<?php

namespace OpenProject;

class FirstClass
{
    public function __construct(string $apiKey)
    {
        // ...
    }
}

And add parameter values to config:

parameters:
    api_key: "asdf"

How do we get those parameters in there?

 services:
     _defaults:
         autowire: true

     OpenProject\:
         resource: "../src"
+
+    OpenProject\FirstClass:
+        arguments:
+             - "%api_key%"

It's that simple! Just kidding.

Symfony guys realized this needs to follow innovations as service registration did.

 services:
     _defaults:
         autowire: true
+    bind:
+        $apiKey: "%api_key%"

     OpenProject\:
         resource: "../src"

Not bad, at least compared to the previous solution.

New Service? New Parameter?
  • create a PHP class
  • require it in the constructor
  • create a parameter
  • require it in the constructor
  • bind it in config
  • bind it in every config where it is required by service
  • also, remove it from every config where it's not used anymore - otherwise you'll get nice Symfony exception

It's that simple! Just kidding, again.

Autowired Parameters in Symfony

If services are autowired by unique type,
parameters can be autowired by unique name.

You don't need this:

 services:
     _defaults:
         autowire: true
-    bind:
-        $apiKey: "%api_key%"

     OpenProject\:
         resource: "../src"

All you need is... love... and to:

parameters:
    api_key: "asdf"
<?php

namespace OpenProject;

class FirstClass
{
    public function __construct(string $apiKey)
    {
        // ...
    }
}

That's all folks!


Convention over Configuration

If you can shave off 2 almost identical approaches - element autowiring - to single 1, do it.

By respecting the naming %param% = $param your code is consistent with services,
where Type = $type and clean and easy to read.


I got bad news - this is not part of Symfony - yet. But neither was autowiring for 8 years until it was.

In the meantime, register Symplify\PackageBuilder\DependencyInjection\CompilerPass\AutoBindParametersCompilerPass.

You don't know how? See Symfony docs.


How Far Can We Push Autowiring Together?

What about array-typed autowiring?

<?php

class SomeClass
{
    /**
     * @param CollectedType[] $collectedClasses
     */
    public function __construct(array $collectedClasses)
    {
        // ...
    }
}

 Done for Symfony 3.4+ and in Nette 3.0.


Maybe convention factory using create() method and it's @return Type or create(): Type?

 services:
     SomeFactory: ~
-    SomeObject:
-        factory: ['@SomeFactory', 'create']

EDITED:  Done for Symfony 3.4+


Or...?

(Place your crazy ideas below ↓)


Typo? Fix it, please  and join 49 people who build this website