Completed
Pull Request — master (#67)
by Eric
06:48
created

CallbackResolver::toClosure()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.2
c 0
b 0
f 0
cc 4
eloc 6
nc 3
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Jarvis\Skill\Core;
6
7
use Jarvis\Jarvis;
8
use Jarvis\Skill\DependencyInjection\Reference;
9
10
/**
11
 * @author Eric Chau <[email protected]>
12
 */
13
class CallbackResolver
14
{
15
    /**
16
     * @var Jarvis
17
     */
18
    private $app;
19
20
    public function __construct(Jarvis $app)
21
    {
22
        $this->app = $app;
23
    }
24
25
    /**
26
     * Resolves and replaces placeholders references by the parameter from Jarvis's
27
     * dependency injection container.
28
     *
29
     * @param  mixed $callback
30
     * @return \Closure
31
     */
32
    public function toClosure($callback): \Closure
33
    {
34
        if ($callback instanceof \Closure) {
35
            return $callback;
36
        }
37
38
        if (is_array($callback) && $callback[0] instanceof Reference) {
39
            $callback[0] = $this->app[(string) $callback[0]] ?? $callback[0];
40
        }
41
42
        return \Closure::fromCallable($callback);
43
    }
44
45
    /**
46
     * Resolves and returns an array of arguments according to the given closure.
47
     * This method can also smartly type hint and find the right object to match
48
     * callback requested arguments.
49
     *
50
     * @param  \Closure $callback
51
     * @param  array    $rawArgs
52
     * @return array
53
     */
54
    public function resolveArgumentsForClosure(\Closure $callback, array $rawArgs = []): array
55
    {
56
        $result = [];
57
        $refMethod = new \ReflectionMethod($callback, '__invoke');
58
        foreach ($refMethod->getParameters() as $refParam) {
59
            if (null !== $refClass = $refParam->getClass()) {
60
                if (isset($this->app[$refClass->name])) {
61
                    $result[$refParam->getPosition()] = $this->app[$refClass->name];
62
63
                    continue;
64
                }
65
            }
66
67
            if (in_array($refParam->name, array_keys($rawArgs))) {
68
                $result[$refParam->getPosition()] = $rawArgs[$refParam->name];
69
            }
70
        }
71
72
        return $result;
73
    }
74
75
    /**
76
     * Shortcut that calls successively ::toClosure(), ::resolveArgumentsForClosure(),
77
     * call_user_func_array() and returns the result.
78
     *
79
     * @param  mixed $callback
80
     * @param  array  $rawArgs
81
     * @return mixed
82
     */
83
    public function resolveAndCall($callback, array $rawArgs = [])
84
    {
85
        $closure = $this->toClosure($callback);
86
87
        return call_user_func_array(
88
            $closure,
89
            $this->resolveArgumentsForClosure($closure, $rawArgs)
90
        );
91
    }
92
}
93