Введение в функциональное программирование на PHP
Функциональное программирование (ФП) — это парадигма, в которой вычисления рассматриваются как вычисление математических функций, избегая изменения состояния и изменяемых данных. Хотя PHP изначально создавался как императивный язык, начиная с версии 5.3 (2009 год) в него были добавлены мощные инструменты для функционального стиля: анонимные функции, замыкания, поддержка функций высшего порядка и многое другое.
В этой статье мы разберем все ключевые концепции функционального программирования в PHP — от базовых анонимных функций до продвинутых техник вроде каррирования и композиции функций. Вы узнаете, как писать более чистый, предсказуемый и тестируемый код.
1. Анонимные функции и замыкания (Closures)
Анонимные функции
Анонимная функция (лямбда) — это функция без имени. Она может быть присвоена переменной, передана как аргумент или возвращена из другой функции.
<?php
// Обычная анонимная функция
$square = function($n) {
return $n * $n;
};
echo $square(5); // 25
// Анонимная функция как callback для array_map
$numbers = [1, 2, 3, 4];
$squared = array_map(function($n) {
return $n * $n;
}, $numbers);
print_r($squared); // [1, 4, 9, 16]
?>
Замыкания (Closures)
Замыкание — это анонимная функция, которая может захватывать переменные из внешней области видимости с помощью ключевого слова use. Это одна из самых мощных возможностей ФП в PHP.
<?php
$multiplier = 3;
$multiply = function($n) use ($multiplier) {
return $n * $multiplier;
};
echo $multiply(10); // 30
// Изменяемая переменная (передаётся по ссылке)
$counter = 0;
$increment = function() use (&$counter) {
$counter++;
};
$increment();
$increment();
echo $counter; // 2
?>
2. Чистые функции и неизменяемость данных
Чистые функции
Чистая функция — это функция, которая:
- Всегда возвращает одинаковый результат для одних и тех же аргументов (детерминирована)
- Не имеет побочных эффектов (не изменяет внешние переменные, не выводит данные, не работает с БД)
<?php
// НЕ чистая функция (зависит от глобальной переменной)
$taxRate = 0.2;
function calculatePriceDirty($price) {
global $taxRate;
return $price * (1 + $taxRate);
}
// Чистая функция
function calculatePricePure($price, $taxRate) {
return $price * (1 + $taxRate);
}
echo calculatePricePure(100, 0.2); // 120 — всегда предсказуемо
?>
Неизменяемость (Immutability)
В ФП принято не изменять существующие данные, а создавать новые. В PHP для этого можно использовать неизменяемые объекты или просто возвращать новые массивы.
<?php
// Плохо: изменение исходного массива
$numbers = [1, 2, 3];
array_push($numbers, 4); // мутация
// Хорошо: создание нового массива
$numbers = [1, 2, 3];
$newNumbers = array_merge($numbers, [4]);
// Функция, возвращающая новый массив
function addItem(array $arr, $item): array {
$result = $arr;
$result[] = $item;
return $result;
}
$original = [1, 2];
$updated = addItem($original, 3);
print_r($original); // [1, 2] — исходный не изменился
print_r($updated); // [1, 2, 3]
?>
3. Функции высшего порядка: array_map, array_filter, array_reduce
Функции высшего порядка — это функции, которые принимают другие функции как аргументы или возвращают их. В PHP встроены три ключевые функции для работы с массивами в функциональном стиле.
array_map — преобразование элементов
<?php
$users = [
['name' => 'Alice', 'age' => 25],
['name' => 'Bob', 'age' => 30],
];
// Извлекаем только имена
$names = array_map(function($user) {
return $user['name'];
}, $users);
print_r($names); // ['Alice', 'Bob']
// Сокращённая запись со стрелочной функцией (PHP 7.4+)
$names = array_map(fn($user) => $user['name'], $users);
?>
array_filter — фильтрация элементов
<?php
$numbers = [1, 2, 3, 4, 5, 6];
// Оставляем только чётные числа
$even = array_filter($numbers, function($n) {
return $n % 2 === 0;
});
print_r($even); // [1 => 2, 3 => 4, 5 => 6] — ключи сохраняются
// Сброс ключей
$even = array_values($even);
print_r($even); // [2, 4, 6]
?>
array_reduce — свертка массива в одно значение
<?php
$numbers = [1, 2, 3, 4, 5];
// Сумма всех чисел
$sum = array_reduce($numbers, function($carry, $item) {
return $carry + $item;
}, 0);
echo $sum; // 15
// Произведение всех чисел
$product = array_reduce($numbers, function($carry, $item) {
return $carry * $item;
}, 1);
echo $product; // 120
?>
4. Рекурсия и каррирование
Рекурсия
Рекурсия — когда функция вызывает саму себя. В ФП рекурсия часто заменяет итеративные циклы. Важно помнить о глубине рекурсии (в PHP по умолчанию ~100-256 вызовов).
<?php
// Факториал через рекурсию
function factorial($n) {
if ($n <= 1) {
return 1;
}
return $n * factorial($n - 1);
}
echo factorial(5); // 120
// Сумма элементов массива через рекурсию
function arraySumRecursive(array $arr) {
if (empty($arr)) {
return 0;
}