107 lines
3.0 KiB
PHP
107 lines
3.0 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;
|
|
use function Kadet\Functional\Internal\isUnary;
|
|
|
|
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;
|
|
}
|
|
|
|
// A first function taking one argument ignores any other, so the compiled pipeline can take one as well.
|
|
if (isUnary($first)) {
|
|
return static function ($value) use ($first, $functions) {
|
|
$result = $first($value);
|
|
foreach ($functions as $function) {
|
|
$result = $result instanceof UnpackedArgs ? $function(...$result->getArrayCopy()) : $function($result);
|
|
}
|
|
|
|
return $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;
|
|
};
|
|
}
|
|
} |