Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • SensioLabs Professional services to help you with Symfony
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by SensioLabs
  1. Home
  2. Documentation
  3. Service Container
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud

Table of Contents

  • Fetching and using Services
  • Creating/Configuring Services in the Container
  • Injecting Services/Config into a Service
    • Handling Multiple Services
    • Manually Wiring Arguments
  • Service Parameters
  • Choose a Specific Service
  • Binding Arguments by Name or Type
  • The autowire Option
  • The autoconfigure Option
  • Public Versus Private Services
  • Importing Many Services at once with resource
    • Multiple Service Definitions Using the Same Namespace
  • Explicitly Configuring Services and Arguments
  • Learn more

Service Container

Edit this page

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

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

Service Container

Screencast

Do you prefer video tutorials? Check out the Symfony Fundamentals screencast series.

Your application is full of useful objects: a "Mailer" object might help you send emails while another object might help you save things to the database. Almost everything that your app "does" is actually done by one of these objects. And each time you install a new bundle, you get access to even more!

In Symfony, these useful objects are called services and each service lives inside a very special object called the service container. If you have the service container, then you can fetch a service by using that service's id:

1
2
$logger = $container->get('logger');
$entityManager = $container->get('doctrine.orm.entity_manager');

The container allows you to centralize the way objects are constructed. It makes your life easier, promotes a strong architecture and is super fast!

Fetching and using Services

The moment you start a Symfony app, your container already contains many services. These are like tools: waiting for you to take advantage of them. In your controller, you can "ask" for a service from the container by type-hinting an argument with the service's class or interface name. Want to log something? No problem:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/AppBundle/Controller/ProductController.php
// ...

use Psr\Log\LoggerInterface;

/**
 * @Route("/products")
 */
public function listAction(LoggerInterface $logger)
{
    $logger->info('Look! I just used a service');

    // ...
}

3.3

The ability to type-hint a service in order to receive it was introduced in Symfony 3.3. See the controller chapter for more details.

What other services are available? Find out by running:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ php bin/console debug:container

# this is just a *small* sample of the output...
=============================== ==================================================================
Service ID                      Class name
=============================== ==================================================================
doctrine                        Doctrine\Bundle\DoctrineBundle\Registry
filesystem                      Symfony\Component\Filesystem\Filesystem
form.factory                    Symfony\Component\Form\FormFactory
logger                          Symfony\Bridge\Monolog\Logger
request_stack                   Symfony\Component\HttpFoundation\RequestStack
router                          Symfony\Bundle\FrameworkBundle\Routing\Router
security.authorization_checker  Symfony\Component\Security\Core\Authorization\AuthorizationChecker
security.password_encoder       Symfony\Component\Security\Core\Encoder\UserPasswordEncoder
session                         Symfony\Component\HttpFoundation\Session\Session
translator                      Symfony\Component\Translation\DataCollectorTranslator
twig                            Twig\Environment
validator                       Symfony\Component\Validator\Validator\ValidatorInterface
=============================== ==================================================================

You can also use the unique "Service ID" to access a service directly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// src/AppBundle/Controller/ProductController.php
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;

class ProductController extends Controller
{
    /**
     * @Route("/products")
     */
    public function listAction()
    {
        $logger = $this->container->get('logger');
        $logger->info('Look! I just used a service');

        // ...
    }
}

Fetching a service directly from the container like this only works if you extend the Controller class.

Throughout the docs, you'll see how to use the many different services that live in the container.

Container: Lazy-loaded for speed

Wait! Are all the services (objects) instantiated on every request? No! The container is lazy: it doesn't instantiate a service until (and unless) you ask for it. For example, if you never use the validator service during a request, the container will never instantiate it.

Creating/Configuring Services in the Container

Tip

The recommended way of configuring services changed in Symfony 3.3. For a deep explanation, see The Symfony 3.3 DI Container Changes Explained (autowiring, _defaults, etc).

You can also organize your own code into services. For example, suppose you need to show your users a random, happy message. If you put this code in your controller, it can't be re-used. Instead, you decide to create a new class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/AppBundle/Service/MessageGenerator.php
namespace AppBundle\Service;

class MessageGenerator
{
    public function getHappyMessage()
    {
        $messages = [
            'You did it! You updated the system! Amazing!',
            'That was one of the coolest updates I\'ve seen all day!',
            'Great work! Keep going!',
        ];

        $index = array_rand($messages);

        return $messages[$index];
    }
}

Congratulations! You've just created your first service class! You can use it immediately inside your controller:

1
2
3
4
5
6
7
8
9
10
11
12
use AppBundle\Service\MessageGenerator;

public function newAction(MessageGenerator $messageGenerator)
{
    // thanks to the type-hint, the container will instantiate a
    // new MessageGenerator and pass it to you!
    // ...

    $message = $messageGenerator->getHappyMessage();
    $this->addFlash('success', $message);
    // ...
}

When you ask for the MessageGenerator service, the container constructs a new MessageGenerator object and returns it (see sidebar below). But if you never ask for the service, it's never constructed: saving memory and speed. As a bonus, the MessageGenerator service is only created once: the same instance is returned each time you ask for it.

Automatic Service Loading in services.yml

The documentation assumes you're using Symfony Standard Edition (version 3.3) services.yml configuration. The most important part is this:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# app/config/services.yml
services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true
        autoconfigure: true
        public: false

    # makes classes in src/AppBundle available to be used as services
    AppBundle\:
        resource: '../../src/AppBundle/*'
        # you can exclude directories or files
        # but if a service is unused, it's removed anyway
        exclude: '../../src/AppBundle/{Entity,Repository}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- app/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
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <!-- default configuration for services in *this* file -->
        <defaults autowire="true" autoconfigure="true" public="false"/>

        <!-- makes classes in src/AppBundle available to be used as services -->
        <prototype namespace="AppBundle\"
            resource="../../src/AppBundle/*"
            exclude="../../src/AppBundle/{Entity,Repository}"/>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// app/config/services.php
use Symfony\Component\DependencyInjection\Definition;

// To use as default configuration for services in *this* file
$defaults = new Definition();
$defaults
    ->setAutowired(true)
    ->setAutoconfigured(true)
    ->setPublic(false)
;

// $this is a reference to the current loader
$this->registerClasses(
    $defaults,
    // makes classes in src/AppBundle available to be used as services
    'AppBundle\\',
    '../../src/AppBundle/*',
    // excludes some directory or files
    '../../src/AppBundle/{Entity,Repository}'
);

Tip

The value of the resource and exclude options can be any valid glob pattern.

Thanks to this configuration, you can automatically use any classes from the src/AppBundle directory as a service, without needing to manually configure it. Later, you'll learn more about this in Service Container.

If you'd prefer to manually wire your service, that's totally possible: see Service Container.

3.3

The _defaults key and ability to load services from a directory were added in Symfony 3.3.

You can also fetch a service directly from the container via its "id", which will be its class name in this case:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use AppBundle\Service\MessageGenerator;

// accessing services like this only works if you extend Controller
class ProductController extends Controller
{
    public function newAction()
    {
        // only works if your service is public
        $messageGenerator = $this->get(MessageGenerator::class);

        $message = $messageGenerator->getHappyMessage();
        $this->addFlash('success', $message);
        // ...
    }
}

However, this only works if you make your service public.

Caution

Service ids are case-insensitive (e.g. AppBundle\Service\MessageGenerator and appbundle\service\messagegenerator refer to the same service). But this was deprecated in Symfony 3.3. Starting in 4.0, service ids will be case sensitive.

Injecting Services/Config into a Service

What if you need to access the logger service from within MessageGenerator? Your service does not have access to the container directly, so you can't fetch it via $this->container->get().

No problem! Instead, create a __construct() method with a $logger argument that has the LoggerInterface type-hint. Set this on a new $logger property and use it later:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// src/AppBundle/Service/MessageGenerator.php
// ...

use Psr\Log\LoggerInterface;

class MessageGenerator
{
    private $logger;

    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    public function getHappyMessage()
    {
        $this->logger->info('About to find a happy message!');
        // ...
    }
}

That's it! The container will automatically know to pass the logger service when instantiating the MessageGenerator. How does it know to do this? Autowiring. The key is the LoggerInterface type-hint in your __construct() method and the autowire: true config in services.yml. When you type-hint an argument, the container will automatically find the matching service. If it can't, you'll see a clear exception with a helpful suggestion.

How should you know to use LoggerInterface for the type-hint? You can either read the docs for whatever feature you're using, or get a list of autowireable type-hints by running:

1
$ php bin/console debug:autowiring

This is just a small subset of the output:

Service ID Class name
Psr\Cache\CacheItemPoolInterface alias for cache.app.recorder
Psr\Log\LoggerInterface alias for monolog.logger
Symfony\Component\EventDispatcher\EventDispatcherInterface alias for debug.event_dispatcher
Symfony\Component\HttpFoundation\RequestStack alias for request_stack
Symfony\Component\HttpFoundation\Session\SessionInterface alias for session
Symfony\Component\Routing\RouterInterface alias for router.default

Handling Multiple Services

Suppose you also want to email a site administrator each time a site update is made. To do that, you create a new class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// src/AppBundle/Updates/SiteUpdateManager.php
namespace AppBundle\Updates;

use AppBundle\Service\MessageGenerator;

class SiteUpdateManager
{
    private $messageGenerator;
    private $mailer;

    public function __construct(MessageGenerator $messageGenerator, \Swift_Mailer $mailer)
    {
        $this->messageGenerator = $messageGenerator;
        $this->mailer = $mailer;
    }

    public function notifyOfSiteUpdate()
    {
        $happyMessage = $this->messageGenerator->getHappyMessage();

        $message = (new \Swift_Message('Site update just happened!'))
            ->setFrom('admin@example.com')
            ->setTo('manager@example.com')
            ->addPart(
                'Someone just updated the site. We told them: '.$happyMessage
            );

        return $this->mailer->send($message) > 0;
    }
}

This uses the MessageGenerator and the Swift_Mailer service. As long as you're loading all services from src/AppBundle, you can use the service immediately:

1
2
3
4
5
6
7
8
9
10
11
12
use AppBundle\Updates\SiteUpdateManager;

public function newAction(SiteUpdateManager $siteUpdateManager)
{
    // ...

    if ($siteUpdateManager->notifyOfSiteUpdate()) {
        $this->addFlash('success', 'Notification mail was sent successfully.');
    }

    // ...
}

Thanks to autowiring and your type-hints in __construct(), the container creates the SiteUpdateManager object and passes it the correct argument. In most cases, this works perfectly.

Manually Wiring Arguments

But there are a few cases when an argument to a service cannot be autowired. For example, suppose you want to make the admin email configurable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// src/AppBundle/Updates/SiteUpdateManager.php
// ...

class SiteUpdateManager
{
    // ...
+    private $adminEmail;

-    public function __construct(MessageGenerator $messageGenerator, \Swift_Mailer $mailer)
+    public function __construct(MessageGenerator $messageGenerator, \Swift_Mailer $mailer, $adminEmail)
    {
        // ...
+        $this->adminEmail = $adminEmail;
    }

    public function notifyOfSiteUpdate()
    {
        // ...

        $message = \Swift_Message::newInstance()
            // ...
-            ->setTo('manager@example.com')
+            ->setTo($this->adminEmail)
            // ...
        ;
        // ...
    }
}

If you make this change and refresh, you'll see an error:

1
2
Cannot autowire service "AppBundle\Updates\SiteUpdateManager": argument "$adminEmail"
of method "__construct()" must have a type-hint or be given a value explicitly.

That makes sense! There is no way that the container knows what value you want to pass here. No problem! In your configuration, you can explicitly set this argument:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
# app/config/services.yml
services:
    # ... same as before

    AppBundle\:
        resource: '../../src/AppBundle/*'
        exclude: '../../src/AppBundle/{Entity,Repository}'

    # explicitly configure the service
    AppBundle\Updates\SiteUpdateManager:
        arguments:
            $adminEmail: 'manager@example.com'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- app/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
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <!-- ...  same as before -->

        <prototype namespace="AppBundle\"
            resource="../../src/AppBundle/*"
            exclude="../../src/AppBundle/{Entity,Repository}"/>

        <!-- explicitly configure the service -->
        <service id="AppBundle\Updates\SiteUpdateManager">
            <argument key="$adminEmail">manager@example.com</argument>
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// app/config/services.php
use AppBundle\Updates\SiteUpdateManager;
use Symfony\Component\DependencyInjection\Definition;

// ... same as before

// $this is a reference to the current loader
$this->registerClasses(
    $defaults,
    'AppBundle\\',
    '../../src/AppBundle/*',
    // excludes
    '../../src/AppBundle/{Entity,Repository}'
);

// explicitly configure the service
$container->getDefinition(SiteUpdateManager::class)
    ->setArgument('$adminEmail', 'manager@example.com')
;

3.3

The ability to configure an argument by its name ($adminEmail) was introduced in Symfony 3.3. Previously, you could configure it only by its index (2 in this case) or by using empty quotes for the other arguments.

Thanks to this, the container will pass manager@example.com as the third argument to __construct when creating the SiteUpdateManager service. The other arguments will still be autowired.

But, isn't this fragile? Fortunately, no! If you rename the $adminEmail argument to something else - e.g. $mainEmail - you will get a clear exception when you reload the next page (even if that page doesn't use this service).

Service Parameters

In addition to holding service objects, the container also holds configuration, called parameters. To create a parameter, add it under the parameters key and reference it with the %parameter_name% syntax:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
# app/config/services.yml
parameters:
    admin_email: manager@example.com

services:
    # ... same as before

    AppBundle\Updates\SiteUpdateManager:
        arguments:
            $adminEmail: '%admin_email%'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- app/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
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <parameters>
        <parameter key="admin_email">manager@example.com</parameter>
    </parameters>

    <services>
        <!-- ... same as before -->

        <service id="AppBundle\Updates\SiteUpdateManager">
            <argument key="$adminEmail">%admin_email%</argument>
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
// app/config/services.php
use AppBundle\Updates\SiteUpdateManager;

// ... same as before

$container->setParameter('admin_email', 'manager@example.com');

$container->getDefinition(SiteUpdateManager::class)
    ->setArgument('$adminEmail', '%admin_email%')
;

Actually, once you define a parameter, it can be referenced via the %parameter_name% syntax in any other service configuration file - like config.yml. Many parameters are defined in a parameters.yml file.

You can then fetch the parameter in the service:

1
2
3
4
5
6
7
8
9
10
11
class SiteUpdateManager
{
    // ...

    private $adminEmail;

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

You can also fetch parameters directly from the container:

1
2
3
4
5
6
7
8
9
10
public function newAction()
{
    // ...

    // this ONLY works if you extend Controller
    $adminEmail = $this->container->getParameter('admin_email');

    // or a shorter way!
    // $adminEmail = $this->getParameter('admin_email');
}

For more info about parameters, see Introduction to Parameters.

Choose a Specific Service

The MessageGenerator service created earlier requires a LoggerInterface argument:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/AppBundle/Service/MessageGenerator.php
// ...

use Psr\Log\LoggerInterface;

class MessageGenerator
{
    private $logger;

    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }
    // ...
}

However, there are multiple services in the container that implement LoggerInterface, such as logger, monolog.logger.request, monolog.logger.php, etc. How does the container know which one to use?

In these situations, the container is usually configured to automatically choose one of the services - logger in this case (read more about why in Defining Services Dependencies Automatically (Autowiring)). But, you can control this and pass in a different logger:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
# app/config/services.yml
services:
    # ... same code as before

    # explicitly configure the service
    AppBundle\Service\MessageGenerator:
        arguments:
            $logger: '@monolog.logger.request'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- app/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
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <!-- ... same code as before -->

        <!-- explicitly configure the service -->
        <service id="AppBundle\Service\MessageGenerator">
            <argument key="$logger" type="service" id="monolog.logger.request"/>
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
// app/config/services.php
use AppBundle\Service\MessageGenerator;
use Symfony\Component\DependencyInjection\Reference;

// ... same as before

// explicitly configure the service
$container->getDefinition(MessageGenerator::class)
    ->setArgument('$logger', new Reference('monolog.logger.request'))
;

This tells the container that the $logger argument to __construct should use service whose id is monolog.logger.request.

Tip

The @ symbol is important: that's what tells the container you want to pass the service whose id is monolog.logger.request, and not just the string monolog.logger.request.

Binding Arguments by Name or Type

You can also use the bind keyword to bind specific arguments by name or type:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# config/services.yaml
services:
    _defaults:
        bind:
            # pass this value to any $adminEmail argument for any service
            # that's defined in this file (including controller arguments)
            $adminEmail: 'manager@example.com'

            # pass this service to any $requestLogger argument for any
            # service that's defined in this file
            $requestLogger: '@monolog.logger.request'

            # pass this service for any LoggerInterface type-hint for any
            # service that's defined in this file
            Psr\Log\LoggerInterface: '@monolog.logger.request'

    # ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 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
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <defaults autowire="true" autoconfigure="true" public="false">
            <bind key="$adminEmail">manager@example.com</bind>
            <bind key="$requestLogger"
                type="service"
                id="monolog.logger.request"
            />
            <bind key="Psr\Log\LoggerInterface"
                type="service"
                id="monolog.logger.request"
            />
        </defaults>

        <!-- ... -->
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// config/services.php
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

$defaults = new Definition();
$defaults
    ->setAutowired(true)
    ->setAutoconfigured(true)
    ->setPublic(false)
    ->setBinds([
        // pass this value to any $adminEmail argument for any service
        // that's defined in this file (including controller arguments)
        '$adminEmail' => 'manager@example.com',

        // pass this service to any $requestLogger argument for any
        // service that's defined in this file
        '$requestLogger' => new Reference('@monolog.logger.request'),

        // pass this service for any LoggerInterface type-hint for any
        // service that's defined in this file
        LoggerInterface::class => new Reference('monolog.logger.request'),
    ])
;

By putting the bind key under _defaults, you can specify the value of any argument for any service defined in this file! You can bind arguments by name (e.g. $adminEmail) or by type (e.g. Psr\Log\LoggerInterface).

The bind config can also be applied to specific services or when loading many services at once (i.e. Service Container).

The autowire Option

Above, the services.yml file has autowire: true in the _defaults section so that it applies to all services defined in that file. With this setting, you're able to type-hint arguments in the __construct() method of your services and the container will automatically pass you the correct arguments. This entire entry has been written around autowiring.

For more details about autowiring, check out Defining Services Dependencies Automatically (Autowiring).

The autoconfigure Option

3.3

The autoconfigure option was introduced in Symfony 3.3.

Above, the services.yml file has autoconfigure: true in the _defaults section so that it applies to all services defined in that file. With this setting, the container will automatically apply certain configuration to your services, based on your service's class. This is mostly used to auto-tag your services.

For example, to create a Twig Extension, you need to create a class, register it as a service, and tag it with twig.extension:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
# app/config/services.yml
services:
    # ...

    AppBundle\Twig\MyTwigExtension:
        tags: [twig.extension]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- app/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
        https://symfony.com/schema/dic/services/services-1.0.xsd">

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

        <service id="AppBundle\Twig\MyTwigExtension">
            <tag name="twig.extension"/>
        </service>
    </services>
</container>
1
2
3
4
5
6
7
// app/config/services.php
use AppBundle\Twig\MyTwigExtension;

// ...
$container->getDefinition(MyTwigExtension::class)
    ->addTag('twig.extension')
;

But, with autoconfigure: true, you don't need the tag. In fact, if you're using the Symfony Standard Edition services.yml config, you don't need to do anything: the service will be automatically loaded. Then, autoconfigure will add the twig.extension tag for you, because your class implements Twig\Extension\ExtensionInterface. And thanks to autowire, you can even add constructor arguments without any configuration.

You can still manually configure the service if you need to.

Public Versus Private Services

Thanks to the _defaults section in services.yml, every service defined in this file is public: false by default:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
# app/config/services.yml
services:
    # default configuration for services in *this* file
    _defaults:
        # ...
        public: false
1
2
3
4
5
6
7
8
9
10
11
12
<!-- app/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
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <!-- Default configuration for services in *this* file -->
        <defaults autowire="true" autoconfigure="true" public="false"/>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
// app/config/services.php
<?php

use Symfony\Component\DependencyInjection\Definition;

// To use as default configuration for services in *this* file
$defaults = new Definition();
$defaults
    // ...
    ->setPublic(false)
;

What does this mean? When a service is not public, you cannot access it directly from the container:

1
2
3
4
5
6
7
8
9
use AppBundle\Service\MessageGenerator;

public function newAction(MessageGenerator $messageGenerator)
{
    // type-hinting it as an argument DOES work

    // but accessing it directly from the container does NOT Work
    $this->container->get(MessageGenerator::class);
}

Usually, this is OK: there are better ways to access a service. But, if you do need to make your service public, just override this setting:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
# app/config/services.yml
services:
    # ... same code as before

    # explicitly configure the service
    AppBundle\Service\MessageGenerator:
        public: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- app/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
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <!-- ... same code as before -->

        <!-- Explicitly configure the service -->
        <service id="AppBundle\Service\MessageGenerator" public="true"></service>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
// app/config/services.php
<?php

use AppBundle\Service\MessageGenerator;

// ... same code as before

$container->getDefinition(MessageGenerator::class)
    ->setPublic(true)
;

Importing Many Services at once with resource

You've already seen that you can import many services at once by using the resource key. For example, the default Symfony configuration contains this:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# app/config/services.yml
services:
    # ... same as before

    # the namespace prefix for classes (must end in \)
    AppBundle\:
        # create services for all the classes found in this directory...
        resource: '../../src/AppBundle/*'
        # ...except for the classes located in these directories
        exclude: '../../src/AppBundle/{Entity,Repository}'

    # these were imported above, but we want to add the tag
    AppBundle\Controller\:
        resource: '../../src/AppBundle/Controller'
        # apply some configuration to these services
        tags: ['controller.service_arguments']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- app/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
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <!-- ... same as before -->

        <!-- the namespace prefix for classes (must end in \) -->
        <prototype namespace="AppBundle\"
            resource="../../src/AppBundle/*"
            exclude="../../src/AppBundle/{Entity,Repository}"/>
            <!-- create services for all the classes found in this directory...
                 ...except for the classes located in these directories -->

        <!-- these were imported above, but we want to add the tag -->
        <prototype namespace="AppBundle\Controller\" resource="../../src/AppBundle/Controller" public="true">
            <tag name="controller.service_arguments"/>
        </prototype>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// app/config/services.php
// ... same as before

// $this is a reference to the current loader
$this->registerClasses(
    $defaults,
    // the namespace prefix for classes (must end in \)
    'AppBundle\\',
    // create services for all the classes found in this directory...
    '../../src/AppBundle/*',
    '../../src/AppBundle/{Entity,Repository}'
);

// changes default config
$defaults->addTag('controller.service_arguments');

// these were imported above, but we want to the tag
$this->registerClasses(
    $defaults,
    'AppBundle\\Controller\\',
    '../../src/AppBundle/Controller/*'
);

Tip

The value of the resource and exclude options can be any valid glob pattern.

This can be used to quickly make many classes available as services and apply some default configuration. The id of each service is its fully-qualified class name. You can override any service that's imported by using its id (class name) below (e.g. see Service Container). If you override a service, none of the options (e.g. public) are inherited from the import (but the overridden service does still inherit from _defaults).

You can also exclude certain paths. This is optional, but will slightly increase performance in the dev environment: excluded paths are not tracked and so modifying them will not cause the container to be rebuilt.

Note

Wait, does this mean that every class in src/AppBundle is registered as a service? Even model or entity classes? Actually, no. As long as you have public: false under your _defaults key (or you can add it under the specific import), all the imported services are private. Thanks to this, all classes in src/AppBundle that are not explicitly used as services are automatically removed from the final container. In reality, the import simply means that all classes are "available to be used as services" without needing to be manually configured.

Multiple Service Definitions Using the Same Namespace

3.4

The namespace option in the YAML configuration was introduced in Symfony 3.4.

If you define services using the YAML config format, the PHP namespace is used as the key of each configuration, so you can't define different service configs for classes under the same namespace:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
# app/config/services.yml
services:
    AppBundle\Domain\:
        resource: '../../src/AppBundleDomain/*'

    # ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- app/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
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <prototype namespace="AppBundle\Domain"
            resource="../../src/AppBundle/Domain/*"/>
        </prototype>

        <!-- ... -->
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
// app/config/services.php
use Symfony\Component\DependencyInjection\Definition;

$defaults = new Definition();

// $this is a reference to the current loader
$this->registerClasses(
    $defaults,
    'AppBundle\\Domain\\',
    '../../src/AppBundle/Domain/*'
);

// ...

In order to have multiple definitions, add the namespace option and use any unique string as the key of each service config:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
# app/config/services.yml
services:
    command_handlers:
        namespace: App\Domain\
        resource: '../../src/Domain/*/CommandHandler'
        tags: [command_handler]

    event_subscribers:
        namespace: App\Domain\
        resource: '../../src/Domain/*/EventSubscriber'
        tags: [event_subscriber]

# ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- app/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
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <prototype namespace="AppBundle\Domain"
            resource="../../src/AppBundle/Domain/*/CommandHandler"/>
            <tag name="command_handler"/>
        </prototype>

        <prototype namespace="AppBundle\Domain"
            resource="../../src/AppBundle/Domain/*/EventSubscriber"/>
            <tag name="event_subscriber"/>
        </prototype>

        <!-- ... -->
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// app/config/services.php
use Symfony\Component\DependencyInjection\Definition;

$commandHandlers = new Definition();
$commandHandlers->addTag('command_handler');

// $this is a reference to the current loader
$this->registerClasses(
    $commandHandlers,
    'AppBundle\\Domain\\',
    '../../src/AppBundle/Domain/*/CommandHandler'
);

$eventSubscribers = new Definition();
$eventSubscribers->addTag('event_subscriber');

$this->registerClasses(
    $eventSubscribers,
    'AppBundle\\Domain\\',
    '../../src/AppBundle/Domain/*/EventSubscriber'
);

Explicitly Configuring Services and Arguments

Prior to Symfony 3.3, all services and (typically) arguments were explicitly configured: it was not possible to load services automatically and autowiring was much less common.

Both of these features are optional. And even if you use them, there may be some cases where you want to manually wire a service. For example, suppose that you want to register 2 services for the SiteUpdateManager class - each with a different admin email. In this case, each needs to have a unique service id:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# app/config/services.yml
services:
    # ...

    # this is the service's id
    site_update_manager.superadmin:
        class: AppBundle\Updates\SiteUpdateManager
        # you CAN still use autowiring: we just want to show what it looks like without
        autowire: false
        # manually wire all arguments
        arguments:
            - '@AppBundle\Service\MessageGenerator'
            - '@mailer'
            - 'superadmin@example.com'

    site_update_manager.normal_users:
        class: AppBundle\Updates\SiteUpdateManager
        autowire: false
        arguments:
            - '@AppBundle\Service\MessageGenerator'
            - '@mailer'
            - 'contact@example.com'

    # Create an alias, so that - by default - if you type-hint SiteUpdateManager,
    # the site_update_manager.superadmin will be used
    AppBundle\Updates\SiteUpdateManager: '@site_update_manager.superadmin'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!-- app/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
        https://symfony.com/schema/dic/services/services-1.0.xsd">

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

        <!-- you CAN still use autowiring: we just want to show what it looks like without -->
        <service id="site_update_manager.superadmin"
            class="AppBundle\Updates\SiteUpdateManager"
            autowire="false">
            <!-- manually wire all arguments -->
            <argument type="service" id="AppBundle\Service\MessageGenerator"/>
            <argument type="service" id="mailer"/>
            <argument>superadmin@example.com</argument>
        </service>

        <service id="site_update_manager.normal_users"
            class="AppBundle\Updates\SiteUpdateManager"
            autowire="false">
            <argument type="service" id="AppBundle\Service\MessageGenerator"/>
            <argument type="service" id="mailer"/>
            <argument>contact@example.com</argument>
        </service>

        <!-- Create an alias, so that - by default - if you type-hint SiteUpdateManager,
             the site_update_manager.superadmin will be used -->
        <service id="AppBundle\Updates\SiteUpdateManager" alias="site_update_manager.superadmin"/>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// app/config/services.php
use AppBundle\Service\MessageGenerator;
use AppBundle\Updates\SiteUpdateManager;
use Symfony\Component\DependencyInjection\Reference;

// register the service by its id
$container->register('site_update_manager.superadmin', SiteUpdateManager::class)
    // you CAN still use autowiring: we just want to show what it looks like without
    ->setAutowired(false)
    // manually wire all arguments
    ->setArguments([
        new Reference(MessageGenerator::class),
        new Reference('mailer'),
        'superadmin@example.com'
    ])
;

$container->register('site_update_manager.normal_users', SiteUpdateManager::class)
    ->setAutowired(false)
    ->setArguments([
        new Reference(MessageGenerator::class),
        new Reference('mailer'),
        'contact@example.com'
    ])
;

// Create an alias, so that - by default - if you type-hint SiteUpdateManager,
// the site_update_manager.superadmin will be used
$container->setAlias(SiteUpdateManager::class, 'site_update_manager.superadmin')

In this case, two services are registered: site_update_manager.superadmin and site_update_manager.normal_users. Thanks to the alias, if you type-hint SiteUpdateManager the first (site_update_manager.superadmin) will be passed. If you want to pass the second, you'll need to manually wire the service.

Caution

If you do not create the alias and are loading all services from src/AppBundle, then three services have been created (the automatic service + your two services) and the automatically loaded service will be passed - by default - when you type-hint SiteUpdateManager. That's why creating the alias is a good idea.

Learn more

  • The Symfony 3.3 DI Container Changes Explained (autowiring, _defaults, etc)
  • How to Create Service Aliases and Mark Services as Private
  • Defining Services Dependencies Automatically (Autowiring)
  • Service Method Calls and Setter Injection
  • How to Work with Compiler Passes in Bundles
  • How to Configure a Service with a Configurator
  • How to Debug the Service Container & List Services
  • How to work with Service Definition Objects
  • How to Inject Values Based on Complex Expressions
  • Using a Factory to Create Services
  • How to Import Configuration Files/Resources
  • Types of Injection
  • Lazy Services
  • How to Make Service Arguments/References Optional
  • Introduction to Parameters
  • How to Manage Common Dependencies with Parent Services
  • How to Retrieve the Request from the Service Container
  • How to Decorate Services
  • Service Subscribers & Locators
  • How to Define Non Shared Services
  • How to Inject Instances into the Container
  • How to Work with Service Tags
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version
    We stand with Ukraine.
    Version:
    Measure & Improve Symfony Code Performance

    Measure & Improve Symfony Code Performance

    Check Code Performance in Dev, Test, Staging & Production

    Check Code Performance in Dev, Test, Staging & Production

    Symfony footer

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

    Avatar of Florian Guimier, a Symfony contributor

    Thanks Florian Guimier for being a Symfony contributor

    1 commit • 115 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