Passed
Push — master ( 0ab929...f14eca )
by Dmitriy
02:17
created

DefinitionExtractor::fromParameter()   B

Complexity

Conditions 10
Paths 12

Size

Total Lines 52
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 20.7986

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 20
c 1
b 0
f 0
nc 12
nop 1
dl 0
loc 52
ccs 11
cts 21
cp 0.5238
crap 20.7986
rs 7.6666

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\Definitions\Infrastructure;
6
7
use ReflectionClass;
8
use ReflectionException;
9
use ReflectionFunctionAbstract;
10
use ReflectionNamedType;
11
use ReflectionParameter;
12
use ReflectionUnionType;
13
use Yiisoft\Definitions\ClassDefinition;
14
use Yiisoft\Definitions\Contract\DefinitionInterface;
15
use Yiisoft\Definitions\Exception\NotFoundException;
16
use Yiisoft\Definitions\Exception\NotInstantiableClassException;
17
use Yiisoft\Definitions\Exception\NotInstantiableException;
18
use Yiisoft\Definitions\ParameterDefinition;
19
20
/**
21
 * This class resolves dependencies by using class type hints.
22
 * Note that service names need not match the parameter names, parameter names are ignored
23
 *
24
 * @internal
25
 */
26
final class DefinitionExtractor
27
{
28
    private static ?self $instance = null;
29
30
    /**
31
     * @psalm-var array<string, array<string, DefinitionInterface>>
32
     */
33
    private static array $dependencies = [];
34
35 1
    private function __construct()
36
    {
37 1
    }
38
39 33
    public static function getInstance(): self
40
    {
41 33
        if (self::$instance === null) {
42 1
            self::$instance = new self();
43
        }
44
45 33
        return self::$instance;
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::instance could return the type null which is incompatible with the type-hinted return Yiisoft\Definitions\Infr...ure\DefinitionExtractor. Consider adding an additional type-check to rule them out.
Loading history...
46
    }
47
48
    /**
49
     * @psalm-param class-string $class
50
     *
51
     * @throws NotFoundException
52
     * @throws NotInstantiableException
53
     *
54
     * @return DefinitionInterface[]
55
     * @psalm-return array<string, DefinitionInterface>
56
     */
57 29
    public function fromClassName(string $class): array
58
    {
59 29
        if (isset(self::$dependencies[$class])) {
60 17
            return self::$dependencies[$class];
61
        }
62
63
        try {
64 12
            $reflectionClass = new ReflectionClass($class);
65 1
        } catch (ReflectionException $e) {
66 1
            throw new NotFoundException($class);
67
        }
68
69 11
        if (!$reflectionClass->isInstantiable()) {
70 1
            throw new NotInstantiableClassException($class);
71
        }
72
73 10
        $constructor = $reflectionClass->getConstructor();
74 10
        $dependencies = $constructor === null ? [] : $this->fromFunction($constructor);
75 10
        self::$dependencies[$class] = $dependencies;
76
77 10
        return $dependencies;
78
    }
79
80
    /**
81
     * @return DefinitionInterface[]
82
     * @psalm-return array<string, DefinitionInterface>
83
     */
84 14
    public function fromFunction(ReflectionFunctionAbstract $reflectionFunction): array
85
    {
86 14
        $result = [];
87 14
        foreach ($reflectionFunction->getParameters() as $parameter) {
88 14
            $result[$parameter->getName()] = $this->fromParameter($parameter);
89
        }
90 14
        return $result;
91
    }
92
93 14
    private function fromParameter(ReflectionParameter $parameter): DefinitionInterface
94
    {
95 14
        $type = $parameter->getType();
96
97 14
        if ($type === null || $parameter->isVariadic()) {
98 2
            return new ParameterDefinition($parameter);
99
        }
100
101
        // PHP 8 union type is used as type hint
102
        /** @psalm-suppress UndefinedClass, TypeDoesNotContainType */
103 13
        if ($type instanceof ReflectionUnionType) {
104
            $types = [];
105
            /** @var ReflectionNamedType $unionType */
106
            foreach ($type->getTypes() as $unionType) {
107
                if (!$unionType->isBuiltin()) {
108
                    $typeName = $unionType->getName();
109
                    if ($typeName === 'self') {
110
                        // If type name is "self", it means that called class and
111
                        // $parameter->getDeclaringClass() returned instance of `ReflectionClass`.
112
                        /** @psalm-suppress PossiblyNullReference */
113
                        $typeName = $parameter->getDeclaringClass()->getName();
114
                    }
115
116
                    $types[] = $typeName;
117
                }
118
            }
119
120
            if ($types === []) {
121
                return new ParameterDefinition($parameter);
122
            }
123
124
            /** @psalm-suppress MixedArgument */
125
            return new ClassDefinition(implode('|', $types), $type->allowsNull());
126
        }
127
128
        /** @var ReflectionNamedType $type */
129
130
        // Our parameter has a class type hint
131 13
        if (!$type->isBuiltin()) {
132 10
            $typeName = $type->getName();
133 10
            if ($typeName === 'self') {
134
                // If type name is "self", it means that called class and
135
                // $parameter->getDeclaringClass() returned instance of `ReflectionClass`.
136
                /** @psalm-suppress PossiblyNullReference */
137 1
                $typeName = $parameter->getDeclaringClass()->getName();
138
            }
139
140 10
            return new ClassDefinition($typeName, $type->allowsNull());
141
        }
142
143
        // Our parameter does have a built-in type hint
144 4
        return new ParameterDefinition($parameter);
145
    }
146
}
147