Нещодавно відбувся реліз PHP 8.5, але розробники core-команди вже працюють над наступною версією. PHP 8.6 вийде приблизно 19 листопада 2026 року, і в ній з’явиться довгоочікувана фіча, на яку я дуже чекав: partial function application (часткове застосування функцій).
Щоб зрозуміти суть partial function application, варто згадати first-class callables — можливість, що з’явилася ще в PHP 8.1. Вона дозволяє створювати посилання на callables за допомогою синтаксису ...:
$replace = str_replace(...);
Завдяки такому посиланню ви можете викликати функцію пізніше:
// … $replace([' ', '_'], '-', $title);
Сама по собі ця можливість може здатися не дуже корисною, проте first-class callables стають у пригоді, коли функція приймає інший callable як аргумент:
$trimmedStrings = array_map( trim(...), $strings, );
Без first-class callables цей код виглядав би так:
$trimmedStrings = array_map( fn (string $input) => trim($input), $strings, );
Фактично, «під капотом» first-class callables створюють closure, яка огортає оригінальну функцію — так само як у прикладі вище.
Однак у first-class callables є суттєвий недолік. Як ви могли помітити на прикладі str_replace(), функція може приймати три аргументи, але first-class callables дозволяють посилатися лише на функцію в цілому. Ви не можете «заздалегідь заповнити» частину її параметрів. У випадку зі str_replace() було б значно зручніше мати короткий запис для такого коду:
$slugReplace = fn (string $input) => str_replace([' ', '_'], '-', $input);
Саме цю проблему вирішує partial function application (PFA). З новою фічею цей приклад можна записати так:
$slugReplace = str_replace([' ', '_'], '-', ?);
Знак питання на місці третього параметра — це placeholder. Він створює нову closure, яка огортає функцію str_replace().
Можливості PFA значно ширші. Особисто я в захваті від того, як це працюватиме з pipe operator, що з'явився у PHP 8.5. Оператор |> завжди вимагає функцію з рівно одним параметром праворуч, через що раніше доводилося постійно використовувати додаткові closure:
$output = $input |> trim(...) |> (fn (string $string) => str_replace(' ', '-', $string)) |> (fn (string $string) => str_replace(['.', '/', '…'], '', $string)) |> strtolower(...);
Завдяки PFA цей код стає набагато чистішим:
$output = $input |> trim(...) |> str_replace(' ', '-', ?) |> str_replace(['.', '/', '…'], '', ?) |> strtolower(...);
На мою думку, PFA — це ключ до того, щоб зробити pipe operator по-справжньому корисним. На жаль, чекати доведеться ще рік, але ми вже впевнені, що ця функція з'явиться.
Докладніше про partial function application
Placeholder ? — це лише початок. По-перше, ви можете використовувати декілька placeholder-ів одночасно:
$replaceWhitespace = str_replace(' ', ?, ?); $replaceWhitespace('=', $input);
Placeholder-и можна розміщувати будь-де, а не тільки в кінці списку аргументів:
$filterNumbers = array_filter( ?, // Передаємо вхідний масив is_numeric(...) // Викликаємо `is_numeric` для кожного елемента ); $filterNumbers(['a', 1, 'b', 2]);
Окрім ?, існує також варіативний placeholder .... Він позначає нуль або більше параметрів і захоплює всі аргументи, які ще не були визначені:
$replaceWhitespace = str_replace(' ', ...); $replaceWhitespace('=', $input);
Крім того, PFA працює з іменованими аргументами, а варіативний placeholder вміє правильно обробляти «залишки»:
$filterNumbers = array_filter( mode: ?, // Робимо `$mode` першим параметром callable: is_numeric(...), ... // Сюди потраплять усі інші параметри (у цьому випадку лише `$array`) ); $filterNumbers(ARRAY_FILTER_USE_KEY, ['a', 1, 'b', 2]);
Це може здатися складним, але в RFC є ще більше прикладів. Що важливо: PFA має дуже чіткі повідомлення про помилки, тому виправити некоректний код буде легко.
Оскільки варіативний placeholder означає нуль або більше параметрів, його можна використовувати для створення посилання на функцію, яка вже має всі аргументи, але ще не була викликана:
$performQuery = query('UPDATE table SET …', ...); if ($needToUpdate) { $performQuery(); }
Це зручно для відкладеного виконання ресурсомістких функцій.
Наостанок, PFA можна використовувати з об'єктами та статичними методами:
$makeThriller = Book::make( title: ?, category: Category::THRILLER, ); $book = $makeThriller(title: 'Timeline Taxi'); $publish = $book->publish(DateTime::now(), ...); if ($shouldPublish) { $publish(); }
Зауважте, що є невелика кількість функцій, де PFA не працюватиме:
- виклики конструкторів
compact()extract()func_get_args()get_defined_vars()
В усіх інших випадках PFA працює бездоганно, навіть із магічними методами!
class Model { public function __call($method, $args) { printf("%s::%s\n", __CLASS__, $method); print_r($args); } } $model = new Model(); $magic = $model->magic(?); $magic('Hello!');
Підсумовуючи: PFA — це крута фіча, яка особливо розкриється у поєднанні з pipe operator. З нетерпінням чекаю на PHP 8.6!
Якщо у вас є питання чи думки, ви завжди можете залишити коментар, написати мені на пошту, приєднатися до мого Discord-серверу або підписатися на розсилку.