Введение: два лагеря, одна битва
Каждый PHP-разработчик рано или поздно оказывается перед выбором: писать в функциональном стиле или нырять в пучину классов и объектов. Это как выбрать между швейцарским ножом и мультитулом — оба инструмента решают задачи, но по-разному. В этой статье мы разберём функциональный vs объектно-ориентированный подход в PHP, посмотрим на реальный код и решим, когда какой инструмент брать.
PHP — уникальный язык. Он родился как процедурный, дорос до ООП, а в последних версиях (7.4+, 8.x) активно впитывает функциональные фишки: стрелочные функции, замыкания, типизацию. Это значит, что вы можете смешивать подходы. Но как не запутаться? Давайте разбираться.
Основы: чем отличаются два мира
Функциональный подход (Functional Programming)
Здесь главное — функции. Чистые функции без побочных эффектов, которые всегда возвращают предсказуемый результат. Никакого общего состояния, никаких мутаций переменных. Данные передаются по цепочке, как конвейер.
// Функциональный стиль: просто, предсказуемо
$numbers = [1, 2, 3, 4, 5];
$result = array_map(
fn($n) => $n * 2,
array_filter($numbers, fn($n) => $n % 2 === 0)
);
print_r($result); // [4, 8]
Заметили? Мы не меняем исходный массив, не используем циклы. Всё декларативно: взять чётные, удвоить, отдать. Это экономит время на отладку — побочных эффектов нет.
Объектно-ориентированный подход (OOP)
ООП строится вокруг объектов — сущностей, которые хранят состояние и поведение. Классы инкапсулируют данные, наследники расширяют функциональность, полиморфизм позволяет менять логику на лету.
// ООП стиль: структура и переиспользование
class NumberProcessor {
private array $numbers;
public function __construct(array $numbers) {
$this->numbers = $numbers;
}
public function getEvens(): array {
return array_filter($this->numbers, fn($n) => $n % 2 === 0);
}
public function multiplyBy(int $factor): self {
$this->numbers = array_map(fn($n) => $n * $factor, $this->numbers);
return $this;
}
public function getResult(): array {
return $this->numbers;
}
}
$processor = new NumberProcessor([1, 2, 3, 4, 5]);
$result = $processor->getEvens()->multiplyBy(2)->getResult();
print_r($result); // [4, 8]
Здесь мы создали объект, который хранит состояние. Можно цепочкой вызывать методы. Плюс: если нужно добавить новую операцию — просто дописываем метод в класс.
Когда функциональный подход выигрывает
1. Обработка данных без побочных эффектов
Представьте, что вы пишете парсер логов или конвертер валют. Данные текут по конвейеру: прочитать → отфильтровать → преобразовать → сохранить. Функции здесь — идеальный вариант. Они не зависят от внешнего состояния, их легко тестировать.
2. Коллекции и массивы
PHP предлагает мощные функции для работы с массивами: array_map, array_filter, array_reduce. В связке со стрелочными функциями (PHP 7.4+) это превращает код в поэзию.
$users = [
['name' => 'Анна', 'age' => 25],
['name' => 'Борис', 'age' => 17],
['name' => 'Вика', 'age' => 30],
];
$adultNames = array_map(
fn($user) => $user['name'],
array_filter($users, fn($user) => $user['age'] >= 18)
);
// ['Анна', 'Вика']
Чисто, читаемо, без классов. Для простых трансформаций — лучший выбор.
3. Тестирование без моков
Функции не имеют состояния, поэтому их легко тестировать. Не нужно создавать сложные моки и стабы. Просто передали данные — получили результат.
Когда объектно-ориентированный подход незаменим
1. Сложная бизнес-логика с состоянием
Если ваш объект меняет своё состояние в ответ на действия (например, корзина интернет-магазина), ООП — ваш выбор. Класс инкапсулирует данные, методы контролируют изменения.
class ShoppingCart {
private array $items = [];
private float $total = 0.0;
public function addItem(string $name, float $price): void {
$this->items[] = $name;
$this->total += $price;
}
public function removeItem(string $name): void {
// логика удаления
}
public function getTotal(): float {
return $this->total;
}
}
В функциональном стиле пришлось бы каждый раз передавать и возвращать массив с товарами и суммой — неудобно и опасно.
2. Большие проекты с наследованием и полиморфизмом
Когда у вас десятки типов пользователей, платежей, уведомлений — ООП спасает через наследование и интерфейсы. Вы определяете общий контракт, а конкретные классы реализуют детали.
interface PaymentInterface {
public function process(float $amount): bool;
}
class CreditCardPayment implements PaymentInterface {
public function process(float $amount): bool {
// обработка карты
return true;
}
}
class PayPalPayment implements PaymentInterface {
public function process(float $amount): bool {
// обработка PayPal
return true;
}
}
В функциональном подходе пришлось бы писать много switch или if, что ведёт к запутанному коду.
3. Фреймворки и паттерны
Laravel, Symfony, Yii2 — все они построены на ООП. Dependency Injection, Service Container, Repository pattern — это всё про классы и объекты. Работая в таких проектах, вы обязаны знать ООП.
Сравнительная таблица: функциональный vs ООП в PHP
| Критерий | Функциональный подход | ООП |
|---|---|---|
| Состояние | Нет (чистые функции) | Есть (свойства объектов) |
| Побочные эффекты | Минимизированы | Допустимы (логирование, БД) |