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 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);
function property(string $property, $predicate)
{
return predicate(f\pipe(t\property($property), $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;
}
});
/**
* @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)

View File

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

View File

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

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

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)) {
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
*

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