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: case $predicate instanceof Predicate:
return $predicate; return $predicate;
case is_callable($predicate): case is_callable($predicate):
return new ClosurePredicate(\Closure::fromCallable($predicate)); return new ClosurePredicate(closure($predicate));
default: default:
throw new \InvalidArgumentException('$predicate is not valid predicate.'); 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; list($object, $method) = $callable;
$reflection = new \ReflectionObject($object); $reflection = new \ReflectionObject($object);
$method = $reflection->getMethod($method); $method = $reflection->getMethod($method);
if (!$method->isPublic()) { 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)); throw new NoAccessException(sprintf('Method "%s" can only be accessed from inside of object.', $method->name));
} }
} }
}
return $method->getClosure($object); 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) function not($predicate)
@ -101,6 +124,10 @@ function repeat($times, callable $callback)
} }
} }
function identity($x) {
return $x;
}
/** /**
* @param callable $callable * @param callable $callable
* *

View File

@ -1,6 +1,7 @@
<?php <?php
use function Kadet\Functional\apply; use function Kadet\Functional\apply;
use function Kadet\Functional\arify;
use function Kadet\Functional\ref; 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() public function testApplicationOfArrayNotation()
{ {
$object = new TestObject(); $object = new TestObject();
@ -54,4 +62,32 @@ class RandomTest extends \PHPUnit\Framework\TestCase
ref([$object, 'priv']); 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));
}
} }