From 9e348d929c1909c799b862668a74cc8accc96725 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 17 Mar 2019 14:38:28 +0100 Subject: [PATCH] add ref method with arity setting --- src/functions.php | 53 ++++++++++++++----- tests/{RandomTest.php => ReferencingTest.php} | 38 ++++++++++++- 2 files changed, 77 insertions(+), 14 deletions(-) rename tests/{RandomTest.php => ReferencingTest.php} (54%) diff --git a/src/functions.php b/src/functions.php index 98cd039..6deb6ee 100644 --- a/src/functions.php +++ b/src/functions.php @@ -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)) { - list($object, $method) = $callable; - $reflection = new \ReflectionObject($object); - $method = $reflection->getMethod($method); + switch (true) { + case $callable instanceof \Closure: + return $callable; - if (!$method->isPublic()) { - $class = debug_backtrace()[1]['class'] ?? null; + case is_array($callable): + list($object, $method) = $callable; + $reflection = new \ReflectionObject($object); + $method = $reflection->getMethod($method); - if ($reflection->name !== $class) { - throw new NoAccessException(sprintf('Method "%s" can only be accessed from inside of object.', $method->name)); + if (!$method->isPublic()) { + $backtrace = debug_backtrace(); + foreach ($backtrace as $trace) { + if (isset($trace['class']) && $trace['class'] === $reflection->name) { + break; + } + + 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); + 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 * diff --git a/tests/RandomTest.php b/tests/ReferencingTest.php similarity index 54% rename from tests/RandomTest.php rename to tests/ReferencingTest.php index 21a0a61..4e80869 100644 --- a/tests/RandomTest.php +++ b/tests/ReferencingTest.php @@ -1,6 +1,7 @@ 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)); + } } \ No newline at end of file