Clean code em PHP
Nesta aula, você aprenderá os princípios fundamentais do Clean Code em PHP, incluindo boas práticas de nomenclatura, funções pequenas e coesas, uma introdução aos princípios SOLID e como identificar e corrigir code smells. O objetivo é capacitar você a escrever código PHP mais legível, manutenível e profissional.
Bem-vindo à aula 58 do nosso curso de PHP! Hoje vamos mergulhar em um tópico que separa programadores iniciantes de profissionais: Clean Code (código limpo). Não se trata apenas de um estilo de escrita, mas de uma filosofia que visa criar código legível, simples e fácil de manter. Afinal, o código é lido muito mais vezes do que escrito, e a clareza é fundamental para a colaboração e a evolução de projetos.
Nesta aula, vamos explorar os pilares do Clean Code aplicados ao PHP. Você verá como escolher bons nomes, escrever funções pequenas e coesas, entender a importância dos princípios SOLID e, finalmente, como identificar os famosos "code smells" — sinais de que algo pode estar errado no seu código. Prepare-se para transformar a maneira como você escreve PHP!
Nomes
A escolha de nomes é uma das decisões mais importantes na programação. Nomes claros e descritivos tornam o código autoexplicativo, reduzindo a necessidade de comentários e facilitando a manutenção. No PHP, isso se aplica a variáveis, funções, classes, métodos e até mesmo a nomes de arquivos. Um bom nome deve responder a três perguntas: por que existe, o que faz e como é usado. Se um nome precisa de um comentário para ser entendido, então ele não é bom o suficiente.
Evite abreviações ambíguas, como $n ou $d, e prefira nomes completos e significativos. Use convenções consistentes, como $userName em vez de $uname. Para funções, use verbos que indiquem ação, como getUser() ou calculateTotal(). Para booleanos, use prefixos como is, has ou can (ex.: isActive, hasPermission).
// Ruim
$n = 'João';
$d = new DateTime();
// Bom
$userName = 'João';
$currentDate = new DateTime();
// Funções: use verbos
function getUserById(int $id): User { /* ... */ }
// Booleanos: prefixos claros
function isValidEmail(string $email): bool { /* ... */ }Funções pequenas
Funções pequenas são a base de um código limpo. Elas devem fazer apenas uma coisa, fazer bem e ter um nome que descreva essa ação. Funções grandes tendem a esconder lógica complexa, dificultam testes e aumentam o acoplamento. Uma boa regra é: se uma função não cabe em uma tela (20-30 linhas), ela está fazendo demais. Além disso, funções pequenas facilitam a reutilização e a composição.
Para escrever funções pequenas, é essencial extrair blocos de lógica em funções auxiliares. Isso também melhora a legibilidade, pois cada função se torna uma abstração que lê como um texto. Lembre-se de que a função deve ter um único propósito e um único nível de abstração. Por exemplo, uma função que processa um pedido não deve também formatar a saída; isso é responsabilidade de outra função.
// Ruim: função grande e multifuncional
function processOrder(array $order): array {
$total = 0;
foreach ($order['items'] as $item) {
$total += $item['price'] * $item['quantity'];
}
$tax = $total * 0.1;
$shipping = $total > 100 ? 0 : 10;
$grandTotal = $total + $tax + $shipping;
$result = [
'total' => $total,
'tax' => $tax,
'shipping' => $shipping,
'grand_total' => $grandTotal,
];
return $result;
}
// Bom: funções pequenas e coesas
function calculateItemTotal(array $item): float {
return $item['price'] * $item['quantity'];
}
function calculateSubtotal(array $items): float {
return array_sum(array_map('calculateItemTotal', $items));
}
function calculateTax(float $subtotal): float {
return $subtotal * 0.1;
}
function calculateShipping(float $subtotal): float {
return $subtotal > 100 ? 0 : 10;
}
function calculateGrandTotal(float $subtotal, float $tax, float $shipping): float {
return $subtotal + $tax + $shipping;
}
function processOrder(array $order): array {
$subtotal = calculateSubtotal($order['items']);
$tax = calculateTax($subtotal);
$shipping = calculateShipping($subtotal);
$grandTotal = calculateGrandTotal($subtotal, $tax, $shipping);
return [
'subtotal' => $subtotal,
'tax' => $tax,
'shipping' => $shipping,
'grand_total' => $grandTotal,
];
}SOLID (introdução)
SOLID é um acrônimo para cinco princípios de design orientado a objetos que ajudam a criar sistemas mais flexíveis e fáceis de manter. São eles: Single Responsibility Principle (SRP), Open/Closed Principle (OCP), Liskov Substitution Principle (LSP), Interface Segregation Principle (ISP) e Dependency Inversion Principle (DIP). Nesta introdução, vamos focar nos dois primeiros, que são os mais impactantes no dia a dia.
O SRP afirma que uma classe deve ter apenas um motivo para mudar. Ou seja, ela deve ter uma única responsabilidade. Por exemplo, uma classe que lida com persistência e regras de negócio ao mesmo tempo viola o SRP. O OCP diz que as classes devem ser abertas para extensão, mas fechadas para modificação. Isso significa que você deve poder adicionar novos comportamentos sem alterar o código existente, geralmente usando herança ou composição.
// Exemplo SRP: separando responsabilidades
class UserRepository {
public function save(User $user): void { /* persistência */ }
}
class UserMailer {
public function sendWelcomeEmail(User $user): void { /* envio de email */ }
}
// Exemplo OCP: usando interfaces para extensão
interface PaymentMethod {
public function pay(float $amount): void;
}
class CreditCardPayment implements PaymentMethod {
public function pay(float $amount): void { /* processa cartão */ }
}
class PayPalPayment implements PaymentMethod {
public function pay(float $amount): void { /* processa PayPal */ }
}
class Checkout {
public function process(PaymentMethod $payment, float $amount): void {
$payment->pay($amount);
}
}Code smells
Code smells são sinais de que o código pode ter problemas de design. Eles não são bugs, mas indicam fragilidade e dificuldade de manutenção. Alguns dos mais comuns em PHP incluem: métodos longos, classes grandes, duplicação de código, listas de parâmetros longas, acoplamento excessivo e uso de comentários para explicar código confuso. Identificar esses cheiros é o primeiro passo para refatorar e tornar o código mais limpo.
Por exemplo, a duplicação de código pode ser eliminada extraindo métodos ou classes comuns. Longas listas de parâmetros podem ser substituídas por um objeto de parâmetros. Comentários que explicam o que o código faz devem ser removidos, pois o código deve falar por si. Se você sentir necessidade de comentar, é um sinal de que os nomes não estão claros ou a lógica está complexa demais.
// Code smell: parâmetros demais
function createUser(string $name, string $email, string $phone, string $address, string $city, string $zip, string $country): User {
// ...
}
// Refatoração: usando um objeto de valor
class UserData {
public function __construct(
public string $name,
public string $email,
public string $phone,
public string $address,
public string $city,
public string $zip,
public string $country
) {}
}
function createUser(UserData $data): User {
// ...
}Boas práticas e observações finais
Além dos tópicos abordados, é importante lembrar que Clean Code é uma prática contínua. Revise seu código regularmente, faça refatorações incrementais e utilize ferramentas de análise estática como PHPStan ou Psalm para identificar problemas. Não tenha medo de reescrever partes do código que não estão claras. O objetivo é que o código seja legível para outros desenvolvedores e para você mesmo no futuro. Lembre-se: código limpo é mais do que uma regra, é um hábito.
Referências
- Documentação oficial do PHP
- Clean Code - Wikipedia
- PHP-FIG PSRs
- CodeSmell - Martin Fowler
- PHPStan - Static Analysis
- Psalm - Static Analysis
Exercícios
- Renomeie as variáveis e funções a seguir para seguir as boas práticas de nomenclatura:
$x = 5;,$d = '2023-01-01';,function calc($a, $b) { return $a + $b; },$fl = true;. - Refatore a função abaixo para que ela seja pequena e faça apenas uma coisa. A função deve calcular a média de um array de números e retorná-la.
function processNumbers(array $numbers): float { $sum = 0; foreach ($numbers as $num) { $sum += $num; } $count = count($numbers); $average = $sum / $count; return $average; } - Identifique qual princípio SOLID é violado na seguinte classe e explique como corrigir.
class UserService { public function createUser(array $data): void { // valida dados // salva no banco // envia email de boas-vindas } } - Qual dos seguintes é um code smell? Explique por quê e sugira uma melhoria.
function getFullName(string $first, string $last): string { // retorna o nome completo return $first . ' ' . $last; } - Escreva uma função PHP que receba uma lista de produtos (cada produto tem 'nome' e 'preco') e retorne a soma dos preços. Use boas práticas de nomenclatura e mantenha a função pequena.
$numberOfItems = 5;
$date = '2023-01-01';
function sum(int $a, int $b): int { return $a + $b; }
$isValid = true;function sum(array $numbers): float {
return array_sum($numbers);
}
function average(array $numbers): float {
return sum($numbers) / count($numbers);
}UserValidator, UserRepository e UserMailer.return $first . ' ' . $last;function sumProductPrices(array $products): float {
$total = 0;
foreach ($products as $product) {
$total += $product['preco'];
}
return $total;
}