functional-php/src/Utils/FunctionPipeline.php
2026-10-27 23:04:09 +01:00

104 lines
2.8 KiB
PHP

<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Utils;
use Kadet\Functional\Compilable;
use function Kadet\Functional\compile;
class FunctionPipeline implements Compilable
{
/**
* @var callable[]
*/
private $functions = [];
/**
* FunctionPipeline constructor.
*
* @param callable ...$functions
*/
public function __construct(callable ...$functions)
{
$this->functions = $functions;
}
/**
* The function called first, with all arguments of the pipeline, or null when the pipeline is empty.
*
* @internal
*/
public function first(): ?callable
{
return $this->functions[0] ?? null;
}
public function append(callable $callable)
{
return new static(...array_merge($this->functions, [ $callable ]));
}
public function prepend(callable $callable)
{
return new static($callable, ...$this->functions);
}
public function __invoke(...$args)
{
return array_reduce($this->functions, function ($arg, $callable) {
return $arg instanceof UnpackedArgs
? $callable(...$arg->getArrayCopy())
: $callable($arg);
}, new UnpackedArgs($args));
}
/**
* The first function gets all arguments, each next one the result of the previous one (spread when it is
* UnpackedArgs), like invoking the pipeline.
*/
public function compile(): \Closure
{
if (empty($this->functions)) {
return static fn (...$args) => new UnpackedArgs($args);
}
// A curried function decides on its own whether to call or keep currying, so it is called as is.
$functions = array_map(
static fn (callable $function) => $function instanceof CurriedFunction
? static fn (...$args) => $function(...$args)
: compile($function),
$this->functions,
);
$first = array_shift($functions);
if (empty($functions)) {
return $first;
}
if (count($functions) === 1) {
$next = $functions[0];
return static function (...$args) use ($first, $next) {
$result = $first(...$args);
return $result instanceof UnpackedArgs ? $next(...$result->getArrayCopy()) : $next($result);
};
}
return static function (...$args) use ($first, $functions) {
$result = $first(...$args);
foreach ($functions as $function) {
$result = $result instanceof UnpackedArgs ? $function(...$result->getArrayCopy()) : $function($result);
}
return $result;
};
}
}