PHP

PHP 8.6: Částečná aplikace funkcí

// obsah 3
  1. 01 Co znamená otazník a co tři tečky?
  2. 02 Na co si dát bacha?
  3. 03 Co není možné?

Na tuhle novinku se v PHP čekalo roky. Verze 8.6 přináší částečnou aplikaci funkcí (partial function application) a z existující funkce si díky ní vyrobíte closure s předvyplněnými argumenty.

Doteď se musela napsat celá closure:

<?php

$naSlug = static fn (string $text): string => str_replace(' ', '-', $text);

echo $naSlug('Novinky v PHP 8.6'); // Novinky-v-PHP-8.6

Od PHP 8.6 stačí na místo chybějícího argumentu napsat ? a PHP closure vytvoří za nás:

<?php

$naSlug = str_replace(' ', '-', ?);

echo $naSlug('Novinky v PHP 8.6'); // Novinky-v-PHP-8.6

Co znamená otazník a co tři tečky?

Otazník znamená přesně jeden argument, který doplním později. Tři tečky znamenají zbytek argumentů, ať už jich je kolik chce:

<?php

class Kosik
{
    public function pridat(string $zbozi, int $pocet = 1, float $sleva = 0.0): void
    {
        // ...
    }
}

$kosik = new Kosik();
$pridatKnihu = $kosik->pridat('Kniha o PHP', ...);

$pridatKnihu();        // 1 kus, bez slevy
$pridatKnihu(3);       // 3 kusy
$pridatKnihu(3, 10.0); // 3 kusy se slevou

Funguje to i s pojmenovanými argumenty:

<?php

function formatujCenu(float $castka, string $mena = 'CZK', int $desetinnaMista = 2): string
{
    return number_format($castka, $desetinnaMista, ',', ' ') . ' ' . $mena;
}

$vEurech = formatujCenu(?, mena: 'EUR');

echo $vEurech(1234.5); // 1 234,50 EUR

Na co si dát bacha?

Argumenty, které vyplníte hned, se vyhodnotí už při vzniku closure, ne až při jejím zavolání. Tím se to liší od arrow funkce:

<?php

$zacatek = time();

$zaloguj = sprintf('[%d] %s', $zacatek, ?); // $zacatek se vyhodnotí teď

sleep(5);

echo $zaloguj('Hotovo'); // v hranaté závorce je čas z doby vzniku closure

A druhá věc. Každý otazník vytvoří povinný parametr, i když v původní funkci byl nepovinný. Doladilo to navazující RFC:

<?php

function priklad(mixed $a, string $b = 'výchozí') {}

$closure = priklad(?, ?);

$closure('x'); // ArgumentCountError, protože $b už je povinný

Co není možné?

<?php

// Konstruktory se částečně aplikovat nedají
$nova = new Objednavka(?);

// Za "..." už nesmí být další zástupný symbol
$f = pridat(?, ..., ?);

// Poziční "?" nesmí následovat po pojmenovaném argumentu
$g = formatujCenu(mena: 'EUR', ?);

Nefunguje to ani u funkcí, které si sahají do volajícího kontextu (compact(), extract(), func_get_arg() a get_defined_vars()).

RFC prošlo v poměru 33 : 0. Mně to dává smysl hlavně u callbacků typu array_map(), kde jsem doteď psal jednořádkové arrow funkce jenom kvůli přeskládání argumentů. Uvidíme, jestli si to týmy vezmou za své :)

Zdroj: https://wiki.php.net/rfc/partial_function_application_v2

Michal Katuščák
Michal Katuščák

Navrhuji a vyvíjím aplikace nad Symfony a Reactem, zajímám se architekturu softwaru. Žiju v Českých Budějovicích.