Passed
Pull Request — master (#8)
by Sergei
02:40
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 1
    private function __construct()
31
    {
32 1
    }
33
34 16
    public static function getInstance(): self
35
    {
36 16
        if (self::$instance === null) {
37 1
            self::$instance = new self();
38
        }
39
40 16
        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...
41
    }
42
43
    /**
44
     * @psalm-param class-string $class
45
     *
46
     * @throws NotFoundException
47
     * @throws NotInstantiableException
48
     *
49
     * @return DefinitionInterface[]
50
     * @psalm-return array<string, DefinitionInterface>
51
     */
52 12
    public function fromClassName(string $class): array
53
    {
54
        try {
55 12
            $reflectionClass = new ReflectionClass($class);
56 1
        } catch (ReflectionException $e) {
57 1
            throw new NotFoundException($class);
58
        }
59
60 11
        if (!$reflectionClass->isInstantiable()) {
61 1
            throw new NotInstantiableClassException($class);
62
        }
63
64 10
        $constructor = $reflectionClass->getConstructor();
65 10
        return $constructor === null ? [] : $this->fromFunction($constructor);
66
    }
67
68
    /**
69
     * @return DefinitionInterface[]
70
     * @psalm-return array<string, DefinitionInterface>
71
     */
72 14
    public function fromFunction(ReflectionFunctionAbstract $reflectionFunction): array
73
    {
74 14
        $result = [];
75 14
        foreach ($reflectionFunction->getParameters() as $parameter) {
76 14
            $result[$parameter->getName()] = $this->fromParameter($parameter);
77
        }
78 14
        return $result;
79
    }
80
81 14
    private function fromParameter(ReflectionParameter $parameter): DefinitionInterface
82
    {
83 14
        $type = $parameter->getType();
84
85 14
        if ($type === null || $parameter->isVariadic()) {
86 2
            return new ParameterDefinition($parameter);
87
        }
88
89
        // PHP 8 union type is used as type hint
90
        /** @psalm-suppress UndefinedClass, TypeDoesNotContainType */
91 13
        if ($type instanceof ReflectionUnionType) {
92
            $types = [];
93
            /** @var ReflectionNamedType $unionType */
94
            foreach ($type->getTypes() as $unionType) {
95
                if (!$unionType->isBuiltin()) {
96
                    $typeName = $unionType->getName();
97
                    if ($typeName === 'self') {
98
                        // If type name is "self", it means that called class and
99
                        // $parameter->getDeclaringClass() returned instance of `ReflectionClass`.
100
                        /** @psalm-suppress PossiblyNullReference */
101
                        $typeName = $parameter->getDeclaringClass()->getName();
102
                    }
103
104
                    $types[] = $typeName;
105
                }
106
            }
107
108
            if ($types === []) {
109
                return new ParameterDefinition($parameter);
110
            }
111
112
            /** @psalm-suppress MixedArgument */
113
            return new ClassDefinition(implode('|', $types), $type->allowsNull());
114
        }
115
116
        /** @var ReflectionNamedType $type */
117
118
        // Our parameter has a class type hint
119 13
        if (!$type->isBuiltin()) {
120 10
            $typeName = $type->getName();
121 10
            if ($typeName === 'self') {
122
                // If type name is "self", it means that called class and
123
                // $parameter->getDeclaringClass() returned instance of `ReflectionClass`.
124
                /** @psalm-suppress PossiblyNullReference */
125 1
                $typeName = $parameter->getDeclaringClass()->getName();
126
            }
127
128 10
            return new ClassDefinition($typeName, $type->allowsNull());
129
        }
130
131
        // Our parameter does have a built-in type hint
132 4
        return new ParameterDefinition($parameter);
133
    }
134
}
135