Service Container
Warning: You are browsing the documentation for Symfony 2.x, which is no longer maintained.
Read the updated version of this page for Symfony 7.1 (the current stable version).
Your application is full of useful objects: one "Mailer" object might help you deliver email messages 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 is the heart of Symfony: it allows you to standardize and centralize the way objects are constructed. It makes your life easier, is super fast, and emphasizes an architecture that promotes reusable and decoupled code. It's also a big reason that Symfony is so fast and extensible!
Finally, configuring and using the service container is easy. By the end of this article, you'll be comfortable creating your own objects via the container and customizing objects from any third-party bundle. You'll begin writing code that is more reusable, testable and decoupled, simply because the service container makes writing good code so easy.
Fetching and using Services
The moment you start a Symfony app, the container already contains many services.
These are like tools, waiting for you to take advantage of them. In your controller,
you have access to the container via $this->container
. Want to log
something? No problem:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// src/AppBundle/Controller/ProductController.php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class ProductController extends Controller
{
/**
* @Route("/products")
*/
public function listAction()
{
$logger = $this->container->get('logger');
$logger->info('Look! I just used a service');
// ...
}
}
logger
is a unique key for the Logger
object. 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 app/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
=============================== ==================================================================
Throughout the docs, you'll see how to use the many different services that live in the container.
Container: Lazy-loaded for speed
If the container holds so many useful objects (services), does that mean those
objects are 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
You can also leverage the container to organize your own code into services. For example, suppose you want to show your users a random, happy message every time they do something. 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. Next, you can teach the service container how to instantiate it:
1 2 3 4 5
# app/config/services.yml
services:
app.message_generator:
class: AppBundle\Service\MessageGenerator
arguments: []
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
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="app.message_generator" class="AppBundle\Service\MessageGenerator">
</service>
</services>
</container>
1 2 3 4 5
// app/config/services.php
use AppBundle\Service\MessageGenerator;
$container->register('app.message_generator', MessageGenerator::class)
->setArguments(array());
That's it! Your service - with the unique key app.message_generator
- is now
available in the container. You can use it immediately inside your controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
public function newAction()
{
// ...
// the container will instantiate a new MessageGenerator()
$messageGenerator = $this->container->get('app.message_generator');
// or use this shorter syntax
// $messageGenerator = $this->get('app.message_generator');
$message = $messageGenerator->getHappyMessage();
$this->addFlash('success', $message);
// ...
}
When you ask for the app.message_generator
service, the container constructs
a new MessageGenerator
object and returns it. If you never ask for the
app.message_generator
service during a request, it's never constructed, saving
you memory and increasing the speed of your app. This also means that there's almost
no performance overhead for defining a lot of services.
As a bonus, the app.message_generator
service is only created once: the same
instance is returned each time you ask for it.
Injecting Services/Config into a Service
What if you want to use the logger
service from within MessageGenerator
?
Your service does not have a $this->container
property: that's a special power
only controllers have.
Instead, you should create a __construct()
method, add a $logger
argument
and set it on a $logger
property:
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!');
// ...
}
}
Tip
The LoggerInterface
type-hint in the __construct()
method is optional,
but a good idea. You can find the correct type-hint by reading the docs for the
service or by using the php app/console debug:container
console command.
Next, tell the container the service has a constructor argument:
1 2 3 4 5
# app/config/services.yml
services:
app.message_generator:
class: AppBundle\Service\MessageGenerator
arguments: ['@logger']
1 2 3 4 5 6 7 8 9 10 11 12 13
<!-- 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
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="app.message_generator" class="AppBundle\Service\MessageGenerator">
<argument type="service" id="logger" />
</service>
</services>
</container>
1 2 3 4 5 6
// app/config/services.php
use AppBundle\Service\MessageGenerator;
use Symfony\Component\DependencyInjection\Reference;
$container->register('app.message_generator', MessageGenerator::class)
->addArgument(new Reference('logger'));
That's it! The container now knows to pass the logger
service as an argument
when it instantiates the MessageGenerator
. This is called dependency injection.
The arguments
key holds an array of all of the constructor arguments to the
service (just 1 so far). The @
symbol before @logger
is important: it tells
Symfony to pass the service named logger
.
But you can pass anything as arguments. For example, suppose you want to make your class a bit more 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
// src/AppBundle/Service/MessageGenerator.php
// ...
use Psr\Log\LoggerInterface;
class MessageGenerator
{
private $logger;
private $loggingEnabled;
public function __construct(LoggerInterface $logger, $loggingEnabled)
{
$this->logger = $logger;
$this->loggingEnabled = $loggingEnabled;
}
public function getHappyMessage()
{
if ($this->loggingEnabled) {
$this->logger->info('About to find a happy message!');
}
// ...
}
}
The class now has a second constructor argument. No problem, just update your service config:
1 2 3 4 5
# app/config/services.yml
services:
app.message_generator:
class: AppBundle\Service\MessageGenerator
arguments: ['@logger', 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
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="app.message_generator" class="AppBundle\Service\MessageGenerator">
<argument type="service" id="logger" />
<argument>true</argument>
</service>
</services>
</container>
1 2 3 4 5 6 7 8 9
// app/config/services.php
use AppBundle\Service\MessageGenerator;
use Symfony\Component\DependencyInjection\Reference;
$container->register('app.message_generator', MessageGenerator::class)
->setArguments(array(
new Reference('logger'),
true,
));
You can even leverage environments to control this new value in different situations.
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:
1 2 3 4 5 6 7 8
# app/config/services.yml
parameters:
enable_generator_logging: true
services:
app.message_generator:
class: AppBundle\Service\MessageGenerator
arguments: ['@logger', '%enable_generator_logging%']
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<!-- 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
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<parameters>
<parameter key="enable_generator_logging">true</parameter>
</parameters>
<service id="app.message_generator" class="AppBundle\Service\MessageGenerator">
<argument type="service" id="logger" />
<argument>%enable_generator_logging%</argument>
</service>
</services>
</container>
1 2 3 4 5 6 7 8 9 10 11
// app/config/services.php
use AppBundle\Service\MessageGenerator;
use Symfony\Component\DependencyInjection\Reference;
$container->setParameter('enable_generator_logging', true);
$container->register('app.message_generator', MessageGenerator::class)
->setArguments(array(
new Reference('logger'),
'%enable_generator_logging%',
));
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
public function newAction()
{
// ...
$isLoggingEnabled = $this->container
->getParameter('enable_generator_logging');
// ...
}
Note
If you use a string that starts with @
or %
, you need to escape it by
adding another @
or %
:
1 2 3 4 5 6 7
# app/config/parameters.yml
parameters:
# This will be parsed as string '@securepass'
mailer_password: '@@securepass'
# Parsed as http://symfony.com/?foo=%s&bar=%d
url_pattern: 'http://symfony.com/?foo=%%s&bar=%%d'
For more info about parameters, see Introduction to Parameters.
Learn more
- 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 Work with Scopes
- How to Decorate Services
- How to Define Non Shared Services
- How to Inject Instances into the Container
- How to Work with Service Tags
- How to Work with Services Provided by Third-Party Bundles