Функциональный или ООП в PHP: что выбрать и не сойти с ума

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

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

Начать курс

Введение: два лагеря, одна битва

Каждый 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

Блоги

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

Критерий Функциональный подход ООП
Состояние Нет (чистые функции) Есть (свойства объектов)
Побочные эффекты Минимизированы Допустимы (логирование, БД)