add reflection for applied function

This commit is contained in:
Kacper Donat 2018-07-15 20:42:37 +02:00
parent fd3288a4ad
commit 596031d09e
8 changed files with 211 additions and 9 deletions

16
src/Decorator.php Normal file
View File

@ -0,0 +1,16 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional;
interface Decorator
{
public function getDecorated();
}

View File

@ -10,7 +10,9 @@
namespace Kadet\Functional\Predicate;
class ClosurePredicate extends AbstractPredicate
use Kadet\Functional\Decorator;
class ClosurePredicate extends AbstractPredicate implements Decorator
{
/**
* @var \Closure
@ -31,4 +33,9 @@ class ClosurePredicate extends AbstractPredicate
{
return ($this->closure)(...$args);
}
public function getDecorated()
{
return $this->closure;
}
}

View File

@ -16,7 +16,6 @@ use function Kadet\Functional\predicate;
* @param $expected
*
* @return \Kadet\Functional\Predicate
* @throws \Exception
*/
function equals($expected): Predicate
{
@ -29,7 +28,6 @@ function equals($expected): Predicate
* @param $expected
*
* @return \Kadet\Functional\Predicate
* @throws \Exception
*/
function same($expected): Predicate
{
@ -42,7 +40,6 @@ function same($expected): Predicate
* @param $expected
*
* @return \Kadet\Functional\Predicate
* @throws \Exception
*/
function gt($expected): Predicate
{
@ -55,7 +52,6 @@ function gt($expected): Predicate
* @param $expected
*
* @return \Kadet\Functional\Predicate
* @throws \Exception
*/
function ge($expected): Predicate
{
@ -68,7 +64,6 @@ function ge($expected): Predicate
* @param $expected
*
* @return \Kadet\Functional\Predicate
* @throws \Exception
*/
function lt($expected): Predicate
{
@ -81,7 +76,6 @@ function lt($expected): Predicate
* @param $expected
*
* @return \Kadet\Functional\Predicate
* @throws \Exception
*/
function le($expected): Predicate
{

View File

@ -0,0 +1,27 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Reflection;
class ReflectionAppliedParameter extends \ReflectionParameter
{
private $value;
public function __construct($function, string $parameter, $value)
{
parent::__construct($function, $parameter);
$this->value = $value;
}
public function getValue()
{
return $this->value;
}
}

View File

@ -0,0 +1,58 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Reflection;
use Kadet\Functional\Utils\PartiallyAppliedFunction;
class ReflectionPartiallyAppliedFunction extends \ReflectionFunction
{
public $applied;
public function __construct(PartiallyAppliedFunction $applied)
{
parent::__construct($applied->getDecorated());
$this->applied = $applied;
}
public function getNumberOfParameters()
{
return max(parent::getNumberOfParameters() - count($this->applied->getArguments()), 0);
}
public function getNumberOfRequiredParameters()
{
return max(parent::getNumberOfRequiredParameters() - count($this->applied->getArguments()), 0);
}
public function getNumberOfAppliedParameters()
{
return count($this->applied->getArguments());
}
public function getAppliedParameters()
{
$applied = $this->applied->getArguments();
$parameters = array_intersect_key(parent::getParameters(), $applied);
return array_map(function (\ReflectionParameter $parameter) use ($applied) {
return new ReflectionAppliedParameter(
$this->applied->getDecorated(),
$parameter->name,
$applied[$parameter->getPosition()]
);
}, $parameters);
}
public function getParameters()
{
return array_diff_key(parent::getParameters(), $this->applied->getArguments());
}
}

View File

@ -10,8 +10,11 @@
namespace Kadet\Functional\Utils;
use const Kadet\Functional\_ as PLACEHOLDER;
use Kadet\Functional\Decorator;
use function Kadet\Functional\not;
use function Kadet\Functional\Predicates\same;
class PartiallyAppliedFunction
class PartiallyAppliedFunction implements Decorator
{
protected $callable;
protected $arguments = [];
@ -30,6 +33,11 @@ class PartiallyAppliedFunction
}
}
public function getArguments()
{
return array_filter($this->arguments, not(same(PLACEHOLDER)));
}
public function __invoke(...$args)
{
$arguments = $this->prepareArguments($args);
@ -50,4 +58,9 @@ class PartiallyAppliedFunction
array_values($args)
);
}
public function getDecorated()
{
return $this->callable;
}
}

View File

@ -48,10 +48,27 @@ class PartialApplicationTest extends \PHPUnit\Framework\TestCase
public function testPartialApplicationOfAppliedFunction()
{
$applied = f\partial('array_merge', ['a'], _, ['c']);
$double = f\partial($applied, _, ['d']);
$this->assertEquals(['a', 'b', 'c', 'd', 'e'], $double(['b'], ['e']));
}
public function testDecoration()
{
$x = function() {};
$applied = f\partial($x);
$this->assertSame($applied->getDecorated(), $x);
}
public function testArgumentProviding()
{
$applied = f\partial('array_merge', ['a'], _, ['c']);
$this->assertSame(
[0 => ['a'], 2 => ['c']],
$applied->getArguments()
);
}
}

View File

@ -0,0 +1,70 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
use Kadet\Functional as f;
use const Kadet\Functional\_;
use Kadet\Functional\Reflection\ReflectionAppliedParameter;
use Kadet\Functional\Reflection\ReflectionPartiallyAppliedFunction;
class ReflectionPartialAppliedFunctionTest extends \PHPUnit\Framework\TestCase
{
private $function;
protected function setUp()
{
$this->function = f\partial(function (array $a, array $b, array $c, array $d = ['d']) {
return array_merge($a, $b, $c, $d);
}, ['a'], _, ['c']);
}
public function testNumberOfParameters()
{
$reflection = new ReflectionPartiallyAppliedFunction($this->function);
$this->assertSame(2, $reflection->getNumberOfParameters());
}
public function testNumberOfRequiredParameters()
{
$reflection = new ReflectionPartiallyAppliedFunction($this->function);
$this->assertSame(1, $reflection->getNumberOfRequiredParameters());
}
public function testNumberOfAppliedParameters()
{
$reflection = new ReflectionPartiallyAppliedFunction($this->function);
$this->assertSame(2, $reflection->getNumberOfAppliedParameters());
}
public function testParameters()
{
$reflection = new ReflectionPartiallyAppliedFunction($this->function);
$parameters = $reflection->getParameters();
$this->assertContainsOnlyInstancesOf(ReflectionParameter::class, $parameters);
$this->assertCount(2, $parameters);
$this->assertEquals([1, 3], array_keys($parameters));
}
public function testAppliedParameters()
{
$reflection = new ReflectionPartiallyAppliedFunction($this->function);
$parameters = $reflection->getAppliedParameters();
$this->assertContainsOnlyInstancesOf(ReflectionAppliedParameter::class, $parameters);
$this->assertCount(2, $parameters);
$this->assertEquals([0, 2], array_keys($parameters));
$this->assertEquals([0 => ['a'], 2 => ['c']], array_map(function(ReflectionAppliedParameter $parameter) {
return $parameter->getValue();
}, $parameters));
}
}