Introdução ao Dependency Injection em PHP
O desenvolvimento moderno em PHP é impensável sem os princípios de design orientado a objetos. Um dos padrões-chave que permite construir aplicações flexíveis, testáveis e de fácil manutenção é o Dependency Injection (DI) ou injeção de dependências.
A essência do padrão é simples: em vez de criar objetos dependentes dentro da classe, nós os passamos (injetamos) de fora. Isso muda radicalmente a arquitetura da aplicação, tornando as classes independentes de implementações específicas e simplificando seus testes.
Neste artigo, vamos analisar o que é Dependency Injection em PHP, quais são as formas de implementá-lo e como usar contêineres DI para automatizar o processo.
O problema: por que é ruim sem DI?
Vamos considerar um exemplo típico. A classe UserController é responsável por processar requisições relacionadas a usuários. Ela usa um banco de dados através da classe MysqlDatabase:
<?php
class MysqlDatabase { public function query(string $sql): array { // ... conexão com MySQL e execução da consulta return []; }}
class UserController { private MysqlDatabase $database;
public function __construct() { // Acoplamento rígido a uma implementação específica $this->database = new MysqlDatabase(); }
public function listUsers(): array { return $this->database->query('SELECT * FROM users'); }}
$controller = new UserController();Quais são os problemas deste código?
- Alto acoplamento:
UserControllerestá rigidamente ligado aMysqlDatabase. Se quisermos migrar para PostgreSQL ou usar armazenamento em arquivo, teremos que alterar o código do controller. - Dificuldade de teste: Você não conseguirá substituir o banco de dados por um objeto mock em testes unitários. Cada teste acessará o banco real, o que é lento e inconveniente.
- Violação do princípio da responsabilidade única: O controller não deveria saber como criar uma conexão com o banco de dados. Sua função é processar requisições.
A solução para este problema é aplicar o princípio da Inversão de Controle (IoC) e usar Dependency Injection.
Formas de injeção de dependências em PHP
Existem três formas principais de injetar uma dependência em uma classe:
1. Injeção via construtor (Constructor Injection)
A forma mais comum e recomendada. A dependência é passada através do parâmetro do construtor e geralmente é armazenada como uma propriedade privada da classe.
<?php
interface DatabaseInterface { public function query(string $sql): array;}
class MysqlDatabase implements DatabaseInterface { public function query(string $sql): array { // Implementação para MySQL return []; }}
class PostgresDatabase implements DatabaseInterface { public function query(string $sql): array { // Implementação para PostgreSQL return []; }}
class UserController { private DatabaseInterface $database;
// A dependência é injetada através do construtor public function __construct(DatabaseInterface $database) { $this->database = $database; }
public function listUsers(): array { return $this->database->query('SELECT * FROM users'); }}
// Uso$mysqlDb = new MysqlDatabase();$controller = new UserController($mysqlDb);
// Facilmente trocamos para outro BD$postgresDb = new PostgresDatabase();$controller2 = new UserController($postgresDb);Vantagens: A dependência é explicitamente visível, o objeto é criado em um estado válido (a dependência é obrigatória).
2. Injeção via setters (Setter Injection)
Adequado para dependências opcionais ou quando as dependên