L'Injection de Dépendance dans Symfony : Une Force Cachée


Imen EZZINE
@imenezzine
@imenezzine1
Développeuse PHP Symfony









-
Symfony 1.x : une approche rigide
-

Text
class ReportGenerator
{
public function generateAndStore(array $data, string $fileName): void
{
$pdfGenerator = new PdfGenerator('/usr/local/bin/wkhtmltopdf');
$pdfContent = $pdfGenerator->create($data);
$storage = new FileStorage('/var/reports');
$storage->save($fileName, $pdfContent);
$logger = new Logger('/var/log/app.log');
$logger->log('Report ' . $fileName . ' successfully generated and stored.');
}
}
- Les dépendances...
-
- ne sont pas modifiables.
-
- ne sont pas configurables.
-
- seront toujours testées avec votre code.
-
- ne sont pas explicites.
-
-
Symfony 1.x : une approche rigide
-
-



Symfony 2 est sorti le 28 juillet 2011. 🎉
-
Le composant DependencyInjection,
-
L’architecture bundle,
-
L’intégration poussée avec Composer (arrivé un peu plus tard),
-
L’approche RESTful, l’autoloader PSR-0, etc.

Un vrai tournant dans l'histoire du framework !
class ReportGenerator
{
private $pdfGenerator;
private $storage;
private $logger;
public function __construct(PdfGenerator $pdfGenerator, FileStorage $storage, LoggerInterface $logger)
{
$this->pdfGenerator = $pdfGenerator;
$this->storage = $storage;
$this->logger = $logger;
}
public function generateAndStore(array $data, string $fileName): void
{
$pdfContent = $this->pdfGenerator->create($data);
$this->storage->save($fileName, $pdfContent);
$this->logger->info('Report ' . $fileName . ' successfully generated and stored.');
}
}

🤔 Le composant DependencyInjection, c'est quoi ?
🤔 Le composant DependencyInjection, c'est quoi ?

- - Le composant DependencyInjection de Symfony permet de gérer et injecter automatiquement les dépendances entre les classes de ton application.
- Il repose sur un conteneur de services qui centralise la création, la configuration et la mise à disposition des objets.
- Cela permet un code plus modulaire, testable et maintenable, en suivant les principes de l’injection de dépendances et de l’inversion de contrôle.
Text


Text

Service Locator
Introduit une refonte du container DI avec le support du PSR-11 (ContainerInterface)
-
La performance (on ne crée les services que lorsqu’ils sont utilisés)
-
La modularité (on injecte un sous-ensemble de services)
Text

Service Locator
# 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']
Text

Service Locator
// src/AppBundle/CommandBus.php
namespace AppBundle;
use Psr\Container\ContainerInterface;
class CommandBus
{
private $locator;
public function __construct(ContainerInterface $locator)
{
$this->locator = $locator;
}
public function handle($command)
{
$commandClass = get_class($command);
if ($this->locator->has($commandClass)) {
$handler = $this->locator->get($commandClass);
return $handler->handle($command);
}
throw new \LogicException(sprintf('No handler found for command "%s".', $commandClass));
}
}
Symfony 3 : Simplification avec l'autowiring
services:
App\Service\UserService: ~ # Autowiring activé automatiquement
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
App\EventListener\MyEventListener: ~
Symfony 4 et au-delà : Évolutions modernes
Autoconfiguration
namespace App\EventListener;
use Symfony\Component\HttpKernel\Event\RequestEvent;
class MyEventListener
{
public function onKernelRequest(RequestEvent $event)
{
// Logique à exécuter lors de l'événement
}
}
DATABASE_URL=mysql://user:password@127.0.0.1:3306/mydb
Symfony 4 et au-delà : Évolutions modernes
Gestion des variables d'environnement
parameters:
database_url: '%env(DATABASE_URL)%'
services:
App\Service\DatabaseService:
arguments:
$url: '%database_url%'

Symfony 5
Utilisation des attributs
namespace App\Service;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
class MyService
{
public function __construct(
#[Autowire(service: 'logger')] private LoggerInterface $logger,
) {}
}


L'ajout de ce composant a eu plusieurs effets importants :
-
Révolution de l'écosystème PHP : Symfony 2.0 a bouleversé la façon dont les applications PHP étaient structurées et développées3.
-
Nouvelle philosophie : Le concept de services et l'injection de dépendances ont introduit une nouvelle approche dans la conception des applications Symfony3.
-
Apprentissage nécessaire : Les développeurs ont dû réapprendre le framework, car la version 2.0 était radicalement différente de la précédente3.
Impacts de l'introduction
✅ Découplage
✅ Facilité de maintenance
✅ Testabilité améliorée
✅ Performance optimisée

Depuis son introduction, le composant d'injection de dépendances a continué d'évoluer :
-
Il est devenu plus puissant et central au framework au fil des versions1.
-
Les versions récentes de Symfony (comme Symfony 6) ont simplifié son utilisation avec des fonctionnalités comme l'autowiring et l'autoconfiguration4.
L'injection de dépendances reste un concept fondamental dans Symfony, permettant une meilleure organisation du code, une plus grande flexibilité et une maintenance plus aisée des applications.
Évolution du composant
class UserService {
private Database $db;
public function __construct() {
$this->db = new Database();
}
}
Avant et après le composant DependencyInjection
🔴 Avant Symfony et son conteneur de services
❌ Problèmes :
- Dépendance forte avec
Database
- Code rigide et difficilement testable
- Pas de gestion centralisée des services

🟢 Après : Symfony et son conteneur de services
use Symfony\Component\DependencyInjection\Attribute\Autowire;
class UserService {
public function __construct(
#[Autowire(service: 'database_connection')]
private Database $db
) {}
}
✅ Avantages :
- Code plus flexible et testable
- Configuration centralisée
- Meilleure maintenabilité

Symfony 6.1 : Introduction de #[Autowire]
use Symfony\Component\DependencyInjection\Attribute\Autowire;
class MyService {
public function __construct(
#[Autowire(service: 'my_dependency')] private MyDependency $dependency
) {}
}
✅ Avantages :
- Plus besoin de config YAML/XML
- Injection visible directement dans le code

Symfony 6.1 : #[Autoconfigure]
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
#[Autoconfigure]
class MyService {}
✅ Permet d’activer automatiquement l’autowiring
et les tags




Merci!
Des questions ?
Bento
By imenezzine
Bento
- 84