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

Exercícios

  1. 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;.
  2. ✓ Resposta:
    $numberOfItems = 5;
    $date = '2023-01-01';
    function sum(int $a, int $b): int { return $a + $b; }
    $isValid = true;
  3. 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;
    }
  4. ✓ Resposta: A função já é pequena, mas podemos extrair a soma para uma função auxiliar:
    function sum(array $numbers): float {
        return array_sum($numbers);
    }
    
    function average(array $numbers): float {
        return sum($numbers) / count($numbers);
    }
  5. 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
        }
    }
  6. ✓ Resposta: Viola o SRP (Single Responsibility Principle), pois a classe tem múltiplas responsabilidades: validação, persistência e envio de email. A correção é separar essas responsabilidades em classes distintas, como UserValidator, UserRepository e UserMailer.
  7. 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;
    }
  8. ✓ Resposta: O comentário é um code smell, pois o código é autoexplicativo. A melhoria é remover o comentário, deixando apenas o código limpo: return $first . ' ' . $last;
  9. 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.
  10. ✓ Resposta:
    function sumProductPrices(array $products): float {
        $total = 0;
        foreach ($products as $product) {
            $total += $product['preco'];
        }
        return $total;
    }