Introdução à Injeção de Dependência (DI) em PHP
O desenvolvimento moderno em PHP é impossível sem a compreensão dos princípios de baixo acoplamento entre componentes. Dependency Injection (DI), ou injeção de dependências, é um dos padrões de design chave que permite criar aplicações flexíveis, testáveis e facilmente mantíveis. Em vez de a classe criar suas próprias dependências (por exemplo, um objeto de banco de dados ou logger), ela as recebe de fora. Isso muda radicalmente a arquitetura do código.
Neste artigo, vamos detalhar o que é Dependency Injection em PHP, quais são as formas de injeção, como funciona um contêiner DI e por que esse padrão se tornou padrão para frameworks modernos, como Laravel, Symfony e Yii2.
1. O Problema: Acoplamento Rígido (Tight Coupling)
Antes de falar sobre a solução, vamos ver o problema. Um exemplo clássico é a classe UserController, que cria diretamente um objeto Database dentro de si:
<?phpclass Database { public function query(string $sql): array { // Conexão ao BD e execução da consulta return []; }}
class UserController { private Database $db;
public function __construct() { // Dependência rígida — criamos o objeto dentro do construtor $this->db = new Database(); }
public function getUsers(): array { return $this->db->query('SELECT * FROM users'); }}?>Desvantagens dessa abordagem:
- Dificuldade de teste: Você não conseguirá substituir
Databasepor um objeto mock em testes unitários. - Violação do Princípio da Responsabilidade Única (SRP): A classe é responsável não apenas pela lógica de negócio, mas também pela criação de dependências.
- Dificuldade de modificação: Se quiser substituir MySQL por PostgreSQL, terá que alterar o código em todas as classes onde
Databaseé criado.
2. A Solução: Injeção de Dependências (Dependency Injection)
Dependency Injection propõe passar as dependências de fora. Existem três formas principais de injeção: via construtor, via setters e via interface (método). Vamos analisar cada uma.
2.1. Injeção via Construtor (Constructor Injection)
A forma mais comum e recomendada. As dependências são passadas uma vez na criação do objeto e não podem ser alteradas posteriormente.
<?phpclass UserController { private Database $db;
// A dependência é passada de fora public function __construct(Database $db) { $this->db = $db; }
public function getUsers(): array { return $this->db->query('SELECT * FROM users'); }}
// Uso:$db = new Database();$controller = new UserController($db);?>Vantagens: Garante que a dependência esteja sempre disponível; ideal para dependências obrigatórias. Desvantagens: Se a classe tiver muitas dependências, o construtor se torna volumoso.
2.2. Injeção via Setters (Setter Injection)
Adequado para dependências opcionais, que podem ser definidas após a criação do objeto.
<?phpclass Logger { public function log(string $message): void { echo $message; }}
class UserController { private ?Logger $logger = null;
public function setLogger(Logger $logger): void { $this->logger = $logger; }
public function getUsers(): array { if ($this->logger) { $this->logger->log('Buscando usuários...'); } // Lógica de obtenção de usuários return []; }}
// Uso:$controller = new UserController();$controller->setLogger(new Logger());?>Vantagens: