Service container
Service container
Service container
Tool for managing class dependencies and performing dependency injection.
Automatic Service Loading
Thanks to this configuration, you can automatically use any classes from the src/ directory as a service, without needing to manually configure it.
If you'd prefer to manually wire your service, that's totally possible
# config/services.yaml
services:
# default configuration for services in *this* file
_defaults:
autowire: true
# Automatically injects dependencies in your services.
autoconfigure: true
# Automatically registers your services as commands, event subscribers, etc.
# When a service is autoconfigured, it means that Symfony
# will automatically tag it when possible.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# order is important in this file because service definitions
# always *replace* previous ones; add your own service configuration below
# ...
Injecting services/config into a service
The container will automatically know to pass the logger
service when instantiating the MessageGenerator
thanks toautowiring
. autowire:true in services.yml
Configure arguments
class MessageGenerator
{
public function __construct(
private LoggerInterface $logger,
) {
}
}
# config/services.yaml
services:
# ... same as before
# explicitly configure the service
App\Service\SiteUpdateManager:
arguments:
$adminEmail: 'manager@example.com'
Tags
Service tags are a way to tell Symfony or other third-party bundles that your service should be registered in some special way. Take the following example:
# config/services.yaml
services:
App\Twig\AppExtension:
tags: ['twig.extension']
Services tagged with the twig.extension
tag are collected during the initialization of TwigBundle and added to Twig as extensions.
Service parameters
services:
App\Service\SomeService:
arguments:
$emailSender: '%email_sender%'
#[Required] in setter
same as
services:
App\Service\MessageGenerator:
# ...
calls:
- setLogger: ['@logger']
Choose a specific service
We inject LoggerInterface
but it has multiple implementations such as logger
, monolog.logger.request
, monolog.logger.php
In these situations, the container is usually configured to automatically choose one of the service
But, you can control this and pass in a different logger:
# config/services.yaml
services:
# ... same code as before
# explicitly configure the service
App\Service\MessageGenerator:
arguments:
# 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'
$logger: '@monolog.logger.request'
public vs private services
By default every service is private. You cannot access them using $container->get()
As a best practice, you should only create private services and you should fetch services using dependency injection instead of using $container->get()
.
bind arguments globally
services:
_defaults:
bind:
$emailNotificationsEnabled: '%email_notifications_enabled%'
namespace App\Service;
class EmailSender
{
private $mailer;
private $emailNotificationsEnabled;
public function __construct(\Swift_Mailer $mailer, bool $emailNotificationsEnabled)
{
$this->mailer = $mailer;
$this->emailNotificationsEnabled = $emailNotificationsEnabled;
}
}
Later
more
service subscriber and locator
decorator service
factory service
service closures
Injecting a Closure as an Argument ??
Container Building Workflow
Binding Arguments by Name or Type ??
A Compiler Pass in Symfony is a way to manipulate or modify service definitions within the dependency injection container before it is fully compiled and used by the application.
What It's Useful For:
Customizing Service Definitions: You can add, remove, or alter services and their dependencies programmatically.
Tag Processing: It allows you to find all services tagged with a specific label and apply custom logic, such as registering them in a registry service.
Advanced Dependency Injection: You can dynamically modify service configurations that can't be predefined in static YAML or XML files.
Example Use Case:
Suppose you have multiple services tagged as app.custom_logger
. You can use a compiler pass to collect these services and inject them into a logger manager.
How It Works:
Create the Compiler Pass Class: Extend
CompilerPassInterface
and implement theprocess
method.Modify Service Container: Use the
ContainerBuilder
object to access and modify service definitions.Register the Compiler Pass: Add it to the
Kernel
in thebuild
method.
Code Example:
php
Copy code
namespace App\DependencyInjection;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class CustomLoggerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->has('app.logger_manager')) {
return;
}
$definition = $container->findDefinition('app.logger_manager');
$taggedServices = $container->findTaggedServiceIds('app.custom_logger');
foreach ($taggedServices as $id => $tags) {
$definition->addMethodCall('addLogger', [new Reference($id)]);
}
}
}
Register it in Kernel
:
php
Copy code
use App\DependencyInjection\CustomLoggerPass;
class Kernel extends BaseKernel
{
protected function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new CustomLoggerPass());
}
}
Summary:
Compiler Passes are powerful for customizing and optimizing service definitions during the container compilation process, providing flexibility for complex dependency injection scenarios.