Completed
Push — master ( 1a456d...8db859 )
by Tomáš
07:37
created

shouldFactoryBuiltDefinitionBeAutowired()   C

Complexity

Conditions 8
Paths 21

Size

Total Lines 39
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 8.064

Importance

Changes 0
Metric Value
dl 0
loc 39
ccs 18
cts 20
cp 0.9
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 23
nc 21
nop 2
crap 8.064
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Symplify
7
 * Copyright (c) 2016 Tomas Votruba (http://tomasvotruba.cz).
8
 */
9
10
namespace Symplify\DefaultAutowire\DependencyInjection\Definition;
11
12
use ReflectionClass;
13
use ReflectionMethod;
14
use Symfony\Component\DependencyInjection\ContainerBuilder;
15
use Symfony\Component\DependencyInjection\Definition;
16
use Symfony\Component\DependencyInjection\DefinitionDecorator;
17
use Symfony\Component\DependencyInjection\Reference;
18
19
final class DefinitionAnalyzer
20
{
21
    /**
22
     * @var DefinitionValidator
23
     */
24
    private $definitionValidator;
25
26 16
    public function __construct(DefinitionValidator $definitionValidator)
27
    {
28 16
        $this->definitionValidator = $definitionValidator;
29 16
    }
30
31 16
    public function shouldDefinitionBeAutowired(ContainerBuilder $containerBuilder, Definition $definition) : bool
32
    {
33 16
        if (! $this->definitionValidator->validate($definition)) {
34 2
            return false;
35
        }
36
37 16
        $isFactory = $definition->getFactory() !== null;
38
39 16
        if ($isFactory) {
40 12
            return $this->shouldFactoryBuiltDefinitionBeAutowired($containerBuilder, $definition);
41
        }
42
43 6
        return $this->shouldClassDefinitionBeAutowired($definition);
44
    }
45
46
    /**
47
     * @param ContainerBuilder $containerBuilder
48
     * @param Definition $definition
49
     *
50
     * @return bool
51
     */
52 12
    private function shouldFactoryBuiltDefinitionBeAutowired(
53
        ContainerBuilder $containerBuilder,
54
        Definition $definition
55
    ) : bool {
56 12
        $factory = $definition->getFactory();
57
58
        // functions specified as string are not supported
59 12
        if (is_string($factory)) {
60
            return false;
61
        }
62
63 12
        list($class, $method) = $factory;
64 12
        if ($class instanceof Reference) {
65 9
            $factoryClassDefinition = $containerBuilder->findDefinition($class);
66 9
            if ($factoryClassDefinition instanceof DefinitionDecorator) {
67 2
                $factoryClassDefinition = $containerBuilder->findDefinition($factoryClassDefinition->getParent());
68
            }
69 9
            $class = $factoryClassDefinition->getClass();
70 9
            if (strpos($class, '%') !== false) {
71 2
                $class = $containerBuilder->getParameter(str_replace('%', '', $class));
72
            }
73
        }
74
75 12
        $factoryMethodReflection = new ReflectionMethod($class, $method);
76
77 12
        if (! $this->hasMethodArguments($factoryMethodReflection)) {
78 5
            return false;
79
        }
80
81 7
        if ($this->areAllMethodArgumentsRequired($definition, $factoryMethodReflection)) {
82 5
            return false;
83
        }
84
85 4
        if (! $this->haveMissingArgumentsTypehints($definition, $factoryMethodReflection)) {
86
            return false;
87
        }
88
89 4
        return true;
90
    }
91
92 6
    private function shouldClassDefinitionBeAutowired(Definition $definition) : bool
93
    {
94 6
        $classReflection = new ReflectionClass($definition->getClass());
95 6
        if (! $classReflection->hasMethod('__construct')
96 6
            || ! $this->hasMethodArguments($classReflection->getConstructor())
97
        ) {
98 3
            return false;
99
        }
100
101 5
        $constructorReflection = $classReflection->getConstructor();
102 5
        if ($this->areAllMethodArgumentsRequired($definition, $constructorReflection)) {
103 3
            return false;
104
        }
105
106 4
        if (! $this->haveMissingArgumentsTypehints($definition, $constructorReflection)) {
107 2
            return false;
108
        }
109
110 4
        return true;
111
    }
112
113 16
    private function hasMethodArguments(ReflectionMethod $methodReflection) : bool
114
    {
115 16
        return $methodReflection->getNumberOfParameters() !== 0;
116
    }
117
118 10
    private function areAllMethodArgumentsRequired(
119
        Definition $definition,
120
        ReflectionMethod $constructorReflection
121
    ) : bool {
122 10
        $constructorArgumentsCount = count($definition->getArguments());
123 10
        $constructorRequiredArgumentsCount = $constructorReflection->getNumberOfRequiredParameters();
124
125 10
        if ($constructorArgumentsCount === $constructorRequiredArgumentsCount) {
126 6
            return true;
127
        }
128
129 6
        return false;
130
    }
131
132 6
    private function haveMissingArgumentsTypehints(
133
        Definition $definition,
134
        ReflectionMethod $constructorReflection
135
    ) : bool {
136 6
        $arguments = $definition->getArguments();
137 6
        if (! count($arguments)) {
138 3
            return true;
139
        }
140
141 5
        $i = 0;
142 5
        foreach ($constructorReflection->getParameters() as $parameterReflection) {
143 5
            if (! isset($arguments[$i])) {
144 5
                if ($parameterReflection->isDefaultValueAvailable()) {
145 2
                    ++$i;
146 2
                    continue;
147
                }
148
149 3
                if (null !== $parameterReflection->getType()) {
150 3
                    return true;
151
                }
152
            }
153
154 5
            ++$i;
155
        }
156
157 2
        return false;
158
    }
159
}
160