Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
declare(strict_types=1);
4
 
5
namespace DI;
6
 
7
use DI\Compiler\RequestedEntryHolder;
8
use DI\Definition\Definition;
9
use DI\Definition\Exception\InvalidDefinition;
10
use DI\Invoker\FactoryParameterResolver;
11
use Invoker\Exception\NotCallableException;
12
use Invoker\Exception\NotEnoughParametersException;
13
use Invoker\Invoker;
14
use Invoker\InvokerInterface;
15
use Invoker\ParameterResolver\AssociativeArrayResolver;
16
use Invoker\ParameterResolver\DefaultValueResolver;
17
use Invoker\ParameterResolver\NumericArrayResolver;
18
use Invoker\ParameterResolver\ResolverChain;
19
 
20
/**
21
 * Compiled version of the dependency injection container.
22
 *
23
 * @author Matthieu Napoli <matthieu@mnapoli.fr>
24
 */
25
abstract class CompiledContainer extends Container
26
{
27
    /**
28
     * This const is overridden in child classes (compiled containers).
29
     * @var array
30
     */
31
    protected const METHOD_MAPPING = [];
32
 
33
    private ?InvokerInterface $factoryInvoker = null;
34
 
35
    public function get(string $id) : mixed
36
    {
37
        // Try to find the entry in the singleton map
38
        if (isset($this->resolvedEntries[$id]) || array_key_exists($id, $this->resolvedEntries)) {
39
            return $this->resolvedEntries[$id];
40
        }
41
 
42
        /** @psalm-suppress UndefinedConstant */
43
        $method = static::METHOD_MAPPING[$id] ?? null;
44
 
45
        // If it's a compiled entry, then there is a method in this class
46
        if ($method !== null) {
47
            // Check if we are already getting this entry -> circular dependency
48
            if (isset($this->entriesBeingResolved[$id])) {
1441 ariadna 49
                $idList = implode(" -> ", [...array_keys($this->entriesBeingResolved), $id]);
50
                throw new DependencyException("Circular dependency detected while trying to resolve entry '$id': Dependencies: " . $idList);
1 efrain 51
            }
52
            $this->entriesBeingResolved[$id] = true;
53
 
54
            try {
55
                $value = $this->$method();
56
            } finally {
57
                unset($this->entriesBeingResolved[$id]);
58
            }
59
 
60
            // Store the entry to always return it without recomputing it
61
            $this->resolvedEntries[$id] = $value;
62
 
63
            return $value;
64
        }
65
 
66
        return parent::get($id);
67
    }
68
 
69
    public function has(string $id) : bool
70
    {
71
        // The parent method is overridden to check in our array, it avoids resolving definitions
72
        /** @psalm-suppress UndefinedConstant */
73
        if (isset(static::METHOD_MAPPING[$id])) {
74
            return true;
75
        }
76
 
77
        return parent::has($id);
78
    }
79
 
80
    protected function setDefinition(string $name, Definition $definition) : void
81
    {
82
        // It needs to be forbidden because that would mean get() must go through the definitions
83
        // every time, which kinds of defeats the performance gains of the compiled container
84
        throw new \LogicException('You cannot set a definition at runtime on a compiled container. You can either put your definitions in a file, disable compilation or ->set() a raw value directly (PHP object, string, int, ...) instead of a PHP-DI definition.');
85
    }
86
 
87
    /**
88
     * Invoke the given callable.
89
     */
90
    protected function resolveFactory($callable, $entryName, array $extraParameters = []) : mixed
91
    {
92
        // Initialize the factory resolver
93
        if (! $this->factoryInvoker) {
94
            $parameterResolver = new ResolverChain([
95
                new AssociativeArrayResolver,
96
                new FactoryParameterResolver($this->delegateContainer),
97
                new NumericArrayResolver,
98
                new DefaultValueResolver,
99
            ]);
100
 
101
            $this->factoryInvoker = new Invoker($parameterResolver, $this->delegateContainer);
102
        }
103
 
104
        $parameters = [$this->delegateContainer, new RequestedEntryHolder($entryName)];
105
 
106
        $parameters = array_merge($parameters, $extraParameters);
107
 
108
        try {
109
            return $this->factoryInvoker->call($callable, $parameters);
110
        } catch (NotCallableException $e) {
111
            throw new InvalidDefinition("Entry \"$entryName\" cannot be resolved: factory " . $e->getMessage());
112
        } catch (NotEnoughParametersException $e) {
113
            throw new InvalidDefinition("Entry \"$entryName\" cannot be resolved: " . $e->getMessage());
114
        }
115
    }
116
}