Proyectos de Subversion Moodle

Rev

| 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\Definition\Helper;
6
 
7
use DI\Definition\Exception\InvalidDefinition;
8
use DI\Definition\ObjectDefinition;
9
use DI\Definition\ObjectDefinition\MethodInjection;
10
use DI\Definition\ObjectDefinition\PropertyInjection;
11
 
12
/**
13
 * Helps defining how to create an instance of a class.
14
 *
15
 * @author Matthieu Napoli <matthieu@mnapoli.fr>
16
 */
17
class CreateDefinitionHelper implements DefinitionHelper
18
{
19
    private const DEFINITION_CLASS = ObjectDefinition::class;
20
 
21
    private ?string $className;
22
 
23
    private ?bool $lazy = null;
24
 
25
    /**
26
     * Array of constructor parameters.
27
     */
28
    protected array $constructor = [];
29
 
30
    /**
31
     * Array of properties and their value.
32
     */
33
    private array $properties = [];
34
 
35
    /**
36
     * Array of methods and their parameters.
37
     */
38
    protected array $methods = [];
39
 
40
    /**
41
     * Helper for defining an object.
42
     *
43
     * @param string|null $className Class name of the object.
44
     *                               If null, the name of the entry (in the container) will be used as class name.
45
     */
46
    public function __construct(string $className = null)
47
    {
48
        $this->className = $className;
49
    }
50
 
51
    /**
52
     * Define the entry as lazy.
53
     *
54
     * A lazy entry is created only when it is used, a proxy is injected instead.
55
     *
56
     * @return $this
57
     */
58
    public function lazy() : self
59
    {
60
        $this->lazy = true;
61
 
62
        return $this;
63
    }
64
 
65
    /**
66
     * Defines the arguments to use to call the constructor.
67
     *
68
     * This method takes a variable number of arguments, example:
69
     *     ->constructor($param1, $param2, $param3)
70
     *
71
     * @param mixed ...$parameters Parameters to use for calling the constructor of the class.
72
     *
73
     * @return $this
74
     */
75
    public function constructor(mixed ...$parameters) : self
76
    {
77
        $this->constructor = $parameters;
78
 
79
        return $this;
80
    }
81
 
82
    /**
83
     * Defines a value to inject in a property of the object.
84
     *
85
     * @param string $property Entry in which to inject the value.
86
     * @param mixed  $value    Value to inject in the property.
87
     *
88
     * @return $this
89
     */
90
    public function property(string $property, mixed $value) : self
91
    {
92
        $this->properties[$property] = $value;
93
 
94
        return $this;
95
    }
96
 
97
    /**
98
     * Defines a method to call and the arguments to use.
99
     *
100
     * This method takes a variable number of arguments after the method name, example:
101
     *
102
     *     ->method('myMethod', $param1, $param2)
103
     *
104
     * Can be used multiple times to declare multiple calls.
105
     *
106
     * @param string $method       Name of the method to call.
107
     * @param mixed ...$parameters Parameters to use for calling the method.
108
     *
109
     * @return $this
110
     */
111
    public function method(string $method, mixed ...$parameters) : self
112
    {
113
        if (! isset($this->methods[$method])) {
114
            $this->methods[$method] = [];
115
        }
116
 
117
        $this->methods[$method][] = $parameters;
118
 
119
        return $this;
120
    }
121
 
122
    public function getDefinition(string $entryName) : ObjectDefinition
123
    {
124
        $class = $this::DEFINITION_CLASS;
125
        /** @var ObjectDefinition $definition */
126
        $definition = new $class($entryName, $this->className);
127
 
128
        if ($this->lazy !== null) {
129
            $definition->setLazy($this->lazy);
130
        }
131
 
132
        if (! empty($this->constructor)) {
133
            $parameters = $this->fixParameters($definition, '__construct', $this->constructor);
134
            $constructorInjection = MethodInjection::constructor($parameters);
135
            $definition->setConstructorInjection($constructorInjection);
136
        }
137
 
138
        if (! empty($this->properties)) {
139
            foreach ($this->properties as $property => $value) {
140
                $definition->addPropertyInjection(
141
                    new PropertyInjection($property, $value)
142
                );
143
            }
144
        }
145
 
146
        if (! empty($this->methods)) {
147
            foreach ($this->methods as $method => $calls) {
148
                foreach ($calls as $parameters) {
149
                    $parameters = $this->fixParameters($definition, $method, $parameters);
150
                    $methodInjection = new MethodInjection($method, $parameters);
151
                    $definition->addMethodInjection($methodInjection);
152
                }
153
            }
154
        }
155
 
156
        return $definition;
157
    }
158
 
159
    /**
160
     * Fixes parameters indexed by the parameter name -> reindex by position.
161
     *
162
     * This is necessary so that merging definitions between sources is possible.
163
     *
164
     * @throws InvalidDefinition
165
     */
166
    private function fixParameters(ObjectDefinition $definition, string $method, array $parameters) : array
167
    {
168
        $fixedParameters = [];
169
 
170
        foreach ($parameters as $index => $parameter) {
171
            // Parameter indexed by the parameter name, we reindex it with its position
172
            if (is_string($index)) {
173
                $callable = [$definition->getClassName(), $method];
174
 
175
                try {
176
                    $reflectionParameter = new \ReflectionParameter($callable, $index);
177
                } catch (\ReflectionException $e) {
178
                    throw InvalidDefinition::create($definition, sprintf("Parameter with name '%s' could not be found. %s.", $index, $e->getMessage()));
179
                }
180
 
181
                $index = $reflectionParameter->getPosition();
182
            }
183
 
184
            $fixedParameters[$index] = $parameter;
185
        }
186
 
187
        return $fixedParameters;
188
    }
189
}