Refatoração de código legado em PHP: nível avançado
Trabalhar com código legado (obsoleto) em PHP é uma realidade inevitável para muitos desenvolvedores. Frequentemente, esse código é uma aplicação monolítica sem testes, com dependências globais e mistura de lógica de negócios com apresentação. Uma simples refatoração "de olho" pode levar a erros fatais. Neste artigo, veremos técnicas avançadas de refatoração que permitirão modernizar sistemas legados de forma segura e eficiente.
1. Estratégia "Seam": criando pontos de ruptura
O primeiro passo na refatoração de código legado é criar costuras (seams). Uma costura é um lugar onde você pode alterar o comportamento do código sem editá-lo diretamente. Isso é especialmente importante quando o código não tem cobertura de testes e você não pode executá-lo isoladamente.
Vamos considerar um exemplo clássico: trabalho direto com o banco de dados dentro de um método.
<?php// Código legadoclass UserReport { public function getActiveUsers() { $db = new PDO('mysql:host=localhost;dbname=test', 'root', ''); $stmt = $db->query("SELECT * FROM users WHERE active = 1"); return $stmt->fetchAll(); }}?>Problema: new PDO(...) cria uma dependência rígida. Para testar este método, você precisa ter um banco de dados real.
Solução: injeção de dependência através do construtor.
<?php// Refatoração: criando uma costura via DIclass UserReport { private PDO $pdo;
public function __construct(PDO $pdo) { $this->pdo = $pdo; }
public function getActiveUsers(): array { $stmt = $this->pdo->query("SELECT * FROM users WHERE active = 1"); return $stmt->fetchAll(); }}
// Uso$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');$report = new UserReport($pdo);?>Agora você pode substituir o PDO por um objeto mock nos testes. Esta é uma costura simples, mas eficaz.
2. Padrão "Sprout Method" e "Sprout Class"
Quando você não pode alterar um método existente (por exemplo, devido à falta de testes), use a estratégia "Sprout" — crie novo código ao lado do antigo, redirecionando gradualmente as chamadas.
Exemplo: temos um método enorme que valida e salva um usuário.
<?phpclass UserService { public function saveUser(array $data): bool { // 100 linhas de validação, registro e salvamento $email = $data['email'] ?? ''; if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { throw new InvalidArgumentException('Invalid email'); } // ... muito outro código $db = new PDO(...); $stmt = $db->prepare("INSERT INTO users ..."); // ... }}?>Em vez de refatorar o método inteiro de uma vez, crie uma nova classe para validação.
<?phpclass UserValidator { public function validate(array $data): void { if (!filter_var($data['email'] ?? '', FILTER_VALIDATE_EMAIL)) { throw new InvalidArgumentException('Invalid email'); } // Adicione outras verificações }}
class UserService { private UserValidator $validator;
public function __construct(UserValidator $validator) { $this->validator = $validator; }
public function saveUser(array $data): bool { $this->validator->validate($data); // O restante do código permanece por enquanto }}?>Agora você pode escrever testes unitários para UserValidator sem tocar no código antigo. Gradualmente, você extrairá a lógica de saveUser para novas classes.
3. Eliminação de estados globais e estáticos
Código legado frequentemente usa variáveis globais, singletons e métodos estáticos. Isso torna o código praticamente impossível de