Completed
Pull Request — master (#13)
by Paweł
03:59 queued 01:37
created

PartialApplication::countRequiredArguments()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 1
nop 1
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Scalp;
6
7
use function Scalp\Reflection\reflectionFunction;
0 ignored issues
show
Bug introduced by
The type Scalp\Reflection\reflectionFunction was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
9
final class PartialApplication
10
{
11
    private $f;
12
    private $arguments;
13
14
    public function __construct(callable $f, array $arguments)
15
    {
16
        $this->guardAgainstMissingArguments($f, $arguments);
17
18
        $this->f = $f;
19
        $this->arguments = $arguments;
20
    }
21
22
    public function __invoke()
23
    {
24
        $appliedArguments = $this->applyArguments(func_get_args());
25
26
        $this->guardAgainstMissingAppliedArguments($appliedArguments);
27
28
        return ($this->f)(...$appliedArguments);
29
    }
30
31
    private function applyArguments(array $arguments): array
32
    {
33
        $argsIterator = new \ArrayIterator($arguments);
34
35
        $replacePlaceholders = function ($arg) use ($argsIterator) {
36
            $replacement = $arg === __ && $argsIterator->valid()
0 ignored issues
show
Bug introduced by
The constant __ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
37
                ? $argsIterator->current()
38
                : $arg;
39
40
            $argsIterator->next();
41
42
            return $replacement;
43
        };
44
45
        return array_map($replacePlaceholders, $this->arguments);
46
    }
47
48
    private function guardAgainstMissingArguments(callable $f, array $arguments): void
49
    {
50
        $required = $this->countRequiredArguments($f);
51
52
        if (\count($arguments) < $required) {
53
            throw new \BadFunctionCallException('Number of passed arguments is less than required arguments. Use `__` const to add placeholder or value to apply.');
54
        }
55
    }
56
57
    private function guardAgainstMissingAppliedArguments(array $appliedArguments): void
58
    {
59
        $placeholders = $this->placeholderArguments($appliedArguments);
60
61
        if ($placeholders !== []) {
62
            throw new \BadFunctionCallException(sprintf(
63
                'Partially applied function has %d missing argument%s at position: %s.',
64
                \count($placeholders),
65
                \count($placeholders) > 1 ? 's' : '',
66
                implode(', ', array_map(function (int $idx): int {
67
                    return $idx + 1;
68
                }, array_keys($placeholders)))
69
            ));
70
        }
71
    }
72
73
    private function countRequiredArguments(callable $f): int
74
    {
75
        $rf = reflectionFunction($f);
76
77
        [$count, $required] = array_reduce($rf->getParameters(), function (array $carry, \ReflectionParameter $parameter): array {
78
            $count = $carry[0] + 1;
79
            $required = $parameter->isOptional() ? $carry[1] : $count;
80
81
            return [$count, $required];
82
        }, [0, 0]);
83
84
        return $required;
85
    }
86
87
    private function placeholderArguments(array $arguments): array
88
    {
89
        return array_filter($arguments, function ($arg): bool { return $arg === __; });
0 ignored issues
show
Bug introduced by
The constant __ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
90
    }
91
}
92