Passed
Push — develop ( 0a189f...1b174b )
by nguereza
15:41 queued 03:57
created

ConstructorResolver::resolveParameter()   B

Complexity

Conditions 10
Paths 20

Size

Total Lines 44
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 20
c 0
b 0
f 0
nc 20
nop 3
dl 0
loc 44
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Platine Container
5
 *
6
 * Platine Container is the implementation of PSR 11
7
 *
8
 * This content is released under the MIT License (MIT)
9
 *
10
 * Copyright (c) 2020 Platine Container
11
 * Copyright (c) 2019 Dion Chaika
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
/**
33
 *  @file ConstructorResolver.php
34
 *
35
 *  This class use constructor to resolve the instance
36
 *
37
 *  @package    Platine\Container
38
 *  @author Platine Developers Team
39
 *  @copyright  Copyright (c) 2020
40
 *  @license    http://opensource.org/licenses/MIT  MIT License
41
 *  @link   https://www.platine-php.com
42
 *  @version 1.0.0
43
 *  @filesource
44
 */
45
46
declare(strict_types=1);
47
48
namespace Platine\Container\Resolver;
49
50
use Platine\Container\ContainerInterface;
51
use Platine\Container\Exception\ContainerException;
52
use Platine\Container\ParameterCollection;
53
use ReflectionClass;
54
use ReflectionException;
55
use ReflectionNamedType;
56
use ReflectionParameter;
57
use ReflectionUnionType;
58
59
/**
60
 * @class ConstructorResolver
61
 * @package Platine\Container\Resolver
62
 */
63
class ConstructorResolver implements ResolverInterface
64
{
65
    /**
66
     * {@inheritdoc}
67
     */
68
    public function resolve(
69
        ContainerInterface $container,
70
        string $type,
71
        ?ParameterCollection $parameters = null
72
    ): mixed {
73
        try {
74
            $class = new ReflectionClass($type);
75
        } catch (ReflectionException $e) {
76
            throw new ContainerException($e->getMessage());
77
        }
78
79
        if (!$class->isInstantiable()) {
80
            throw new ContainerException(sprintf('Type/class [%s] is not instantiable!', $type));
81
        }
82
83
        $constructor = $class->getConstructor();
84
        if ($constructor === null) {
85
            try {
86
                return $class->newInstanceWithoutConstructor();
87
            } catch (ReflectionException $e) {
88
                throw new ContainerException($e->getMessage());
89
            }
90
        }
91
92
        $callback = function (ReflectionParameter $parameter) use ($container, $parameters) {
93
            return $this->resolveParameter(
94
                $container,
95
                $parameter,
96
                $parameters
97
            );
98
        };
99
100
        $arguments = array_map($callback, $constructor->getParameters());
101
        try {
102
            return $class->newInstanceArgs($arguments);
103
        } catch (ReflectionException $e) {
104
            throw new ContainerException($e->getMessage());
105
        }
106
    }
107
108
    /**
109
     * Resolve the parameter
110
     * @param  ContainerInterface       $container
111
     * @param  ReflectionParameter     $parameter           the reflection parameter
112
     * @param  ParameterCollection|null $parameters
113
     * @return mixed
114
     */
115
    protected function resolveParameter(
116
        ContainerInterface $container,
117
        ReflectionParameter $parameter,
118
        ?ParameterCollection $parameters = null
119
    ) {
120
        $class = null;
121
        $types = $this->getTypes($parameter);
122
123
        // TODO: we can have more than one type, so we take only the first one
124
        if (count($types) > 0 && $types[0]->isBuiltin() === false) {
125
            $class = new ReflectionClass($types[0]->getName());
126
        }
127
128
        //If the parameter is not a class
129
        if ($class === null) {
130
            if ($parameters !== null) {
131
                if (
132
                        $parameters->has($parameter->name) &&
133
                        $parameters->get($parameter->name) !== null
134
                ) {
135
                    return $parameters->get($parameter->name)
136
                                       ->getValue($container);
137
                }
138
            }
139
140
            if ($parameter->isDefaultValueAvailable()) {
141
                try {
142
                    return $parameter->getDefaultValue();
143
                } catch (ReflectionException $e) {
144
                    throw new ContainerException($e->getMessage());
145
                }
146
            }
147
148
            if ($parameter->isOptional()) {
149
                // This branch is required to work around PHP bugs where a parameter is optional
150
                // but has no default value available through reflection. Specifically, PDO exhibits
151
                // this behavior.
152
                return null;
153
            }
154
155
            throw new ContainerException(sprintf('Parameter [%s] is not bound!', $parameter->name));
156
        }
157
158
        return $container->get($class->name);
159
    }
160
161
    /**
162
     * Return the types of the given parameter
163
     * @param ReflectionParameter $parameter
164
     * @return ReflectionNamedType[]
165
     */
166
    protected function getTypes(ReflectionParameter $parameter): array
167
    {
168
        if ($parameter->getType() === null) {
169
            return [];
170
        }
171
172
        $type = $parameter->getType();
173
        $types = [];
174
        if ($type instanceof ReflectionNamedType) {
175
            $types = [$type];
176
        }
177
178
        if ($type instanceof ReflectionUnionType) {
179
            /** @var ReflectionNamedType[] $namedTypes */
180
            $namedTypes = $type->getTypes();
181
            $types = $namedTypes;
182
        }
183
184
        return $types;
185
    }
186
}
187