Passed
Push — 1.x ( ac6076...be1e2f )
by Kevin
01:10
created

Callback::invoke()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 14
c 0
b 0
f 0
nc 6
nop 1
dl 0
loc 27
rs 8.8333
1
<?php
2
3
namespace Zenstruck;
4
5
use Zenstruck\Callback\Exception\UnresolveableArgument;
6
use Zenstruck\Callback\Parameter;
7
8
/**
9
 * @author Kevin Bond <[email protected]>
10
 */
11
final class Callback
12
{
13
    /** @var \ReflectionFunction */
14
    private $function;
15
16
    private function __construct(\ReflectionFunction $function)
17
    {
18
        $this->function = $function;
19
    }
20
21
    /**
22
     * @param callable|\ReflectionFunction $value
23
     */
24
    public static function createFor($value): self
25
    {
26
        if (\is_callable($value)) {
27
            $value = new \ReflectionFunction(\Closure::fromCallable($value));
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type ReflectionFunction; however, parameter $callback of Closure::fromCallable() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

27
            $value = new \ReflectionFunction(\Closure::fromCallable(/** @scrutinizer ignore-type */ $value));
Loading history...
28
        }
29
30
        if (!$value instanceof \ReflectionFunction) {
31
            throw new \InvalidArgumentException('$value must be callable.');
32
        }
33
34
        return new self($value);
35
    }
36
37
    /**
38
     * Invoke the callable with the passed arguments. Arguments of type
39
     * Zenstruck\Callback\Parameter are resolved before invoking.
40
     *
41
     * @param mixed ...$arguments
42
     *
43
     * @return mixed
44
     *
45
     * @throws UnresolveableArgument
46
     */
47
    public function invoke(...$arguments)
48
    {
49
        $parameters = $this->function->getParameters();
50
51
        foreach ($arguments as $key => $argument) {
52
            if (!$argument instanceof Parameter) {
53
                continue;
54
            }
55
56
            if (!\array_key_exists($key, $parameters) && !$argument->isOptional()) {
57
                throw new \ArgumentCountError(\sprintf('No argument %d for callable. Expected type: "%s".', $key + 1, $argument->type()));
58
            }
59
60
            if ($argument->isOptional()) {
61
                $arguments[$key] = null;
62
63
                continue;
64
            }
65
66
            try {
67
                $arguments[$key] = $argument->resolve($parameters[$key]);
68
            } catch (UnresolveableArgument $e) {
69
                throw new UnresolveableArgument(\sprintf('Unable to resolve argument %d for callback. Expected type: "%s".', $key + 1, $argument->type()), $e);
70
            }
71
        }
72
73
        return $this->function->invoke(...$arguments);
74
    }
75
76
    /**
77
     * Invoke the callable using the passed Parameter to resolve all callable
78
     * arguments.
79
     *
80
     * @param int $min Enforce a minimum number of arguments the callable must have
81
     *
82
     * @return mixed
83
     *
84
     * @throws UnresolveableArgument
85
     */
86
    public function invokeAll(Parameter $parameter, int $min = 0)
87
    {
88
        $arguments = $this->function->getParameters();
89
90
        if (\count($arguments) < $min) {
91
            throw new \ArgumentCountError("{$min} argument(s) of type \"{$parameter->type()}\" required.");
92
        }
93
94
        foreach ($arguments as $key => $argument) {
95
            try {
96
                $arguments[$key] = $parameter->resolve($argument);
97
            } catch (UnresolveableArgument $e) {
98
                throw new UnresolveableArgument(\sprintf('Unable to resolve argument %d for callback. Expected type: "%s"', $key + 1, $parameter->type()), $e);
99
            }
100
        }
101
102
        return $this->function->invoke(...$arguments);
103
    }
104
}
105