Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php declare(strict_types=1);
2
 
3
namespace Invoker;
4
 
5
use Invoker\Exception\NotCallableException;
6
use Invoker\Exception\NotEnoughParametersException;
7
use Invoker\ParameterResolver\AssociativeArrayResolver;
8
use Invoker\ParameterResolver\DefaultValueResolver;
9
use Invoker\ParameterResolver\NumericArrayResolver;
10
use Invoker\ParameterResolver\ParameterResolver;
11
use Invoker\ParameterResolver\ResolverChain;
12
use Invoker\Reflection\CallableReflection;
13
use Psr\Container\ContainerInterface;
14
use ReflectionParameter;
15
 
16
/**
17
 * Invoke a callable.
18
 */
19
class Invoker implements InvokerInterface
20
{
21
    /** @var CallableResolver|null */
22
    private $callableResolver;
23
 
24
    /** @var ParameterResolver */
25
    private $parameterResolver;
26
 
27
    /** @var ContainerInterface|null */
28
    private $container;
29
 
30
    public function __construct(?ParameterResolver $parameterResolver = null, ?ContainerInterface $container = null)
31
    {
32
        $this->parameterResolver = $parameterResolver ?: $this->createParameterResolver();
33
        $this->container = $container;
34
 
35
        if ($container) {
36
            $this->callableResolver = new CallableResolver($container);
37
        }
38
    }
39
 
40
    /**
41
     * {@inheritdoc}
42
     */
43
    public function call($callable, array $parameters = [])
44
    {
45
        if ($this->callableResolver) {
46
            $callable = $this->callableResolver->resolve($callable);
47
        }
48
 
49
        if (! is_callable($callable)) {
50
            throw new NotCallableException(sprintf(
51
                '%s is not a callable',
52
                is_object($callable) ? 'Instance of ' . get_class($callable) : var_export($callable, true)
53
            ));
54
        }
55
 
56
        $callableReflection = CallableReflection::create($callable);
57
 
58
        $args = $this->parameterResolver->getParameters($callableReflection, $parameters, []);
59
 
60
        // Sort by array key because call_user_func_array ignores numeric keys
61
        ksort($args);
62
 
63
        // Check all parameters are resolved
64
        $diff = array_diff_key($callableReflection->getParameters(), $args);
65
        $parameter = reset($diff);
66
        if ($parameter && \assert($parameter instanceof ReflectionParameter) && ! $parameter->isVariadic()) {
67
            throw new NotEnoughParametersException(sprintf(
68
                'Unable to invoke the callable because no value was given for parameter %d ($%s)',
69
                $parameter->getPosition() + 1,
70
                $parameter->name
71
            ));
72
        }
73
 
74
        return call_user_func_array($callable, $args);
75
    }
76
 
77
    /**
78
     * Create the default parameter resolver.
79
     */
80
    private function createParameterResolver(): ParameterResolver
81
    {
82
        return new ResolverChain([
83
            new NumericArrayResolver,
84
            new AssociativeArrayResolver,
85
            new DefaultValueResolver,
86
        ]);
87
    }
88
 
89
    /**
90
     * @return ParameterResolver By default it's a ResolverChain
91
     */
92
    public function getParameterResolver(): ParameterResolver
93
    {
94
        return $this->parameterResolver;
95
    }
96
 
97
    public function getContainer(): ?ContainerInterface
98
    {
99
        return $this->container;
100
    }
101
 
102
    /**
103
     * @return CallableResolver|null Returns null if no container was given in the constructor.
104
     */
105
    public function getCallableResolver(): ?CallableResolver
106
    {
107
        return $this->callableResolver;
108
    }
109
}