Clean Code на PHP: принципы и примеры на практике
Чистый код — это не роскошь, а необходимость для любого профессионального PHP-разработчика. Когда вы пишете код, который легко читать, понимать и изменять, вы экономите время себе и своей команде. В этой статье мы разберем ключевые принципы Clean Code на PHP с конкретными примерами, которые можно сразу применить в проектах.
Почему чистый код важен для PHP-проектов?
PHP — гибкий язык, но эта гибкость часто приводит к хаосу, если не следить за качеством кода. Чистый код помогает:
- Быстрее находить и исправлять ошибки;
- Легко добавлять новые функции без риска сломать существующую логику;
- Сокращать время вхождения в проект для новых разработчиков;
- Повышать общую стабильность приложения.
Давайте перейдем к конкретным принципам и примерам.
Правильные имена переменных и функций
Первый шаг к чистому коду — осмысленные имена. Имя должно отвечать на вопрос "Что это?" или "Что делает?".
Плохой пример
<?php
$a = 86400;
$b = $a * 7;
$c = $b * 0.8;
Хороший пример (Clean Code)
<?php
$secondsPerDay = 86400;
$weeklySeconds = $secondsPerDay * 7;
$discountedWeeklySeconds = $weeklySeconds * 0.8;
Принцип: Имена должны быть описательными и не требовать комментариев. Избегайте сокращений (кроме общепринятых, например $i для счетчика цикла).
Пример для функций
<?php
// Плохо
function calc($a, $b) {
return $a + $b * 1.2;
}
// Хорошо
function calculateTotalWithTax(float $price, float $taxRate = 0.2): float {
return $price * (1 + $taxRate);
}
Принцип единой ответственности (SRP) в классах
Один из принципов SOLID гласит: "У класса должна быть только одна причина для изменения". Это значит, что каждый класс должен отвечать за одну конкретную задачу.
Нарушение SRP
<?php
class OrderProcessor {
public function process(Order $order) {
// Валидация заказа
if (empty($order->items)) {
throw new Exception("Пустой заказ");
}
// Сохранение в БД
$this->saveToDatabase($order);
// Отправка email
mail($order->email, "Заказ оформлен", "Спасибо за покупку!");
// Генерация PDF счета
$this->generateInvoice($order);
}
}
Чистая реализация с SRP
<?php
class OrderValidator {
public function validate(Order $order): void {
if (empty($order->items)) {
throw new Exception("Пустой заказ");
}
}
}
class OrderRepository {
public function save(Order $order): void {
// Логика сохранения в БД
}
}
class EmailNotifier {
public function sendOrderConfirmation(Order $order): void {
mail($order->email, "Заказ оформлен", "Спасибо за покупку!");
}
}
class InvoiceGenerator {
public function generate(Order $order): string {
// Генерация PDF
return "invoice_".$order->id.".pdf";
}
}
// Использование
class OrderProcessor {
public function __construct(
private OrderValidator $validator,
private OrderRepository $repository,
private EmailNotifier $notifier,
private InvoiceGenerator $invoiceGenerator
) {}
public function process(Order $order): void {
$this->validator->validate($order);
$this->repository->save($order);
$this->notifier->sendOrderConfirmation($order);
$this->invoiceGenerator->generate($order);
}
}
Теперь каждый класс отвечает за свою зону ответственности, что упрощает тестирование и поддержку.
Избегание "магических чисел" и дублирования
Магические числа — это числовые литералы, значение которых непонятно без контекста. Они делают код хрупким и сложным для изменения.
Плохой код
<?php
function getDiscount($price) {
if ($price > 1000) {
return $price * 0.9; // 10% скидка
} elseif ($price > 500) {
return $price * 0.95; // 5% скидка
}
return $price;
}
Чистый код с константами
<?php
class DiscountCalculator {
const HIGH_VALUE_THRESHOLD = 1000;
const MEDIUM_VALUE_THRESHOLD = 500;
const HIGH_DISCOUNT_RATE = 0.9;
const MEDIUM_DISCOUNT_RATE = 0.95;
public function applyDiscount(float $price): float {
if ($price > self::HIGH_VALUE_THRESHOLD) {
return $price * self::HIGH_DISCOUNT_RATE;
}
if ($price > self::MEDIUM_VALUE_THRESHOLD) {
return $price * self::MEDIUM_DISCOUNT_RATE;
}
return $price;
}
}
Совет: Выносите повторяющиеся значения в константы или конфигурационные файлы. Это делает код самодокументируемым.
Управление условиями и ранний возврат (Early Return)
Глубоко вложенные if — признак плохого кода. Используйте ранний возврат для упрощения логики.
Плохо
<?php
function processUser($user) {
if ($user) {
if ($user->isActive()) {
if ($user->hasPermission('admin')) {
// Сложная логика
return "Admin access granted";
} else {
return "No permission";
}
} else {
return "User not active";
}
} else {
return "User not found";
}
}
Хорошо (Clean Code)
<?php
function processUser($user): string {
if (!$user) {
return "User not found";
}
if (!$user->isActive()) {
return "User not active";
}
if (!$user->hasPermission('admin')) {
return "No permission";
}
return "Admin access granted";
}
Такой код читается как последовательность проверок сверху вниз, что гораздо понятнее.