Compare commits
3 Commits
0b58a4c620
...
12918eb3cb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12918eb3cb | ||
|
|
fff1013711 | ||
|
|
9e348d929c |
@ -12,6 +12,8 @@ namespace Kadet\Functional\Predicates;
|
||||
|
||||
use function Kadet\Functional\predicate;
|
||||
use Kadet\Functional\Predicate\ClosurePredicate;
|
||||
use Kadet\Functional\Transforms as t;
|
||||
use Kadet\Functional as f;
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
@ -22,11 +24,7 @@ use Kadet\Functional\Predicate\ClosurePredicate;
|
||||
*/
|
||||
function method(string $method, $predicate, ...$args)
|
||||
{
|
||||
$predicate = predicate($predicate);
|
||||
|
||||
return new ClosurePredicate(function ($object) use ($method, $predicate, $args) {
|
||||
return $predicate($object->{$method}($args));
|
||||
});
|
||||
return predicate(f\pipe(t\method($method, ...$args), $predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,18 +33,20 @@ function method(string $method, $predicate, ...$args)
|
||||
*
|
||||
* @return \Kadet\Functional\Predicate
|
||||
*/
|
||||
function property(string $property, $predicate) {
|
||||
$predicate = predicate($predicate);
|
||||
|
||||
return new ClosurePredicate(function ($object) use ($property, $predicate) {
|
||||
if (isset($object->$property)) {
|
||||
return $predicate($object->$property);
|
||||
} elseif (method_exists($object, $getter = 'get'.ucfirst($property))) {
|
||||
return $predicate($object->$getter());
|
||||
} else {
|
||||
return false;
|
||||
function property(string $property, $predicate)
|
||||
{
|
||||
return predicate(f\pipe(t\property($property), $predicate));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param $predicate
|
||||
*
|
||||
* @return \Kadet\Functional\Predicate
|
||||
*/
|
||||
function field(string $field, $predicate)
|
||||
{
|
||||
return predicate(f\pipe(t\field($field), $predicate));
|
||||
}
|
||||
|
||||
function instance(string $class)
|
||||
|
||||
@ -23,126 +23,176 @@ class ReflectionDecoratedFunction extends \ReflectionFunction
|
||||
$this->reflection = reflect($decorated);
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function inNamespace()
|
||||
{
|
||||
return $this->reflection->inNamespace();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function isClosure()
|
||||
{
|
||||
return $this->reflection->isVariadic();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function isDeprecated()
|
||||
{
|
||||
return $this->reflection->isDeprecated();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function isInternal()
|
||||
{
|
||||
return $this->reflection->isInternal();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function isUserDefined()
|
||||
{
|
||||
return $this->reflection->isUserDefined();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getClosureThis()
|
||||
{
|
||||
return $this->reflection->getClosureThis();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getClosureScopeClass()
|
||||
{
|
||||
return $this->reflection->getClosureScopeClass();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getDocComment()
|
||||
{
|
||||
return $this->reflection->getDocComment();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getEndLine()
|
||||
{
|
||||
return $this->reflection->getEndLine();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getExtension()
|
||||
{
|
||||
return $this->reflection->getExtension();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getExtensionName()
|
||||
{
|
||||
return $this->reflection->getExtensionName();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getFileName()
|
||||
{
|
||||
return $this->reflection->getFileName();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->reflection->getName();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getNamespaceName()
|
||||
{
|
||||
return $this->reflection->getNamespaceName();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getNumberOfParameters()
|
||||
{
|
||||
return $this->reflection->getNumberOfParameters();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getNumberOfRequiredParameters()
|
||||
{
|
||||
return $this->reflection->getNumberOfRequiredParameters();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getParameters()
|
||||
{
|
||||
return $this->reflection->getParameters();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getReturnType()
|
||||
{
|
||||
return $this->reflection->getReturnType();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getShortName()
|
||||
{
|
||||
return $this->reflection->getShortName();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getStartLine()
|
||||
{
|
||||
return $this->reflection->getStartLine();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function getStaticVariables()
|
||||
{
|
||||
return $this->reflection->getStaticVariables();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function hasReturnType()
|
||||
{
|
||||
return $this->reflection->hasReturnType();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function returnsReference()
|
||||
{
|
||||
return $this->reflection->returnsReference();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function isGenerator()
|
||||
{
|
||||
return $this->reflection->isGenerator();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
|
||||
public function isVariadic()
|
||||
{
|
||||
return $this->reflection->isVariadic();
|
||||
|
||||
@ -25,7 +25,7 @@ class CurriedFunction extends PartiallyAppliedFunction
|
||||
* @param int|null $threshold
|
||||
* @param array $args
|
||||
*/
|
||||
public function __construct(callable $callable, int $threshold = null, ...$args)
|
||||
public function __construct(callable $callable, ?int $threshold = null, ...$args)
|
||||
{
|
||||
parent::__construct($callable, ...$args);
|
||||
$this->threshold = $threshold ?: reflect($callable)->getNumberOfRequiredParameters();
|
||||
@ -36,7 +36,7 @@ class CurriedFunction extends PartiallyAppliedFunction
|
||||
return $this->threshold;
|
||||
}
|
||||
|
||||
public function withThreshold(int $threshold = null)
|
||||
public function withThreshold(?int $threshold = null)
|
||||
{
|
||||
return new static($this, $threshold);
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ function apply(callable $callable, ...$args)
|
||||
}
|
||||
}
|
||||
|
||||
function curry(callable $callable, int $threshold = null, ...$args): CurriedFunction
|
||||
function curry(callable $callable, ?int $threshold = null, ...$args): CurriedFunction
|
||||
{
|
||||
return new CurriedFunction($callable, $threshold, ...$args);
|
||||
}
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
118
tests/ObjectTransformsTest.php
Normal file
118
tests/ObjectTransformsTest.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019 Kacper Donat
|
||||
*
|
||||
* @author Kacper "Kadet" Donat <kacper@kadet.net>
|
||||
*
|
||||
* Full license available in separate LICENSE file
|
||||
*/
|
||||
|
||||
class PropertiesObject {
|
||||
private $getter;
|
||||
private $magic = [];
|
||||
public $field = 'value';
|
||||
|
||||
public function __construct($field, $getter, array $magic = [])
|
||||
{
|
||||
$this->field = $field;
|
||||
$this->getter = $getter;
|
||||
$this->magic = $magic;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return $this->getter;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->magic[$name];
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return __CLASS__;
|
||||
}
|
||||
}
|
||||
|
||||
class ObjectTransformsTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
use RandomDataTrait;
|
||||
|
||||
/**
|
||||
* @dataProvider randomDataProvider
|
||||
*/
|
||||
public function testFieldTransform($data = null)
|
||||
{
|
||||
$object = new PropertiesObject($data, null, []);
|
||||
$transform = \Kadet\Functional\Transforms\field('field');
|
||||
|
||||
$this->assertSame($transform($object), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider randomDataProvider
|
||||
*/
|
||||
public function testFieldTransformMagic($data = null)
|
||||
{
|
||||
$object = new PropertiesObject(false, false, ['magic' => $data]);
|
||||
$transform = \Kadet\Functional\Transforms\property('magic');
|
||||
|
||||
$this->assertSame($transform($object), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider randomDataProvider
|
||||
*/
|
||||
public function testPropertyTransformGetter($data = null)
|
||||
{
|
||||
$object = new PropertiesObject(false, $data, []);
|
||||
$transform = \Kadet\Functional\Transforms\property('value');
|
||||
|
||||
$this->assertSame($transform($object), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider randomDataProvider
|
||||
*/
|
||||
public function testGetterTransform($data = null)
|
||||
{
|
||||
$object = new PropertiesObject(false, $data, []);
|
||||
$transform = \Kadet\Functional\Transforms\getter('value');
|
||||
|
||||
$this->assertSame($transform($object), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider randomDataProvider
|
||||
*/
|
||||
public function testPropertyTransformField($data = null)
|
||||
{
|
||||
$object = new PropertiesObject($data, false, []);
|
||||
$transform = \Kadet\Functional\Transforms\property('field');
|
||||
|
||||
$this->assertSame($transform($object), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider randomDataProvider
|
||||
*/
|
||||
public function testPropertyTransformMagic($data = null)
|
||||
{
|
||||
$object = new PropertiesObject(false, false, ['magic' => $data]);
|
||||
$transform = \Kadet\Functional\Transforms\property('magic');
|
||||
|
||||
$this->assertSame($transform($object), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider randomDataProvider
|
||||
*/
|
||||
public function testMethodTransform($data = null)
|
||||
{
|
||||
$object = new PropertiesObject(false, false);
|
||||
$transform = \Kadet\Functional\Transforms\method('__toString');
|
||||
|
||||
$this->assertSame($transform($object), PropertiesObject::class);
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user