reflection of function decorators

This commit is contained in:
Kacper Donat 2018-08-23 21:43:49 +02:00
parent dcee45e10b
commit e46fbb3cc0
8 changed files with 214 additions and 9 deletions

View File

@ -0,0 +1,16 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Exception;
class ApplicationException extends FunctionalException
{
}

View File

@ -0,0 +1,16 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Exception;
class FunctionalException extends \RuntimeException
{
}

View File

@ -0,0 +1,150 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Reflection;
use function Kadet\Functional\reflect;
class ReflectionDecoratedFunction extends \ReflectionFunction
{
public $reflection;
public $decorated;
public function __construct(callable $decorated)
{
$this->decorated = $decorated;
$this->reflection = reflect($decorated);
}
public function inNamespace()
{
return $this->reflection->inNamespace();
}
public function isClosure()
{
return $this->reflection->isVariadic();
}
public function isDeprecated()
{
return $this->reflection->isDeprecated();
}
public function isInternal()
{
return $this->reflection->isInternal();
}
public function isUserDefined()
{
return $this->reflection->isUserDefined();
}
public function getClosureThis()
{
return $this->reflection->getClosureThis();
}
public function getClosureScopeClass()
{
return $this->reflection->getClosureScopeClass();
}
public function getDocComment()
{
return $this->reflection->getDocComment();
}
public function getEndLine()
{
return $this->reflection->getEndLine();
}
public function getExtension()
{
return $this->reflection->getExtension();
}
public function getExtensionName()
{
return $this->reflection->getExtensionName();
}
public function getFileName()
{
return $this->reflection->getFileName();
}
public function getName()
{
return $this->reflection->getName();
}
public function getNamespaceName()
{
return $this->reflection->getNamespaceName();
}
public function getNumberOfParameters()
{
return $this->reflection->getNumberOfParameters();
}
public function getNumberOfRequiredParameters()
{
return $this->reflection->getNumberOfRequiredParameters();
}
public function getParameters()
{
return $this->reflection->getParameters();
}
public function getReturnType()
{
return $this->reflection->getReturnType();
}
public function getShortName()
{
return $this->reflection->getShortName();
}
public function getStartLine()
{
return $this->reflection->getStartLine();
}
public function getStaticVariables()
{
return $this->reflection->getStaticVariables();
}
public function hasReturnType()
{
return $this->reflection->hasReturnType();
}
public function returnsReference()
{
return $this->reflection->returnsReference();
}
public function isGenerator()
{
return $this->reflection->isGenerator();
}
public function isVariadic()
{
return $this->reflection->isVariadic();
}
}

View File

@ -12,7 +12,7 @@ namespace Kadet\Functional\Reflection;
use Kadet\Functional\Utils\FixedFunction; use Kadet\Functional\Utils\FixedFunction;
class ReflectionFixedFunction extends \ReflectionFunction class ReflectionFixedFunction extends ReflectionDecoratedFunction
{ {
public function __construct(FixedFunction $function) public function __construct(FixedFunction $function)
{ {

View File

@ -13,7 +13,7 @@ namespace Kadet\Functional\Reflection;
use Kadet\Functional\Utils\CurriedFunction; use Kadet\Functional\Utils\CurriedFunction;
use Kadet\Functional\Utils\PartiallyAppliedFunction; use Kadet\Functional\Utils\PartiallyAppliedFunction;
class ReflectionPartiallyAppliedFunction extends \ReflectionFunction class ReflectionPartiallyAppliedFunction extends ReflectionDecoratedFunction
{ {
public $applied; public $applied;

View File

@ -32,7 +32,7 @@ class FixedFunction implements Decorator
public function __invoke(...$args) public function __invoke(...$args)
{ {
return ($this->fixed)($this)(...$args); return apply($this->fixed, $this, ...$args);
} }
public function getDecorated() public function getDecorated()

View File

@ -21,11 +21,29 @@ function partial(callable $callable, ...$args): PartiallyAppliedFunction
function apply(callable $callable, ...$args) function apply(callable $callable, ...$args)
{ {
$threshold = reflect($callable)->getNumberOfRequiredParameters(); try {
$reflection = reflect($callable);
$required = $reflection->getNumberOfRequiredParameters();
$all = $reflection->getNumberOfParameters();
return count($args) >= $threshold switch (true) {
? $callable(...$args) case $reflection->isVariadic():
: partial($callable, ...$args); return partial($callable, ...$args);
case count($args) >= $all:
$applied = $callable(...array_slice($args, 0, $all));
return is_callable($applied) ? apply($applied, ...array_slice($args, $all)) : $applied;
case count($args) >= $required:
return $callable(...$args);
default:
return partial($callable, ...$args);
}
} catch (\ReflectionException $exception) {
// if cannot gather necessary information for intelligent application just use normal one
return $callable(...$args);
}
} }
function curry(callable $callable, int $threshold = null, ...$args): CurriedFunction function curry(callable $callable, int $threshold = null, ...$args): CurriedFunction

View File

@ -6,7 +6,6 @@ use Kadet\Functional\Predicate\AllOfPredicate;
use Kadet\Functional\Predicate\AnyOfPredicate; use Kadet\Functional\Predicate\AnyOfPredicate;
use Kadet\Functional\Predicate\ClosurePredicate; use Kadet\Functional\Predicate\ClosurePredicate;
use Kadet\Functional\Predicate\ConstantPredicate; use Kadet\Functional\Predicate\ConstantPredicate;
use function Kadet\Functional\Predicates\instance;
use Kadet\Functional\Reflection\ReflectionFixedFunction; use Kadet\Functional\Reflection\ReflectionFixedFunction;
use Kadet\Functional\Reflection\ReflectionPartiallyAppliedFunction; use Kadet\Functional\Reflection\ReflectionPartiallyAppliedFunction;
use Kadet\Functional\Utils\FixedFunction; use Kadet\Functional\Utils\FixedFunction;
@ -71,7 +70,13 @@ function symbol($name = null)
return sprintf("%s(%s)", $name ?: 'symbol', $id); return sprintf("%s(%s)", $name ?: 'symbol', $id);
} }
function reflect(callable $callable): \ReflectionFunctionAbstract /**
* @param callable $callable
*
* @return \ReflectionFunction
* @throws \ReflectionException
*/
function reflect(callable $callable): \ReflectionFunction
{ {
switch (true) { switch (true) {
case $callable instanceof PartiallyAppliedFunction: case $callable instanceof PartiallyAppliedFunction: