Développeuse PHP Symfony
@imenezzine
@imenezzine1
Les principes de l'Injection de Dépendance (DI)
La configuration du container de services Symfony
L'évolution du composant dans le temps
L’utilisation des Attributs pour configurer les services
➡️ L'Injection de Dépendance (DI) est un pattern de conception qui permet de réduire le couplage entre les différentes parties d'une application.
Au lieu que les classes créent leurs dépendances elles-mêmes, elles reçoivent ces dépendances de l'extérieur, généralement via leur constructeur.
➡️ Le composant DependencyInjection est un outil clé de Symfony qui permet de gérer les dépendances des services de manière centralisée et efficace.
➡️ Au lieu de créer et gérer manuellement les objets, Symfony s’en charge pour nous en utilisant un conteneur de services.
Séparation des préoccupations
Testabilité
Maintenance
Compatible avec les principes SOLID
// Avant DI
class UserController {
private $userService;
public function __construct() {
$this->userService = new UserService(); // ❌ Couplage fort
}
}
// Après DI
class UserController {
private $userService;
public function __construct(UserService $userService) {
$this->userService = $userService; // Dépendance injectée via le constructeur
}
}
Un vrai tournant dans l'histoire du framework !
services:
app.my_service:
class: App\Service\MyService
arguments:
$dependency: '@app.dependency'
services:
App\Service\UserService: ~ # Autowiring activé automatiquement
autowire: true
namespace App\Controller;
use App\Service\UserService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class UserController extends AbstractController
{
public function index(UserService $userService)
{
$users = $userService->getAllUsers();
return $this->render('users/index.html.twig', ['users' => $users]);
}
}
services:
_defaults:
autoconfigure: true # Active l'autoconfiguration pour tous les services
<?php
namespace AppBundle\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class ExceptionSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
// return the subscribed events, their methods and priorities
return [
KernelEvents::EXCEPTION => [
['processException', 10],
],
];
}
public function processException(GetResponseForExceptionEvent $event)
{
// ...
}
}
La performance (on ne crée les services que lorsqu’ils sont utilisés)
La modularité (on injecte un sous-ensemble de services)
# app/config/services.yml
services:
app.command_handler_locator:
class: Symfony\Component\DependencyInjection\ServiceLocator
tags: ['container.service_locator']
arguments:
AppBundle\FooCommand: '@app.command_handler.foo'
AppBundle\BarCommand: '@app.command_handler.bar'
AppBundle\CommandBus:
arguments: ['@app.command_handler_locator']
#[Autowire]
use Symfony\Component\DependencyInjection\Attribute\Autowire;
class MyService {
public function __construct(
#[Autowire(service: 'my_dependency')] private MyDependency $dependency
) {}
}
#[Autoconfigure]
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
#[Autoconfigure]
class MyService {}
#[Autowire]
Nouveaux paramètres : env
, param
et service
class MyService {
public function __construct(
#[Autowire(env: 'APP_SECRET')] private string $appSecret
) {}
}
#[AsAlias]
use Symfony\Component\DependencyInjection\Attribute\AsAlias;
#[AsAlias('my_service')]
class MyService {}
#[
AutowireServiceClosure]
namespace App\Service;
use App\Service\Remote\MessageFormatter;
use Symfony\Component\DependencyInjection\Attribute\AutowireServiceClosure;
class MessageGenerator
{
public function __construct(
#[AutowireServiceClosure('third_party.remote_message_formatter')]
private \Closure $messageFormatterResolver,
) {
}
public function generate(string $message): void
{
$formattedMessage = ($this->messageFormatterResolver)()->format($message);
// ...
}
}
#[
AutowireCallable]
// src/Service/MessageGenerator.php
namespace App\Service;
use Symfony\Component\DependencyInjection\Attribute\AutowireCallable;
class MessageGenerator
{
public function __construct(
#[AutowireCallable(service: 'third_party.remote_message_formatter', method: 'format')]
private \Closure $formatCallable,
) {
}
public function generate(string $message): void
{
$formattedMessage = ($this->formatCallable)($message);
// ...
}
}
#[AutowireMethodOf]
// src/Service/MessageGenerator.php
namespace App\Service;
use Symfony\Component\DependencyInjection\Attribute\AutowireMethodOf;
class MessageGenerator
{
public function __construct(
#[AutowireMethodOf('third_party.remote_message_formatter')]
private \Closure $format,
) {
}
public function generate(string $message): void
{
$formattedMessage = ($this->format)($message);
// ...
}
}
#[AutowireInline]
use Symfony\Component\DependencyInjection\Attribute\AutowireInline;
class MyService {
public function __construct(
#[AutowireInline] private MyDependency $dependency
) {}
}
✅ Moins de configuration YAML/XML
✅ Plus de flexibilité grâce aux attributs PHP
✅ Meilleure lisibilité et maintenabilité du code
➡️ Depuis Symfony 6, on observe une nette simplification de l’injection de dépendances :
Le composant DependencyInjection continue d’évoluer vers plus de simplicité et d’efficacité.
Grâce aux nouvelles fonctionnalités, il est plus facile de structurer ses services et d’écrire du code maintenable et performant.