Compare commits

...

3 Commits

Author SHA1 Message Date
Kacper Donat
12918eb3cb fix deprecations on php 8.4+
Mark threshold parameters as explicitly nullable and silence return type
deprecations of ReflectionDecoratedFunction while keeping php 7.1 support.
2026-10-25 16:01:51 +01:00
Kacper Donat
fff1013711 add transforms test 2019-03-17 15:26:16 +01:00
Kacper Donat
9e348d929c add ref method with arity setting 2019-03-17 14:38:28 +01:00
7 changed files with 264 additions and 33 deletions

View File

@ -12,6 +12,8 @@ namespace Kadet\Functional\Predicates;
use function Kadet\Functional\predicate; use function Kadet\Functional\predicate;
use Kadet\Functional\Predicate\ClosurePredicate; use Kadet\Functional\Predicate\ClosurePredicate;
use Kadet\Functional\Transforms as t;
use Kadet\Functional as f;
/** /**
* @param string $method * @param string $method
@ -22,11 +24,7 @@ use Kadet\Functional\Predicate\ClosurePredicate;
*/ */
function method(string $method, $predicate, ...$args) function method(string $method, $predicate, ...$args)
{ {
$predicate = predicate($predicate); return predicate(f\pipe(t\method($method, ...$args), $predicate));
return new ClosurePredicate(function ($object) use ($method, $predicate, $args) {
return $predicate($object->{$method}($args));
});
} }
/** /**
@ -35,18 +33,20 @@ function method(string $method, $predicate, ...$args)
* *
* @return \Kadet\Functional\Predicate * @return \Kadet\Functional\Predicate
*/ */
function property(string $property, $predicate) { function property(string $property, $predicate)
$predicate = predicate($predicate); {
return predicate(f\pipe(t\property($property), $predicate));
}
return new ClosurePredicate(function ($object) use ($property, $predicate) { /**
if (isset($object->$property)) { * @param string $field
return $predicate($object->$property); * @param $predicate
} elseif (method_exists($object, $getter = 'get'.ucfirst($property))) { *
return $predicate($object->$getter()); * @return \Kadet\Functional\Predicate
} else { */
return false; function field(string $field, $predicate)
} {
}); return predicate(f\pipe(t\field($field), $predicate));
} }
function instance(string $class) function instance(string $class)

View File

@ -23,126 +23,176 @@ class ReflectionDecoratedFunction extends \ReflectionFunction
$this->reflection = reflect($decorated); $this->reflection = reflect($decorated);
} }
#[\ReturnTypeWillChange]
public function inNamespace() public function inNamespace()
{ {
return $this->reflection->inNamespace(); return $this->reflection->inNamespace();
} }
#[\ReturnTypeWillChange]
public function isClosure() public function isClosure()
{ {
return $this->reflection->isVariadic(); return $this->reflection->isVariadic();
} }
#[\ReturnTypeWillChange]
public function isDeprecated() public function isDeprecated()
{ {
return $this->reflection->isDeprecated(); return $this->reflection->isDeprecated();
} }
#[\ReturnTypeWillChange]
public function isInternal() public function isInternal()
{ {
return $this->reflection->isInternal(); return $this->reflection->isInternal();
} }
#[\ReturnTypeWillChange]
public function isUserDefined() public function isUserDefined()
{ {
return $this->reflection->isUserDefined(); return $this->reflection->isUserDefined();
} }
#[\ReturnTypeWillChange]
public function getClosureThis() public function getClosureThis()
{ {
return $this->reflection->getClosureThis(); return $this->reflection->getClosureThis();
} }
#[\ReturnTypeWillChange]
public function getClosureScopeClass() public function getClosureScopeClass()
{ {
return $this->reflection->getClosureScopeClass(); return $this->reflection->getClosureScopeClass();
} }
#[\ReturnTypeWillChange]
public function getDocComment() public function getDocComment()
{ {
return $this->reflection->getDocComment(); return $this->reflection->getDocComment();
} }
#[\ReturnTypeWillChange]
public function getEndLine() public function getEndLine()
{ {
return $this->reflection->getEndLine(); return $this->reflection->getEndLine();
} }
#[\ReturnTypeWillChange]
public function getExtension() public function getExtension()
{ {
return $this->reflection->getExtension(); return $this->reflection->getExtension();
} }
#[\ReturnTypeWillChange]
public function getExtensionName() public function getExtensionName()
{ {
return $this->reflection->getExtensionName(); return $this->reflection->getExtensionName();
} }
#[\ReturnTypeWillChange]
public function getFileName() public function getFileName()
{ {
return $this->reflection->getFileName(); return $this->reflection->getFileName();
} }
#[\ReturnTypeWillChange]
public function getName() public function getName()
{ {
return $this->reflection->getName(); return $this->reflection->getName();
} }
#[\ReturnTypeWillChange]
public function getNamespaceName() public function getNamespaceName()
{ {
return $this->reflection->getNamespaceName(); return $this->reflection->getNamespaceName();
} }
#[\ReturnTypeWillChange]
public function getNumberOfParameters() public function getNumberOfParameters()
{ {
return $this->reflection->getNumberOfParameters(); return $this->reflection->getNumberOfParameters();
} }
#[\ReturnTypeWillChange]
public function getNumberOfRequiredParameters() public function getNumberOfRequiredParameters()
{ {
return $this->reflection->getNumberOfRequiredParameters(); return $this->reflection->getNumberOfRequiredParameters();
} }
#[\ReturnTypeWillChange]
public function getParameters() public function getParameters()
{ {
return $this->reflection->getParameters(); return $this->reflection->getParameters();
} }
#[\ReturnTypeWillChange]
public function getReturnType() public function getReturnType()
{ {
return $this->reflection->getReturnType(); return $this->reflection->getReturnType();
} }
#[\ReturnTypeWillChange]
public function getShortName() public function getShortName()
{ {
return $this->reflection->getShortName(); return $this->reflection->getShortName();
} }
#[\ReturnTypeWillChange]
public function getStartLine() public function getStartLine()
{ {
return $this->reflection->getStartLine(); return $this->reflection->getStartLine();
} }
#[\ReturnTypeWillChange]
public function getStaticVariables() public function getStaticVariables()
{ {
return $this->reflection->getStaticVariables(); return $this->reflection->getStaticVariables();
} }
#[\ReturnTypeWillChange]
public function hasReturnType() public function hasReturnType()
{ {
return $this->reflection->hasReturnType(); return $this->reflection->hasReturnType();
} }
#[\ReturnTypeWillChange]
public function returnsReference() public function returnsReference()
{ {
return $this->reflection->returnsReference(); return $this->reflection->returnsReference();
} }
#[\ReturnTypeWillChange]
public function isGenerator() public function isGenerator()
{ {
return $this->reflection->isGenerator(); return $this->reflection->isGenerator();
} }
#[\ReturnTypeWillChange]
public function isVariadic() public function isVariadic()
{ {
return $this->reflection->isVariadic(); return $this->reflection->isVariadic();

View File

@ -25,7 +25,7 @@ class CurriedFunction extends PartiallyAppliedFunction
* @param int|null $threshold * @param int|null $threshold
* @param array $args * @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); parent::__construct($callable, ...$args);
$this->threshold = $threshold ?: reflect($callable)->getNumberOfRequiredParameters(); $this->threshold = $threshold ?: reflect($callable)->getNumberOfRequiredParameters();
@ -36,7 +36,7 @@ class CurriedFunction extends PartiallyAppliedFunction
return $this->threshold; return $this->threshold;
} }
public function withThreshold(int $threshold = null) public function withThreshold(?int $threshold = null)
{ {
return new static($this, $threshold); return new static($this, $threshold);
} }

View File

@ -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); return new CurriedFunction($callable, $threshold, ...$args);
} }

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) {
list($object, $method) = $callable; case $callable instanceof \Closure:
$reflection = new \ReflectionObject($object); return $callable;
$method = $reflection->getMethod($method);
if (!$method->isPublic()) { case is_array($callable):
$class = debug_backtrace()[1]['class'] ?? null; list($object, $method) = $callable;
$reflection = new \ReflectionObject($object);
$method = $reflection->getMethod($method);
if ($reflection->name !== $class) { if (!$method->isPublic()) {
throw new NoAccessException(sprintf('Method "%s" can only be accessed from inside of object.', $method->name)); $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) 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

@ -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);
}
}

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));
}
} }