Функциональное программирование в PHP: основы и примеры

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

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

Начать курс

Функциональное программирование в PHP: от теории к практике

PHP традиционно ассоциируется с императивным и объектно-ориентированным подходами, однако современный язык предоставляет мощные инструменты для функционального программирования (ФП). В этой статье мы разберём, как применять принципы ФП в PHP: от анонимных функций до каррирования и иммутабельности. Вы узнаете, как сделать код более предсказуемым, тестируемым и лаконичным.

1. Основы: анонимные функции и замыкания

Анонимные функции (лямбды) — краеугольный камень ФП в PHP. Они позволяют передавать поведение как данные.

<?php
// Простая анонимная функция
$greet = function($name) {
    return "Привет, $name!";
};
echo $greet('Мир'); // Привет, Мир!

// Использование с array_map
$numbers = [1, 2, 3, 4, 5];
$squared = array_map(function($n) {
    return $n * $n;
}, $numbers);
print_r($squared); // [1, 4, 9, 16, 25]
?>

Замыкания (closures) — это анонимные функции, которые захватывают переменные из внешней области видимости с помощью ключевого слова use:

<?php
$factor = 3;
$multiply = function($n) use ($factor) {
    return $n * $factor;
};
echo $multiply(5); // 15

// Замыкание с изменяемой переменной (по ссылке)
$counter = 0;
$increment = function() use (&$counter) {
    $counter++;
};
$increment();
echo $counter; // 1
?>

Замыкания незаменимы при создании callback-функций и фабрик.

2. Функции высшего порядка: array_map, array_filter, array_reduce

В PHP встроены классические функции высшего порядка, которые принимают другие функции в качестве аргументов:

  • array_map — преобразует каждый элемент массива;
  • array_filter — отфильтровывает элементы по условию;
  • array_reduce — свёртка массива в одно значение.
<?php
$data = [1, 2, 3, 4, 5, 6];

// Фильтрация чётных чисел
$even = array_filter($data, function($n) {
    return $n % 2 === 0;
}); // [2, 4, 6]

// Удвоение каждого элемента
$doubled = array_map(function($n) {
    return $n * 2;
}, $even); // [4, 8, 12]

// Сумма всех значений
$sum = array_reduce($doubled, function($carry, $n) {
    return $carry + $n;
}, 0); // 24
?>

Обратите внимание: array_filter сохраняет ключи. Если нужна переиндексация, используйте array_values().

3. Иммутабельность и работа с данными

Функциональное программирование поощряет иммутабельность — неизменность данных. Вместо модификации существующих объектов создаются новые.

<?php
// Императивный подход (мутация)
$user = ['name' => 'Иван', 'age' => 25];
$user['age'] = 26; // меняем исходный массив

// Функциональный подход (иммутабельно)
$user = ['name' => 'Иван', 'age' => 25];
$updatedUser = array_merge($user, ['age' => 26]); // новый массив
?>

Для работы с массивами используйте array_merge, array_slice и spread-оператор (PHP 7.4+):

<?php
$original = [1, 2, 3];
$new = [...$original, 4]; // [1, 2, 3, 4] — оригинал не изменился
?>

Иммутабельность упрощает отладку и делает код потокобезопасным.

4. Каррирование и частичное применение

Каррирование — преобразование функции от нескольких аргументов в последовательность функций от одного аргумента. Частичное применение — фиксация части аргументов.

В PHP нет встроенного каррирования, но его легко реализовать:

<?php
// Каррированная функция сложения
function curryAdd($a) {
    return function($b) use ($a) {
        return function($c) use ($a, $b) {
            return $a + $b + $c;
        };
    };
}

$add5 = curryAdd(5);
$add5And3 = $add5(3);
echo $add5And3(2); // 10 (5+3+2)

// Частичное применение с помощью Closure::fromCallable
function multiply($x, $y) {
    return $x * $y;
}

$double = function($y) {
    return multiply(2, $y);
};
echo $double(5); // 10
?>

Библиотека functional-php предоставляет готовые утилиты для каррирования:

<?php
// Требуется composer require lstrojny/functional-php
$curried = Functional\curry('array_map');
$square = $curried(function($n) { return $n * $n; });
$result = $square([1, 2, 3]); // [1, 4, 9]
?>

5. Практический пример: конвейерная обработка данных

Соберём всё вместе — создадим конвейер (pipeline) для обработки массива данных:

<?php
$data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Определяем шаги конвейера как отдельные функции
$filterEven = function($arr) {
    return array_filter($arr, fn($n) => $n % 2 === 0);
};

$square = function($arr) {
    return array_map(fn($n) => $n * $n, $arr);
};

$toJson = function($arr) {
    return json_encode(array_values($arr));
};

// Композиция функций (pipeline)
$pipeline = function($data) use ($filterEven, $square, $toJson) {
    return $toJson($square($filterEven($data)));
};

echo $pipeline($data); // "[4,16,36,64,100]"
?>

Такой подход делает код декларативным: каждый шаг изолирован

Блоги

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