Entendendo Refatoração em PHP
Refatoração é o processo de reestruturar código existente sem alterar seu comportamento externo. Em PHP, especialmente em sistemas legados, essa prática é fundamental para melhorar manutenibilidade, performance e reduzir bugs. Diferentemente de reescrever do zero, refatoração preserva funcionalidades enquanto elimina débito técnico acumulado.
A maioria dos projetos PHP legados sofre com código duplicado, funções gigantes, nomenclatura confusa e ausência de padrões. Refatorar esses sistemas não é luxo—é necessidade. Começamos identificando problemas: complexidade ciclomática alta, baixa coesão, alto acoplamento. Ferramentas como PHPStan e análise estática são aliadas nesse diagnóstico.
Técnicas Fundamentais de Refatoração
Extrair Métodos
Uma das refatorações mais poderosas é quebrar funções grandes em métodos menores. Tome este exemplo legado:
function processarPedido($pedidoId) {
$pedido = $this->db->query("SELECT * FROM pedidos WHERE id = $pedidoId");
$total = 0;
foreach ($pedido['itens'] as $item) {
$total += $item['preco'] * $item['quantidade'];
}
$imposto = $total * 0.15;
$total += $imposto;
$this->db->query("UPDATE pedidos SET total = $total WHERE id = $pedidoId");
$this->enviarEmail($pedido['email'], "Seu pedido foi processado");
return $total;
}
Refatorando em métodos menores:
public function processarPedido($pedidoId): float {
$pedido = $this->obterPedido($pedidoId);
$total = $this->calcularTotal($pedido['itens']);
$this->salvarTotal($pedidoId, $total);
$this->notificarCliente($pedido);
return $total;
}
private function obterPedido($pedidoId): array {
return $this->db->query("SELECT * FROM pedidos WHERE id = ?", [$pedidoId]);
}
private function calcularTotal(array $itens): float {
$subtotal = array_reduce($itens, fn($acc, $item) =>
$acc + ($item['preco'] * $item['quantidade']), 0
);
return $subtotal * 1.15; // inclui imposto
}
private function salvarTotal($pedidoId, float $total): void {
$this->db->query("UPDATE pedidos SET total = ? WHERE id = ?", [$total, $pedidoId]);
}
private function notificarCliente(array $pedido): void {
$this->enviarEmail($pedido['email'], "Seu pedido foi processado");
}
Substituir Condições por Polimorfismo
Código com múltiplas condições é frágil. Veja:
// Antes
function calcularDesconto($tipoCliente, $valor) {
if ($tipoCliente == 'premium') {
return $valor * 0.20;
} elseif ($tipoCliente == 'vip') {
return $valor * 0.30;
} else {
return $valor * 0.05;
}
}
Com polimorfismo:
interface EstrategiaDesconto {
public function calcular(float $valor): float;
}
class DescontoRegular implements EstrategiaDesconto {
public function calcular(float $valor): float {
return $valor * 0.05;
}
}
class DescontoPremium implements EstrategiaDesconto {
public function calcular(float $valor): float {
return $valor * 0.20;
}
}
class DescontoVIP implements EstrategiaDesconto {
public function calcular(float $valor): float {
return $valor * 0.30;
}
}
// Uso
$estrategia = match($tipoCliente) {
'premium' => new DescontoPremium(),
'vip' => new DescontoVIP(),
default => new DescontoRegular(),
};
$desconto = $estrategia->calcular($valor);
Eliminando Code Smells Comuns
Remover Duplicação
Código duplicado é o inimigo da manutenção. Quando encontrar padrões repetidos, extraia para métodos reutilizáveis:
// Antes (duplicado)
$usuariosAtivos = $this->db->query(
"SELECT * FROM usuarios WHERE status = 'ativo' AND deletado_em IS NULL"
);
$pagamentosAtivos = $this->db->query(
"SELECT * FROM pagamentos WHERE status = 'ativo' AND deletado_em IS NULL"
);
// Depois
private function filtrarAtivos(string $tabela): array {
return $this->db->query(
"SELECT * FROM $tabela WHERE status = 'ativo' AND deletado_em IS NULL"
);
}
$usuariosAtivos = $this->filtrarAtivos('usuarios');
$pagamentosAtivos = $this->filtrarAtivos('pagamentos');
Reduzir Acoplamento
Código com alto acoplamento é difícil de testar e modificar. Injete dependências:
// Antes
class Relatorio {
public function gerar() {
$db = new Database();
$email = new Email();
// ...
}
}
// Depois
class Relatorio {
public function __construct(
private Database $db,
private Email $email
) {}
public function gerar(): void {
// Usa $this->db e $this->email
}
}
Ferramentas e Práticas
Use ferramentas para guiar refatoração: PHPStan detecta tipos inconsistentes, Psalm valida tipos, PHP-CS-Fixer padroniza estilo. Testes são imprescindíveis—refatore com testes passando sempre.
composer require --dev phpstan/phpstan
./vendor/bin/phpstan analyse src/
Refatore em pequenos passos. Commit após cada refatoração funcional. Use versionamento para isolar mudanças e facilitar revert se necessário. Nunca refatore e adicione features simultaneamente—separe essas responsabilidades.
Conclusão
Refatoração em PHP é investimento em qualidade duradoura. Dominar extração de métodos, polimorfismo e eliminação de duplicação transforma código legado em sistemas mantíveis. Use ferramentas de análise estática como suporte e sempre mantenha testes passando durante o processo. Código refatorado é código que evolui.