Completed
Push — 2.0 ( a1ad31...edb14a )
by Andrzej
02:52
created

Resolver   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 294
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 4

Importance

Changes 5
Bugs 4 Features 0
Metric Value
wmc 28
c 5
b 4
f 0
lcom 2
cbo 4
dl 0
loc 294
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A add() 0 4 1
A resolve() 0 15 1
A getUnifiedClass() 0 18 3
A getUnifiedClassParams() 0 14 2
C getUnifiedClassParam() 0 63 12
B getUnifiedClassSetters() 0 40 6
A addParams() 0 4 1
A addSetters() 0 4 1
1
<?php
2
/**
3
 * This file is part of the Stack package.
4
 *
5
 * (c) Andrzej Kostrzewa <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Stack\DI\Resolver;
12
13
use Stack\DI\Exception;
14
use Stack\DI\Injection\LazyInterface;
15
16
/**
17
 * Resolves class creation specifics based on constructor params and setter
18
 * definitions, unified across class defaults, inheritance hierarchies, and
19
 * configuration.
20
 *
21
 * @author Andrzej Kostrzewa <[email protected]>
22
 */
23
class Resolver
24
{
25
    /**
26
     * @var array
27
     */
28
    protected $definition = [];
29
30
    /**
31
     * Constructor params in the form `$params[$class][$name] = $value`.
32
     *
33
     * @var array
34
     */
35
    protected $params = [];
36
37
    /**
38
     * Setter definitions in the form of `$setters[$class][$method] = $value`.
39
     *
40
     * @var array
41
     */
42
    protected $setters = [];
43
44
    /**
45
     * Constructor params and setter definitions, unified across class
46
     * defaults, inheritance hierarchies, and configuration.
47
     *
48
     * @var array
49
     */
50
    protected $unifiedClass = [];
51
52
    /**
53
     * Arbitrary values in the form of `$values[$key] = $value`.
54
     *
55
     * @var array
56
     */
57
    protected $values = [];
58
59
    /**
60
     * A Reflector.
61
     *
62
     * @var Reflector
63
     */
64
    protected $reflector;
65
66
    /**
67
     * @var ParameterResolver
68
     */
69
    protected $parameterResolver;
70
71
    /**
72
     * @var SetterResolver
73
     */
74
    protected $setterResolver;
75
76
    /**
77
     * Resolver constructor.
78
     *
79
     * @param Reflector $reflector
80
     */
81
    public function __construct(Reflector $reflector)
82
    {
83
        $this->reflector = $reflector;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 9 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
84
        $this->parameterResolver = new ParameterResolver();
85
        $this->setterResolver = new SetterResolver();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
86
    }
87
88
    /**
89
     * @param $definition
90
     */
91
    public function add($definition)
92
    {
93
        $this->definition = $definition;
94
    }
95
96
    /**
97
     * Creates and returns a new instance of a class using reflection and
98
     * the configuration parameters, optionally with overrides, invoking Lazy
99
     * values along the way.
100
     *
101
     * @param string $class        The class to instantiate.
102
     * @param array  $mergeParams  An array of override parameters.
103
     * @param array  $mergeSetters An array of override setters.
104
     *
105
     * @return object
106
     */
107
    public function resolve(
108
        $class,
109
        array $mergeParams = [],
110
        array $mergeSetters = []
111
    ) {
112
        list($params, $setters) = $this->getUnifiedClass($class);
113
        $params = $this->parameterResolver->resolve($class, $params, $mergeParams);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 17 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
114
        $setters = $this->setterResolver->resolve($class, $setters, $mergeSetters);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 16 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
115
116
        return (object) [
117
            'reflection' => $this->reflector->getClass($class),
118
            'params'     => $params,
119
            'setters'    => $setters,
120
        ];
121
    }
122
123
    /**
124
     * Returns the unified constructor params and setters for a class.
125
     *
126
     * @param string $class The class name to return values for.
127
     *
128
     * @return array An array with two elements; 0 is the constructor params
129
     *               for the class, and 1 is the setter methods and values for the class.
130
     */
131
    public function getUnifiedClass($class)
132
    {
133
        if (isset($this->unifiedClass[$class])) {
134
            return $this->unifiedClass[$class];
135
        }
136
137
        $unifiedClassElement = [[], []];
138
139
        $parent = get_parent_class($class);
140
        if ($parent) {
141
            $unifiedClassElement = $this->getUnifiedClass($parent);
142
        }
143
144
        $this->unifiedClass[$class][0] = $this->getUnifiedClassParams($class, $unifiedClassElement[0]);
145
        $this->unifiedClass[$class][1] = $this->getUnifiedClassSetters($class, $unifiedClassElement[1]);
146
147
        return $this->unifiedClass[$class];
148
    }
149
150
    /**
151
     * Returns the unified constructor params for a class.
152
     *
153
     * @param string $class  The class name to return values for.
154
     * @param array  $parent The parent unified params.
155
     *
156
     * @return array The unified params.
157
     */
158
    protected function getUnifiedClassParams($class, array $parent)
159
    {
160
        $unifiedParams = [];
161
        $classParams   = $this->reflector->getParameters($class);
162
        foreach ($classParams as $classParam) {
163
            $unifiedParams[$classParam->name] = $this->getUnifiedClassParam(
164
                $classParam,
165
                $class,
166
                $parent
167
            );
168
        }
169
170
        return $unifiedParams;
171
    }
172
173
    /**
174
     * Returns a unified param.
175
     *
176
     * @param \ReflectionParameter $param  A parameter reflection.
177
     * @param string               $class  The class name to return values for.
178
     * @param array                $parent The parent unified params.
179
     *
180
     * @return mixed The unified param value.
181
     */
182
    protected function getUnifiedClassParam(\ReflectionParameter $param, $class, $parent)
183
    {
184
        $name     = $param->getName();
185
        $position = $param->getPosition();
186
187
        if (isset($this->definition[$name])) {
188
            return $this->definition[$name];
189
        }
190
191
        /*
192
         * @param self $self
193
         * @param string $class The class name to return values for.
194
         * @param integer $position The class param position.
195
         * @param string $name The class param name.
196
         *
197
         * @return mixed The unified param value.
198
         */
199
        $explicit = function ($self, $class, $position, $name) {
200
            $explicitPosition = isset($self->params[$class])
201
                && array_key_exists($position, $self->params[$class])
202
                && !$self->params[$class][$position] instanceof UnresolvedParam;
203
204
            if ($explicitPosition) {
205
                return $self->params[$class][$position];
206
            }
207
208
            $explicitNamed = isset($self->params[$class])
209
                && array_key_exists($name, $self->params[$class])
210
                && !$self->params[$class][$name] instanceof UnresolvedParam;
211
212
            if ($explicitNamed) {
213
                return $self->params[$class][$name];
214
            }
215
216
            return false;
217
        };
218
219
        /*
220
         * @param string $name The class name to return values for.
221
         * @param array $parent The parent unified params.
222
         * @param \ReflectionParameter $param A parameter reflection.
223
         *
224
         * @return mixed The unified param value, or UnresolvedParam.
225
         */
226
        $implicitOrDefault = function ($name, $parent, $param) {
227
            $implicitNamed = array_key_exists($name, $parent)
228
                && !$parent[$name] instanceof UnresolvedParam;
229
230
            if ($implicitNamed) {
231
                return $parent[$name];
232
            }
233
            /** @var $param \ReflectionParameter */
234
            if ($param->isDefaultValueAvailable()) {
235
                return $param->getDefaultValue();
236
            }
237
238
            return new UnresolvedParam($name);
239
        };
240
241
        $explicitClass = $explicit($this, $class, $position, $name);
242
243
        return $explicitClass ? $explicitClass : $implicitOrDefault($name, $parent, $param);
244
    }
245
246
    /**
247
     * Returns the unified setters for a class.
248
     * Class-specific setters take precedence over trait-based setters, which
249
     * take precedence over interface-based setters.
250
     *
251
     * @param string $class  The class name to return values for.
252
     * @param array  $parent The parent unified setters.
253
     *
254
     * @return array The unified setters.
255
     */
256
    protected function getUnifiedClassSetters($class, array $parent)
257
    {
258
        $unifiedSetters = $parent;
259
260
        $getFromInterfaces = function ($self, $class, &$unifiedSetters) {
261
            $interfaces = class_implements($class);
262
            foreach ($interfaces as $interface) {
263
                if (isset($self->setters[$interface])) {
264
                    $unifiedSetters = array_merge(
265
                        $self->setters[$interface],
266
                        $unifiedSetters
267
                    );
268
                }
269
            }
270
        };
271
272
        $getFromTraits = function ($self, $class, &$unifiedSetters) {
273
            $traits = $self->reflector->getTraits($class);
274
            foreach ($traits as $trait) {
275
                if (isset($self->setters[$trait])) {
276
                    $unifiedSetters = array_merge(
277
                        $self->setters[$trait],
278
                        $unifiedSetters
279
                    );
280
                }
281
            }
282
        };
283
284
        $getFromInterfaces($this, $class, $unifiedSetters);
285
        $getFromTraits($this, $class, $unifiedSetters);
286
287
        if (isset($this->setters[$class])) {
288
            $unifiedSetters = array_merge(
289
                $unifiedSetters,
290
                $this->setters[$class]
291
            );
292
        }
293
294
        return $unifiedSetters;
295
    }
296
297
    /**
298
     * Add constructor parameters definition.
299
     *
300
     * @param array $params
301
     */
302
    public function addParams($params)
303
    {
304
        $this->params = array_merge($this->params, $params);
305
    }
306
307
    /**
308
     * Add setter parameters definition.
309
     *
310
     * @param array $setters
311
     */
312
    public function addSetters($setters)
313
    {
314
        $this->setters = array_merge($this->setters, $setters);
315
    }
316
}
317