add ref method with arity setting

This commit is contained in:
Kacper Donat 2019-03-17 14:38:28 +01:00
parent 0b58a4c620
commit 9e348d929c
2 changed files with 77 additions and 14 deletions

View File

@ -27,31 +27,54 @@ function predicate($predicate): Predicate
case $predicate instanceof Predicate:
return $predicate;
case is_callable($predicate):
return new ClosurePredicate(\Closure::fromCallable($predicate));
return new ClosurePredicate(closure($predicate));
default:
throw new \InvalidArgumentException('$predicate is not valid predicate.');
}
}
function ref($callable)
function closure($callable)
{
if (is_array($callable)) {
switch (true) {
case $callable instanceof \Closure:
return $callable;
case is_array($callable):
list($object, $method) = $callable;
$reflection = new \ReflectionObject($object);
$method = $reflection->getMethod($method);
if (!$method->isPublic()) {
$class = debug_backtrace()[1]['class'] ?? null;
$backtrace = debug_backtrace();
foreach ($backtrace as $trace) {
if (isset($trace['class']) && $trace['class'] === $reflection->name) {
break;
}
if ($reflection->name !== $class) {
if ((isset($trace['class']) && strpos($trace['class'], "Kadet\\Functional\\") !== 0)
|| ($trace['function'] && strpos($trace['function'], "Kadet\\Functional\\") !== 0)) {
throw new NoAccessException(sprintf('Method "%s" can only be accessed from inside of object.', $method->name));
}
}
}
return $method->getClosure($object);
default:
return \Closure::fromCallable($callable);
}
}
return \Closure::fromCallable($callable);
function arify($callable, int $arity)
{
return function (...$args) use ($callable, $arity) {
return $callable(...array_slice($args, 0, $arity));
};
}
function ref($callable, ?int $arity = null) {
$closure = closure($callable);
return $arity !== null ? arify($callable, $arity) : $closure;
}
function not($predicate)
@ -101,6 +124,10 @@ function repeat($times, callable $callback)
}
}
function identity($x) {
return $x;
}
/**
* @param callable $callable
*

View File

@ -1,6 +1,7 @@
<?php
use function Kadet\Functional\apply;
use function Kadet\Functional\arify;
use function Kadet\Functional\ref;
/**
@ -29,8 +30,15 @@ class TestObject
}
}
class RandomTest extends \PHPUnit\Framework\TestCase
class ReferencingTest extends \PHPUnit\Framework\TestCase
{
public function testReferenceClosure()
{
$f = function () { };
$this->assertSame($f, ref($f));
}
public function testApplicationOfArrayNotation()
{
$object = new TestObject();
@ -54,4 +62,32 @@ class RandomTest extends \PHPUnit\Framework\TestCase
ref([$object, 'priv']);
}
public function testSetFunctionArity()
{
$f = function (...$args) {
return count($args);
};
$f = arify($f, 2);
$this->assertSame(1, $f(1));
$this->assertSame(2, $f(1, 2));
$this->assertSame(2, $f(1, 2, 3));
$this->assertSame(2, $f(1, 2, 3, 4));
}
public function testReferencingWithSpecificArity()
{
$f = function (...$args) {
return count($args);
};
$f = ref($f, 2);
$this->assertSame(1, $f(1));
$this->assertSame(2, $f(1, 2));
$this->assertSame(2, $f(1, 2, 3));
$this->assertSame(2, $f(1, 2, 3, 4));
}
}