Completed
Pull Request — master (#184)
by Daniel
11:22
created

Definition::resolveClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php declare(strict_types=1);
2
3
namespace League\Container\Definition;
4
5
use League\Container\Argument\{
6
    ArgumentResolverInterface, ArgumentResolverTrait, ClassNameInterface, RawArgumentInterface
7
};
8
use League\Container\ContainerAwareTrait;
9
use ReflectionClass;
10
use ReflectionException;
11
use ReflectionFunction;
12
13
class Definition implements ArgumentResolverInterface, DefinitionInterface
14
{
15
    use ArgumentResolverTrait;
16
    use ContainerAwareTrait;
17
18
    /**
19
     * @var string
20
     */
21
    protected $alias;
22
23
    /**
24
     * @var mixed
25
     */
26
    protected $concrete;
27
28
    /**
29
     * @var boolean
30
     */
31
    protected $shared = false;
32
33
    /**
34
     * @var array
35
     */
36
    protected $tags = [];
37
38
    /**
39
     * @var array
40
     */
41
    protected $arguments = [];
42
43
    /**
44
     * @var array
45
     */
46
    protected $methods = [];
47
48
    /**
49
     * @var mixed
50
     */
51
    protected $resolved;
52
53
    /**
54
     * Constructor.
55
     *
56
     * @param string $id
57
     * @param mixed  $concrete
58 69
     */
59
    public function __construct(string $id, $concrete = null)
60 69
    {
61
        $concrete = $concrete ?? $id;
62 69
63 69
        $this->alias    = $id;
64 69
        $this->concrete = $concrete;
65
    }
66
67
    /**
68
     * {@inheritdoc}
69 6
     */
70
    public function addTag(string $tag) : DefinitionInterface
71 6
    {
72
        $this->tags[] = $tag;
73 6
74
        return $this;
75
    }
76
77
    /**
78
     * {@inheritdoc}
79 6
     */
80
    public function hasTag(string $tag) : bool
81 6
    {
82
        return in_array($tag, $this->tags, true);
83
    }
84
85
    /**
86
     * {@inheritdoc}
87 36
     */
88
    public function setAlias(string $id) : DefinitionInterface
89 36
    {
90
        $this->alias = $id;
91 36
92
        return $this;
93
    }
94
95
    /**
96
     * {@inheritdoc}
97 33
     */
98
    public function getAlias() : string
99 33
    {
100
        return $this->alias;
101
    }
102
103
    /**
104
     * {@inheritdoc}
105 39
     */
106
    public function setShared(bool $shared = true) : DefinitionInterface
107 39
    {
108
        $this->shared = $shared;
109 39
110
        return $this;
111
    }
112
113
    /**
114
     * {@inheritdoc}
115 45
     */
116
    public function isShared() : bool
117 45
    {
118
        return $this->shared;
119
    }
120
121
    /**
122
     * {@inheritdoc}
123 6
     */
124
    public function getConcrete()
125 6
    {
126
        return $this->concrete;
127
    }
128
129
    /**
130
     * {@inheritdoc}
131 3
     */
132
    public function setConcrete($concrete) : DefinitionInterface
133 3
    {
134 3
        $this->concrete = $concrete;
135
        $this->resolved = null;
136 3
137
        return $this;
138
    }
139
140
    /**
141
     * {@inheritdoc}
142 15
     */
143
    public function addArgument($arg) : DefinitionInterface
144 15
    {
145
        $this->arguments[] = $arg;
146 15
147
        return $this;
148
    }
149
150
    /**
151
     * {@inheritdoc}
152 3
     */
153
    public function addArguments(array $args) : DefinitionInterface
154 3
    {
155 3
        foreach ($args as $arg) {
156
            $this->addArgument($arg);
157
        }
158 3
159
        return $this;
160
    }
161
162
    /**
163
     * {@inheritdoc}
164 3
     */
165
    public function addMethodCall(string $method, array $args = []) : DefinitionInterface
166 3
    {
167 3
        $this->methods[] = [
168 3
            'method'    => $method,
169
            'arguments' => $args
170
        ];
171 3
172
        return $this;
173
    }
174
175
    /**
176
     * {@inheritdoc}
177 3
     */
178
    public function addMethodCalls(array $methods = []) : DefinitionInterface
179 3
    {
180 3
        foreach ($methods as $method => $args) {
181
            $this->addMethodCall($method, $args);
182
        }
183 3
184
        return $this;
185
    }
186
187
    /**
188
     * {@inheritdoc}
189 45
     */
190
    public function resolve(bool $new = false)
191 45
    {
192
        $concrete = $this->concrete;
193 45
194 9
        if ($this->isShared() && $this->resolved !== null && $new === false) {
195
            return $this->resolved;
196
        }
197 45
198 12
        if (is_callable($concrete)) {
199
            $concrete = $this->resolveCallable($concrete);
200
        }
201 45
202 3
        if ($concrete instanceof RawArgumentInterface) {
203
            $this->resolved = $concrete->getValue();
204 3
205
            return $concrete->getValue();
206
        }
207 42
208 6
        if ($concrete instanceof ClassNameInterface) {
209
            $concrete = $concrete->getValue();
210
        }
211 42
212 33
        if (is_string($concrete) && class_exists($concrete)) {
213
            $concrete = $this->resolveClass($concrete);
214
        }
215 42
216 39
        if (is_object($concrete)) {
217
            $concrete = $this->invokeMethods($concrete);
218
        }
219 42
220
        $this->resolved = $concrete;
221 42
222
        return $concrete;
223
    }
224
225
    /**
226
     * Resolve a callable.
227
     *
228
     * @param callable $concrete
229
     *
230
     * @return mixed
231 12
     */
232
    protected function resolveCallable(callable $concrete)
233 12
    {
234
        $resolved = $this->resolveCallableArguments($concrete);
235 12
236
        return call_user_func_array($concrete, $resolved);
237
    }
238
239
    /**
240
     * Resolve callable arguments.
241
     *
242
     * @param callable $concrete The callable
243
     *
244
     * @return array The resolved arguments
245
     */
246
    protected function resolveCallableArguments(callable $concrete): array
247 33
    {
248
        $resolved = $this->resolveArguments($this->arguments);
249 33
250 33
        if (!empty($resolved)) {
251
            return $resolved;
252 33
        }
253
254
        $reflector = new ReflectionFunction($concrete);
255
        $parameters = $reflector->getParameters();
256
257
        return $this->reflectArguments($reflector, $parameters);
258
    }
259
260
    /**
261
     * Resolve a class.
262 39
     *
263
     * @param string $concrete
264 39
     *
265 3
     * @return object
266
     *
267
     * @throws ReflectionException
268 3
     */
269 3
    protected function resolveClass(string $concrete)
270
    {
271
        $resolved   = $this->resolveArguments($this->arguments);
272 39
        $reflection = new ReflectionClass($concrete);
273
274
        return $reflection->newInstanceArgs($resolved);
275
    }
276
277
    /**
278
     * Invoke methods on resolved instance.
279
     *
280
     * @param object $instance
281
     *
282
     * @return object
283
     */
284
    protected function invokeMethods($instance)
285
    {
286
        foreach ($this->methods as $method) {
287
            $args = $this->resolveArguments($method['arguments']);
288
289
            /** @var callable $callable */
290
            $callable = [$instance, $method['method']];
291
            call_user_func_array($callable, $args);
292
        }
293
294
        return $instance;
295
    }
296
}
297