Passed
Pull Request — master (#240)
by Dmitriy
02:26
created

DefinitionStorage::has()   D

Complexity

Conditions 26
Paths 27

Size

Total Lines 108
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 38
CRAP Score 43.5849

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 26
eloc 56
c 1
b 0
f 0
nc 27
nop 1
dl 0
loc 108
ccs 38
cts 54
cp 0.7037
crap 43.5849
rs 4.1666

How to fix   Long Method    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
declare(strict_types=1);
4
5
namespace Yiisoft\Di;
6
7
use Psr\Container\ContainerInterface;
8
use ReflectionClass;
9
use ReflectionException;
10
use ReflectionNamedType;
11
use ReflectionUnionType;
12
use Yiisoft\Factory\Exception\CircularReferenceException;
13
14
/**
15
 * Stores service definitions and checks if a definition could be instantiated.
16
 * @internal
17
 */
18
final class DefinitionStorage
19
{
20
    private array $definitions;
21
    private array $building = [];
22
    /** @psalm-suppress  PropertyNotSetInConstructor */
23
    private ContainerInterface $delegateContainer;
24
25 97
    public function __construct(array $definitions = [])
26
    {
27 97
        $this->definitions = $definitions;
28 97
    }
29
30 90
    public function setDelegateContainer(ContainerInterface $delegateContainer): void
31
    {
32 90
        $this->delegateContainer = $delegateContainer;
33 90
    }
34
35
    /**
36
     * @param string $id class name, interface name or alias name
37
     *
38
     * @throws CircularReferenceException
39
     */
40 91
    public function has(string $id): bool
41
    {
42 91
        if (isset($this->definitions[$id])) {
43 91
            return true;
44
        }
45
46 57
        if (!class_exists($id)) {
47 14
            return false;
48
        }
49
50 52
        if (isset($this->building[$id])) {
51 4
            throw new CircularReferenceException(sprintf(
52 4
                'Circular reference to "%s" detected while building: %s.',
53
                $id,
54 4
                implode(', ', array_keys($this->building))
55
            ));
56
        }
57
58
        try {
59 52
            $reflectionClass = new ReflectionClass($id);
60
        } catch (ReflectionException $e) {
61
            return false;
62
        }
63
64 52
        if (!$reflectionClass->isInstantiable()) {
65
            return false;
66
        }
67
68 52
        $constructor = $reflectionClass->getConstructor();
69
70 52
        if ($constructor === null) {
71 5
            $this->definitions[$id] = $id;
72 5
            return true;
73
        }
74
75 50
        $isResolvable = true;
76 50
        $this->building[$id] = 1;
77
78 50
        foreach ($constructor->getParameters() as $parameter) {
79 50
            $type = $parameter->getType();
80
81 50
            if ($parameter->isVariadic() || $parameter->isOptional()) {
82 44
                break;
83
            }
84
85
            /**
86
             * @var ReflectionNamedType|ReflectionUnionType|null $type
87
             * @psalm-suppress RedundantConditionGivenDocblockType
88
             * @psalm-suppress UndefinedClass
89
             */
90 20
            if ($type === null || !$type instanceof ReflectionUnionType && $type->isBuiltin()) {
91 4
                $isResolvable = false;
92 4
                break;
93
            }
94
95
            // PHP 8 union type is used as type hint
96
            /** @psalm-suppress UndefinedClass, TypeDoesNotContainType */
97 20
            if ($type instanceof ReflectionUnionType) {
98
                $isUnionTypeResolvable = false;
99
                /** @var ReflectionNamedType $unionType */
100
                foreach ($type->getTypes() as $unionType) {
101
                    if (!$unionType->isBuiltin()) {
102
                        $typeName = $unionType->getName();
103
                        if ($typeName === 'self') {
104
                            continue;
105
                        }
106
                        if ($this->has($typeName)) {
107
                            $isUnionTypeResolvable = true;
108
                            break;
109
                        }
110
                    }
111
                }
112
113
                $isResolvable = $isUnionTypeResolvable;
114
                if (!$isResolvable) {
115
                    break;
116
                }
117
                continue;
118
            }
119
120
            /** @var ReflectionNamedType|null $type */
121
            // Our parameter has a class type hint
122 20
            if ($type !== null && !$type->isBuiltin()) {
123 20
                $typeName = $type->getName();
124
125 20
                if ($typeName === 'self') {
126 1
                    throw new CircularReferenceException(sprintf(
127 1
                        'Circular reference to "%s" detected while building: %s.',
128
                        $id,
129 1
                        implode(', ', array_keys($this->building))
130
                    ));
131
                }
132
133
                /** @psalm-suppress RedundantPropertyInitializationCheck */
134 20
                if (!($this->has($typeName) || (isset($this->delegateContainer) ? $this->delegateContainer->has($typeName): false))) {
135 7
                    $isResolvable = false;
136 7
                    break;
137
                }
138
            }
139
        }
140
141 47
        if ($isResolvable) {
142 44
            $this->definitions[$id] = $id;
143
        }
144
145 47
        unset($this->building[$id]);
146
147 47
        return $isResolvable;
148
    }
149
150
    /**
151
    * @return mixed|object
152
    */
153 91
    public function get(string $id)
154
    {
155 91
        return $this->definitions[$id];
156
    }
157
158
    /**
159
     * @param mixed|object $definition
160
     */
161 97
    public function set(string $id, $definition): void
162
    {
163 97
        $this->definitions[$id] = $definition;
164 97
    }
165
}
166