Clean Code на PHP: принципы написания чистого кода с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что такое Clean Code и почему это важно в PHP?

Clean Code (чистый код) — это не просто модный термин, а философия разработки, которая помогает писать программы, понятные не только машине, но и человеку. В контексте PHP, где код часто поддерживается годами разными командами, следование принципам чистого кода становится критически важным навыком.

Чистый код обладает несколькими ключевыми характеристиками:

  • Его легко читать и понимать без комментариев
  • Он предсказуем и не вызывает "сюрпризов" при выполнении
  • Его легко модифицировать без риска сломать другие части системы
  • Он содержит минимум дублирования (принцип DRY)

В этой статье мы разберем основные принципы Clean Code на конкретных примерах PHP-кода.

1. Именование переменных и функций

Плохой пример: что не так с этим кодом?

// ❌ Плохой код
$a = 5;
$b = 10;
$c = $a * $b;

function calc($x, $y) {
    return $x + $y * 0.2;
}

$d = calc($a, $b);

В этом примере совершенно непонятно, что означают переменные $a, $b, $c и что делает функция calc. Через месяц вы сами не вспомните, зачем писали этот код.

Хороший пример: осмысленные имена

// ✅ Хороший код
$productPrice = 5;
$quantity = 10;
$totalCost = $productPrice * $quantity;

function calculatePriceWithTax(float $basePrice, float $taxRate = 0.2): float {
    return $basePrice + ($basePrice * $taxRate);
}

$finalPrice = calculatePriceWithTax($totalCost);

Правила именования в PHP:

  • Используйте существительные для переменных ($userName, $orderTotal)
  • Используйте глаголы для функций (getUser(), calculateTotal())
  • Не сокращайте имена (пишите $customerAddress, а не $custAddr)
  • Избегайте однобуквенных переменных (кроме счетчиков циклов $i, $j)
  • Булевы переменные начинайте с is, has, can ($isActive, $hasPermission)

2. Маленькие функции и принцип единой ответственности (SRP)

Проблема: функция-швейцарский нож

// ❌ Плохой код: функция делает всё сразу
function processOrder(array $orderData): array {
    // Валидация
    if (empty($orderData['email'])) {
        throw new Exception('Email required');
    }
    
    // Сохранение в БД
    $pdo = new PDO('mysql:host=localhost;dbname=shop', 'root', '');
    $stmt = $pdo->prepare("INSERT INTO orders (email, total) VALUES (?, ?)");
    $stmt->execute([$orderData['email'], $orderData['total']]);
    
    // Отправка email
    mail($orderData['email'], 'Order confirmed', 'Thank you!');
    
    // Логирование
    file_put_contents('log.txt', 'Order processed: ' . date('Y-m-d H:i:s') . PHP_EOL, FILE_APPEND);
    
    return ['status' => 'success'];
}

Эта функция нарушает принцип единой ответственности (Single Responsibility Principle). Она занимается валидацией, работой с БД, отправкой почты и логированием. Любое изменение в одном из этих процессов потребует изменения всей функции.

Решение: декомпозиция на маленькие функции

// ✅ Хороший код: каждая функция отвечает за одно действие
function validateOrderData(array $orderData): void {
    if (empty($orderData['email'])) {
        throw new InvalidArgumentException('Email is required');
    }
}

function saveOrderToDatabase(array $orderData): int {
    $pdo = new PDO('mysql:host=localhost;dbname=shop', 'root', '');
    $stmt = $pdo->prepare("INSERT INTO orders (email, total) VALUES (?, ?)");
    $stmt->execute([$orderData['email'], $orderData['total']]);
    return (int)$pdo->lastInsertId();
}

function sendOrderConfirmationEmail(string $email): void {
    mail($email, 'Order confirmed', 'Thank you for your order!');
}

function logOrderProcessing(): void {
    $logMessage = 'Order processed: ' . date('Y-m-d H:i:s') . PHP_EOL;
    file_put_contents('log.txt', $logMessage, FILE_APPEND);
}

// Основная функция теперь просто координирует действия
function processOrder(array $orderData): array {
    validateOrderData($orderData);
    $orderId = saveOrderToDatabase($orderData);
    sendOrderConfirmationEmail($orderData['email']);
    logOrderProcessing();
    
    return ['status' => 'success', 'order_id' => $orderId];
}

Преимущества маленьких функций:

  • Их легко тестировать изолированно
  • Код становится самодокументируемым
  • Упрощается отладка — ошибка сразу локализуется в конкретной функции
  • Функции можно переиспользовать в других местах

3. Избегаем дублирования кода (DRY) и магических чисел

Проблема: магические числа и повторяющийся код

// ❌ Плохой код: магические числа и дублирование
function calculateDiscount(float $price): float {
    if ($price > 1000) {
        return $price * 0.9; // 10% скидка
    } elseif ($price > 500) {
        return $price * 0.95; // 5% скидка
    }
    return $price;
}

function calculateBonus(float $salary): float {
    if ($salary > 10000) {
        return $salary * 0.9; // 10% бонус
    } elseif ($salary > 5000) {
        return $salary * 0.95; // 5% бонус
    }
    return $salary;
}

Здесь мы видим дублирование логики расчета процентов, а также "магические числа" 0.9, 0.95, 1000, 500, смысл которых неочевиден.

Решение: константы и общие функции

// ✅ Хороший код: константы и переиспользуемая логика
const HIGH_VALUE_THRESHOLD = 1000;
const                

Блоги

Рекомендаций по книгам