Passed
Pull Request — master (#54)
by Jesús
02:44
created

resolveDependenciesRecursively()   B

Complexity

Conditions 10
Paths 12

Size

Total Lines 50
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 10.4096

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 10
eloc 24
c 2
b 0
f 0
nc 12
nop 1
dl 0
loc 50
ccs 21
cts 25
cp 0.84
crap 10.4096
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
declare(strict_types=1);
4
5
namespace Gacela\Framework\ClassResolver\DependencyResolver;
6
7
use Gacela\Framework\Config\GacelaFileConfig\GacelaConfigFile;
8
use ReflectionClass;
9
use ReflectionNamedType;
10
use ReflectionParameter;
11
use RuntimeException;
12
use function is_callable;
13
14
final class DependencyResolver
15
{
16
    private GacelaConfigFile $gacelaConfigFile;
17
18 12
    public function __construct(GacelaConfigFile $gacelaConfigFile)
19
    {
20 12
        $this->gacelaConfigFile = $gacelaConfigFile;
21 12
    }
22
23
    /**
24
     * @param class-string $resolvedClassName
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
25
     *
26
     * @return list<mixed>
27
     */
28 12
    public function resolveDependencies(string $resolvedClassName): array
29
    {
30 12
        $reflection = new ReflectionClass($resolvedClassName);
31 12
        $constructor = $reflection->getConstructor();
32 12
        if (!$constructor) {
33 11
            return [];
1 ignored issue
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type Gacela\Framework\ClassRe...DependencyResolver\list.
Loading history...
34
        }
35
36
        /** @var list<mixed> $dependencies */
37 1
        $dependencies = [];
38 1
        foreach ($constructor->getParameters() as $parameter) {
39 1
            $paramType = $parameter->getType();
40 1
            if ($paramType) {
41
                /** @psalm-suppress MixedAssignment */
42 1
                $dependencies[] = $this->resolveDependenciesRecursively($parameter);
43
            }
44
        }
45
46 1
        return $dependencies;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $dependencies returns the type Gacela\Framework\ClassRe...DependencyResolver\list which is incompatible with the type-hinted return array.
Loading history...
47
    }
48
49
    /**
50
     * @return mixed
51
     */
52 1
    private function resolveDependenciesRecursively(ReflectionParameter $parameter)
53
    {
54 1
        if (!$parameter->hasType()) {
55
            throw new RuntimeException("No parameter type for '{$parameter->getName()}'");
56
        }
57
58
        /** @var ReflectionNamedType $paramType */
59 1
        $paramType = $parameter->getType();
60 1
        $paramTypeName = $paramType->getName();
61 1
        if (!class_exists($paramTypeName) && !interface_exists($paramTypeName)) {
62
            return $parameter->getDefaultValue();
63
        }
64
65 1
        $reflection = new ReflectionClass($paramTypeName);
66
67
        // If it's an interface we need to figure out which concrete class do we want to use.
68 1
        if ($reflection->isInterface()) {
69 1
            $mappingInterfaces = $this->gacelaConfigFile->getMappingInterfaces();
70 1
            $concreteClass = $mappingInterfaces[$reflection->getName()] ?? '';
71
            // a callable will be a way to bypass the instantiation and instead
72
            // use the result from the callable that was defined in the gacela config file.
73 1
            if (is_callable($concreteClass)) {
74
                return $concreteClass();
75
            }
76
            // an Exception will be thrown if there is no concrete class found for the interface.
77 1
            if (empty($concreteClass)) {
78
                throw new DependencyResolverNotFoundException($reflection->getName());
79
            }
80
            // finally, override the $reflection (the interface) by the concrete resolved class.
81
            /** @var class-string $concreteClass */
82 1
            $reflection = new ReflectionClass($concreteClass);
83
        }
84
85 1
        $constructor = $reflection->getConstructor();
86 1
        if (!$constructor) {
87 1
            return $reflection->newInstance();
88
        }
89
90
        /** @var list<mixed> $innerDependencies */
91 1
        $innerDependencies = [];
92
93 1
        foreach ($constructor->getParameters() as $constructorParameter) {
94 1
            $paramType = $constructorParameter->getType();
95 1
            if ($paramType) {
96
                /** @psalm-suppress MixedAssignment */
97 1
                $innerDependencies[] = $this->resolveDependenciesRecursively($constructorParameter);
98
            }
99
        }
100
101 1
        return $reflection->newInstanceArgs($innerDependencies);
1 ignored issue
show
Bug introduced by
$innerDependencies of type Gacela\Framework\ClassRe...DependencyResolver\list is incompatible with the type array expected by parameter $args of ReflectionClass::newInstanceArgs(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

101
        return $reflection->newInstanceArgs(/** @scrutinizer ignore-type */ $innerDependencies);
Loading history...
102
    }
103
}
104