Symfony
sponsored by SensioLabs
Menu
  • About
  • Documentation
  • Screencasts
  • Cloud
  • Certification
  • Community
  • Businesses
  • News
  • Download
  1. Home
  2. Documentation
  3. Service Container
  4. Defining Services Dependencies Automatically (Autowiring)
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud
Search by Algolia

Table of Contents

  • An Autowiring Example
  • Autowiring Logic Explained
  • Using Aliases to Enable Autowiring
  • Working with Interfaces
  • Dealing with Multiple Implementations of the Same Type
  • Fixing Non-Autowireable Arguments
  • Autowiring other Methods (e.g. Setters)
  • Autowiring Controller Action Methods
  • Performance Consequences
  • Public and Reusable Bundles

Defining Services Dependencies Automatically (Autowiring)

Edit this page

Warning: You are browsing the documentation for Symfony 4.0, which is no longer maintained.

Read the updated version of this page for Symfony 6.2 (the current stable version).

Defining Services Dependencies Automatically (Autowiring)

Autowiring allows you to manage services in the container with minimal configuration. It reads the type-hints on your constructor (or other methods) and automatically passes the correct services to each method. Symfony's autowiring is designed to be predictable: if it is not absolutely clear which dependency should be passed, you'll see an actionable exception.

Tip

Thanks to Symfony's compiled container, there is no runtime overhead for using autowiring.

An Autowiring Example

Imagine you're building an API to publish statuses on a Twitter feed, obfuscated with ROT13... a fun encoder that shifts all characters 13 letters forward in the alphabet.

Start by creating a ROT13 transformer class:

1
2
3
4
5
6
7
8
9
namespace App\Util;

class Rot13Transformer
{
    public function transform($value)
    {
        return str_rot13($value);
    }
}

And now a Twitter client using this transformer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
namespace App\Service;

use App\Util\Rot13Transformer;

class TwitterClient
{
    private $transformer;

    public function __construct(Rot13Transformer $transformer)
    {
        $this->transformer = $transformer;
    }

    public function tweet($user, $key, $status)
    {
        $transformedStatus = $this->transformer->transform($status);

        // ... connect to Twitter and send the encoded status
    }
}

If you're using the default services.yaml configuration, both classes are automatically registered as services and configured to be autowired. This means you can use them immediately without any configuration.

However, to understand autowiring better, the following examples explicitly configure both services. Also, to keep things simple, configure TwitterClient to be a public service:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# config/services.yaml
services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false
    # ...

    App\Service\TwitterClient:
        # redundant thanks to _defaults, but value is overridable on each service
        autowire: true
        # not required, will help in our example
        public: true

    App\Util\Rot13Transformer:
        autowire: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <defaults autowire="true" autoconfigure="true" public="false" />
        <!-- ... -->

        <service id="App\Service\TwitterClient" autowire="true" public="true" />

        <service id="App\Util\Rot13Transformer" autowire="true" />
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
// config/services.php
use App\Service\TwitterClient;
use App\Util\Rot13Transformer;

// ...

// the autowire method is new in Symfony 3.3
// in earlier versions, use register() and then call setAutowired(true)
$container->autowire(TwitterClient::class)
    ->setPublic(true);

$container->autowire(Rot13Transformer::class)
    ->setPublic(false);

Now, you can use the TwitterClient service immediately in a controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace App\Controller;

use App\Service\TwitterClient;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;

class DefaultController extends Controller
{
    /**
     * @Route("/tweet")
     */
    public function tweet()
    {
        // fetch $user, $key, $status from the POST'ed data

        $twitterClient = $this->container->get(TwitterClient::class);
        $twitterClient->tweet($user, $key, $status);

        // ...
    }
}

This works automatically! The container knows to pass the Rot13Transformer service as the first argument when creating the TwitterClient service.

Autowiring Logic Explained

Autowiring works by reading the Rot13Transformer type-hint in TwitterClient:

1
2
3
4
5
6
7
8
9
10
11
12
// ...
use App\Util\Rot13Transformer;

class TwitterClient
{
    // ...

    public function __construct(Rot13Transformer $transformer)
    {
        $this->transformer = $transformer;
    }
}

The autowiring system looks for a service whose id exactly matches the type-hint: so App\Util\Rot13Transformer. In this case, that exists! When you configured the Rot13Transformer service, you used its fully-qualified class name as its id. Autowiring isn't magic: it simply looks for a service whose id matches the type-hint. If you load services automatically, each service's id is its class name.

If there is not a service whose id exactly matches the type, a clear exception will be thrown.

Autowiring is a great way to automate configuration, and Symfony tries to be as predictable and clear as possible.

Using Aliases to Enable Autowiring

The main way to configure autowiring is to create a service whose id exactly matches its class. In the previous example, the service's id is App\Util\Rot13Transformer, which allows us to autowire this type automatically.

This can also be accomplished using an alias. Suppose that for some reason, the id of the service was instead app.rot13.transformer. In this case, any arguments type-hinted with the class name (App\Util\Rot13Transformer) can no longer be autowired.

No problem! To fix this, you can create a service whose id matches the class by adding a service alias:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
# config/services.yaml
services:
    # ...

    # the id is not a class, so it won't be used for autowiring
    app.rot13.transformer:
        class: App\Util\Rot13Transformer
        # ...

    # but this fixes it!
    # the ``app.rot13.transformer`` service will be injected when
    # an ``App\Util\Rot13Transformer`` type-hint is detected
    App\Util\Rot13Transformer: '@app.rot13.transformer'
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <!-- ... -->

        <service id="app.rot13.transformer" class="App\Util\Rot13Transformer" autowire="true" />
        <service id="App\Util\Rot13Transformer" alias="app.rot13.transformer" />
    </services>
</container>
1
2
3
4
5
6
7
8
// config/services.php
use App\Util\Rot13Transformer;

// ...

$container->autowire('app.rot13.transformer', Rot13Transformer::class)
    ->setPublic(false);
$container->setAlias(Rot13Transformer::class, 'app.rot13.transformer');

This creates a service "alias", whose id is App\Util\Rot13Transformer. Thanks to this, autowiring sees this and uses it whenever the Rot13Transformer class is type-hinted.

Tip

Aliases are used by the core bundles to allow services to be autowired. For example, MonologBundle creates a service whose id is logger. But it also adds an alias: Psr\Log\LoggerInterface that points to the logger service. This is why arguments type-hinted with Psr\Log\LoggerInterface can be autowired.

Working with Interfaces

You might also find yourself type-hinting abstractions (e.g. interfaces) instead of concrete classes as it makes it easy to replace your dependencies with other objects.

To follow this best practice, suppose you decide to create a TransformerInterface:

1
2
3
4
5
6
namespace App\Util;

interface TransformerInterface
{
    public function transform($value);
}

Then, you update Rot13Transformer to implement it:

1
2
3
4
5
// ...
class Rot13Transformer implements TransformerInterface
{
    // ...
}

Now that you have an interface, you should use this as your type-hint:

1
2
3
4
5
6
7
8
9
class TwitterClient
{
    public function __construct(TransformerInterface $transformer)
    {
         // ...
    }

    // ...
}

But now, the type-hint (App\Util\TransformerInterface) no longer matches the id of the service (App\Util\Rot13Transformer). This means that the argument can no longer be autowired.

To fix that, add an alias:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
# config/services.yaml
services:
    # ...

    App\Util\Rot13Transformer: ~

    # the ``App\Util\Rot13Transformer`` service will be injected when
    # an ``App\Util\TransformerInterface`` type-hint is detected
    App\Util\TransformerInterface: '@App\Util\Rot13Transformer'
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <!-- ... -->
        <service id="App\Util\Rot13Transformer" />

        <service id="App\Util\TransformerInterface" alias="App\Util\Rot13Transformer" />
    </services>
</container>
1
2
3
4
5
6
7
// config/services.php
use App\Util\Rot13Transformer;
use App\Util\TransformerInterface;

// ...
$container->autowire(Rot13Transformer::class);
$container->setAlias(TransformerInterface::class, Rot13Transformer::class);

Thanks to the App\Util\TransformerInterface alias, the autowiring subsystem knows that the App\Util\Rot13Transformer service should be injected when dealing with the TransformerInterface.

Dealing with Multiple Implementations of the Same Type

Suppose you create a second class - UppercaseTransformer that implements TransformerInterface:

1
2
3
4
5
6
7
8
9
namespace App\Util;

class UppercaseTransformer implements TransformerInterface
{
    public function transform($value)
    {
        return strtoupper($value);
    }
}

If you register this as a service, you now have two services that implement the App\Util\TransformerInterface type. Autowiring subsystem can not decide which one to use. Remember, autowiring isn't magic; it simply looks for a service whose id matches the type-hint. So you need to choose one by creating an alias from the type to the correct service id (see Defining Services Dependencies Automatically (Autowiring)).

If you want Rot13Transformer to be the service that's used for autowiring, create that alias:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# config/services.yaml
services:
    # ...

    App\Util\Rot13Transformer: ~
    App\Util\UppercaseTransformer: ~

    # the ``App\Util\Rot13Transformer`` service will be injected when
    # a ``App\Util\TransformerInterface`` type-hint is detected
    App\Util\TransformerInterface: '@App\Util\Rot13Transformer'

    App\Service\TwitterClient:
        # the Rot13Transformer will be passed as the $transformer argument
        autowire: true

        # If you wanted to choose the non-default service, wire it manually
        # arguments:
        #     $transformer: '@App\Util\UppercaseTransformer'
        # ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <!-- ... -->
        <service id="App\Util\Rot13Transformer" />
        <service id="App\Util\UppercaseTransformer" />

        <service id="App\Util\TransformerInterface" alias="App\Util\Rot13Transformer" />

        <service id="App\Service\TwitterClient" autowire="true">
            <!-- <argument key="$transformer" type="service" id="App\Util\UppercaseTransformer" /> -->
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
// config/services.php
use App\Util\Rot13Transformer;
use App\Util\UppercaseTransformer;
use App\Util\TransformerInterface;
use App\Service\TwitterClient;

// ...
$container->autowire(Rot13Transformer::class);
$container->autowire(UppercaseTransformer::class);
$container->setAlias(TransformerInterface::class, Rot13Transformer::class);
$container->autowire(TwitterClient::class)
    //->setArgument('$transformer', new Reference(UppercaseTransformer::class))
;

Thanks to the App\Util\TransformerInterface alias, any argument type-hinted with this interface will be passed the App\Util\Rot13Transformer service. But, you can also manually wire the other service by specifying the argument under the arguments key.

Fixing Non-Autowireable Arguments

Autowiring only works when your argument is an object. But if you have a scalar argument (e.g. a string), this cannot be autowired: Symfony will throw a clear exception.

To fix this, you can manually wire the problematic argument. You wire up the difficult arguments, Symfony takes care of the rest.

Autowiring other Methods (e.g. Setters)

When autowiring is enabled for a service, you can also configure the container to call methods on your class when it's instantiated. For example, suppose you want to inject the logger service, and decide to use setter-injection:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
namespace App\Util;

class Rot13Transformer
{
    private $logger;

    /**
     * @required
     */
    public function setLogger(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    public function transform($value)
    {
        $this->logger->info('Transforming '.$value);
        // ...
    }
}

Autowiring will automatically call any method with the @required annotation above it, autowiring each argument. If you need to manually wire some of the arguments to a method, you can always explicitly configure the method call.

Autowiring Controller Action Methods

If you're using the Symfony Framework, you can also autowire arguments to your controller action methods. This is a special case for autowiring, which exists for convenience. See Controller for more details.

Performance Consequences

Thanks to Symfony's compiled container, there is no performance penalty for using autowiring. However, there is a small performance penalty in the dev environment, as the container may be rebuilt more often as you modify classes. If rebuilding your container is slow (possible on very large projects), you may not be able to use autowiring.

Public and Reusable Bundles

Public bundles should explicitly configure their services and not rely on autowiring.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
We stand with Ukraine.
Version:
Symfony Code Performance Profiling

Symfony Code Performance Profiling

Check Code Performance in Dev, Test, Staging & Production

Check Code Performance in Dev, Test, Staging & Production

↓ Our footer now uses the colors of the Ukrainian flag because Symfony stands with the people of Ukraine.

Avatar of Anthony GRASSIOT, a Symfony contributor

Thanks Anthony GRASSIOT (@antograssiot) for being a Symfony contributor

17 commits • 566 lines changed

View all contributors that help us make Symfony

Become a Symfony contributor

Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

Learn how to contribute

Symfony™ is a trademark of Symfony SAS. All rights reserved.

  • What is Symfony?
    • Symfony at a Glance
    • Symfony Components
    • Case Studies
    • Symfony Releases
    • Security Policy
    • Logo & Screenshots
    • Trademark & Licenses
    • symfony1 Legacy
  • Learn Symfony
    • Symfony Docs
    • Symfony Book
    • Reference
    • Bundles
    • Best Practices
    • Training
    • eLearning Platform
    • Certification
  • Screencasts
    • Learn Symfony
    • Learn PHP
    • Learn JavaScript
    • Learn Drupal
    • Learn RESTful APIs
  • Community
    • SymfonyConnect
    • Support
    • How to be Involved
    • Code of Conduct
    • Events & Meetups
    • Projects using Symfony
    • Downloads Stats
    • Contributors
    • Backers
  • Blog
    • Events & Meetups
    • A week of symfony
    • Case studies
    • Cloud
    • Community
    • Conferences
    • Diversity
    • Documentation
    • Living on the edge
    • Releases
    • Security Advisories
    • SymfonyInsight
    • Twig
    • SensioLabs
  • Services
    • SensioLabs services
    • Train developers
    • Manage your project quality
    • Improve your project performance
    • Host Symfony projects
    Deployed on
Follow Symfony
Search by Algolia