Після тривалих обговорень ми нарешті отримаємо pipe operator у PHP 8.5! У цьому дописі ми розберемося, як він працює та чому вам захочеться його використовувати.
Переваги pipe operator
Уявіть, що у вас є вхідні дані — наприклад, рядок — і ви хочете виконати з ним низку операцій. Припустимо, перетворити його на slug.
$input = ' Some kind of string. '; $output = strtolower( str_replace(['.', '/', '…'], '', str_replace(' ', '-', trim($input) ) ) );
Ви напевно згадаєте чимало подібних прикладів із власного коду, і з ними є кілька проблем:
- По-перше, глибоко вкладені виклики функцій швидко стають некерованими.
- По-друге, форматування перетворюється на хаос: важко вирішити, чи переносити кожен аргумент на новий рядок, чи лише самі виклики.
- По-третє, такий код доводиться читати «задом наперед», зсередини назовні: спочатку
trim(), потімstr_replace()і так далі.
Можна було б використати тимчасову змінну:
$input = ' Some kind of string. '; $temp = trim($input); $temp = str_replace(' ', '-', $temp); $temp = str_replace(['.', '/', '…'], '', $temp); $output = strtolower($temp);
Це вже значно краще і вирішує перелічені проблеми. Проте нам доводиться вводити зайву змінну, що виглядає дещо неохайно. Це не критично, але чи можна зробити код ще зручнішим? Саме для цього потрібен pipe operator! Ось як цей фрагмент виглядатиме в PHP 8.5:
$output = $input |> trim(...) |> (fn (string $string) => str_replace(' ', '-', $string)) |> (fn (string $string) => str_replace(['.', '/', '…'], '', $string)) |> strtolower(...);
Виглядає чудово! Але розберімося з деталями синтаксису.
«Права частина»
Принцип роботи pipe operator простий: він передає результат лівої частини в праву. «Ліва частина» — це будь-які дані: рядки, масиви, об'єкти або результат попереднього пайпа. А от із «правою частиною» є нюанси.
Перший варіант — передати посилання на функцію через синтаксис first-class callable:
$output = $input |> trim(...);
Це працює, лише якщо функція приймає тільки один аргумент — ті самі дані з пайпа. Фактично цей код перетворюється на:
$output = trim($input);
Але як бути, якщо функція потребує кілька аргументів? Тут доведеться використовувати callable: короткі чи повні closure, invokable-класи або навіть масиви-посилання на методи. Головне, щоб вони очікували лише один вхідний параметр. Особисто я надаю перевагу коротким closure:
$output = $input |> (fn (string $string) => str_replace(' ', '-', $string));
Необхідність використовувати closure може здатися громіздкою — це навіть багатослівніше за варіант із $temp. Проте pipe operator все одно виграє, бо об'єднує операції у логічний блок, де код сприймається як єдине ціле.
Ба більше, розробники ядра PHP вже працюють над наступною фічею — partial function application. Вона ідеально доповнить pipe operator і дозволить писати так:
// Поки що не працює! $output = $input |> str_replace(' ', '-', ?);
Знак питання тут виступає плейсхолдером, який pipe operator автоматично заповнить вхідними даними. Комбінація цих двох можливостей буде неймовірно зручною, але поки що задовольняємося використанням callable.
Попри певні обмеження, я вважаю pipe operator чудовим нововведенням і з нетерпінням чекаю на реліз PHP 8.5. А що ви думаєте про це? Діліться думками у нашому Discord!