diff --git a/src/Exception/ApplicationException.php b/src/Exception/ApplicationException.php new file mode 100644 index 0000000..82a4d0e --- /dev/null +++ b/src/Exception/ApplicationException.php @@ -0,0 +1,16 @@ + + * + * Full license available in separate LICENSE file + */ + +namespace Kadet\Functional\Exception; + + +class ApplicationException extends FunctionalException +{ + +} \ No newline at end of file diff --git a/src/Exception/FunctionalException.php b/src/Exception/FunctionalException.php new file mode 100644 index 0000000..cb6549d --- /dev/null +++ b/src/Exception/FunctionalException.php @@ -0,0 +1,16 @@ + + * + * Full license available in separate LICENSE file + */ + +namespace Kadet\Functional\Exception; + + +class FunctionalException extends \RuntimeException +{ + +} \ No newline at end of file diff --git a/src/Reflection/ReflectionDecoratedFunction.php b/src/Reflection/ReflectionDecoratedFunction.php new file mode 100644 index 0000000..cb8b628 --- /dev/null +++ b/src/Reflection/ReflectionDecoratedFunction.php @@ -0,0 +1,150 @@ + + * + * 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(); + } +} \ No newline at end of file diff --git a/src/Reflection/ReflectionFixedFunction.php b/src/Reflection/ReflectionFixedFunction.php index cc72f81..76bb455 100644 --- a/src/Reflection/ReflectionFixedFunction.php +++ b/src/Reflection/ReflectionFixedFunction.php @@ -12,7 +12,7 @@ namespace Kadet\Functional\Reflection; use Kadet\Functional\Utils\FixedFunction; -class ReflectionFixedFunction extends \ReflectionFunction +class ReflectionFixedFunction extends ReflectionDecoratedFunction { public function __construct(FixedFunction $function) { diff --git a/src/Reflection/ReflectionPartiallyAppliedFunction.php b/src/Reflection/ReflectionPartiallyAppliedFunction.php index c5b6162..164bb40 100644 --- a/src/Reflection/ReflectionPartiallyAppliedFunction.php +++ b/src/Reflection/ReflectionPartiallyAppliedFunction.php @@ -13,7 +13,7 @@ namespace Kadet\Functional\Reflection; use Kadet\Functional\Utils\CurriedFunction; use Kadet\Functional\Utils\PartiallyAppliedFunction; -class ReflectionPartiallyAppliedFunction extends \ReflectionFunction +class ReflectionPartiallyAppliedFunction extends ReflectionDecoratedFunction { public $applied; diff --git a/src/Utils/FixedFunction.php b/src/Utils/FixedFunction.php index e86582d..5cc38c9 100644 --- a/src/Utils/FixedFunction.php +++ b/src/Utils/FixedFunction.php @@ -32,7 +32,7 @@ class FixedFunction implements Decorator public function __invoke(...$args) { - return ($this->fixed)($this)(...$args); + return apply($this->fixed, $this, ...$args); } public function getDecorated() diff --git a/src/Utils/index.php b/src/Utils/index.php index ae82d71..df42adb 100644 --- a/src/Utils/index.php +++ b/src/Utils/index.php @@ -21,11 +21,29 @@ function partial(callable $callable, ...$args): PartiallyAppliedFunction function apply(callable $callable, ...$args) { - $threshold = reflect($callable)->getNumberOfRequiredParameters(); + try { + $reflection = reflect($callable); + $required = $reflection->getNumberOfRequiredParameters(); + $all = $reflection->getNumberOfParameters(); - return count($args) >= $threshold - ? $callable(...$args) - : partial($callable, ...$args); + switch (true) { + case $reflection->isVariadic(): + 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 diff --git a/src/functions.php b/src/functions.php index d55ac99..6986946 100644 --- a/src/functions.php +++ b/src/functions.php @@ -6,7 +6,6 @@ use Kadet\Functional\Predicate\AllOfPredicate; use Kadet\Functional\Predicate\AnyOfPredicate; use Kadet\Functional\Predicate\ClosurePredicate; use Kadet\Functional\Predicate\ConstantPredicate; -use function Kadet\Functional\Predicates\instance; use Kadet\Functional\Reflection\ReflectionFixedFunction; use Kadet\Functional\Reflection\ReflectionPartiallyAppliedFunction; use Kadet\Functional\Utils\FixedFunction; @@ -71,7 +70,13 @@ function symbol($name = null) 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) { case $callable instanceof PartiallyAppliedFunction: