* * 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; }; } }