Completed
Push — master ( 24ff25...c0ee40 )
by Andrey
02:00
created

DependencyResolver::resolveVariadic()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 2
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Borodulin\Container\Autowire;
6
7
use Borodulin\Container\ContainerException;
8
use Borodulin\Container\NotFoundException;
9
use Psr\Container\ContainerInterface;
10
11
class DependencyResolver
12
{
13
    /**
14
     * @var AutowireItemProvider
15
     */
16
    private $itemProvider;
17
18 12
    public function __construct(AutowireItemProvider $itemProvider)
19
    {
20 12
        $this->itemProvider = $itemProvider;
21 12
    }
22
23 9
    public function resolveId($id): object
24
    {
25 9
        if ($this->itemProvider->isResolved($id)) {
26 1
            return $this->itemProvider->getResolved($id);
27
        }
28 9
        $this->itemProvider->tryResolve($id);
29 9
        if (class_exists($id)) {
30 8
            $reflection = new \ReflectionClass($id);
31 8
            if (!$reflection->isInstantiable()) {
32 1
                throw new ContainerException("$id is not instantiable.");
33
            }
34 7
            $args = null;
35 7
            $constructor = $reflection->getConstructor();
36 7
            if (null !== $constructor) {
37 7
                $args = $this->resolveParameters($constructor->getParameters());
38
            }
39 5
            $instance = $args ? $reflection->newInstanceArgs($args) : $reflection->newInstance();
40 5
            $this->itemProvider->setResolved($id, $instance);
41
42 5
            return $instance;
43
        } else {
44 1
            throw new NotFoundException($id);
45
        }
46
    }
47
48 2
    public function resolveCallableArgs(callable $callable): array
49
    {
50 2
        $reflection = new \ReflectionFunction(\Closure::fromCallable($callable));
51
52 2
        return $this->resolveParameters($reflection->getParameters());
53
    }
54
55
    /**
56
     * @param \ReflectionParameter[] $parameters
57
     *
58
     * @throws ContainerException
59
     * @throws \ReflectionException
60
     */
61 8
    private function resolveParameters(array $parameters): array
62
    {
63 8
        $args = [];
64 8
        $parameterBag = $this->itemProvider->getContainer()->getParameterBag();
65 8
        foreach ($parameters as $parameter) {
66 8
            if (null !== $parameterBag) {
67 2
                if ($this->resolveParameterBag($args, $parameterBag, $parameter)) {
68 2
                    continue;
69
                }
70
            }
71 6
            if ($parameter->isVariadic()) {
72 2
                $this->resolveVariadic($args, $parameter);
73 5
            } elseif ($parameter->isDefaultValueAvailable()) {
74 1
                $args[] = $parameter->getDefaultValue();
75 4
            } elseif ($parameter->getClass()) {
76 3
                $args[] = $this->resolveClass($parameter->getClass()->getName());
77
            } else {
78 1
                throw new ContainerException(sprintf('Unable to autowire parameter %s', $parameter->getName()));
79
            }
80
        }
81
82 6
        return $args;
83
    }
84
85 1
    private function findAllImplementors(string $class): array
86
    {
87 1
        $result = [];
88 1
        foreach ($this->itemProvider->getIds() as $id) {
89 1
            if (class_exists($id)) {
90 1
                $reflection = new \ReflectionClass($id);
91 1
                if ($reflection->implementsInterface($class) || $reflection->isSubclassOf($class)) {
92 1
                    $result[] = $this->resolveId($id);
93
                }
94
            }
95
        }
96
97 1
        return $result;
98
    }
99
100 2
    private function resolveParameterBag(array &$args, ContainerInterface $parameterBag, \ReflectionParameter $parameter): bool
101
    {
102 2
        if ($parameter->getType() && $parameter->getType()->isBuiltin()) {
103 2
            if ($parameterBag->has($parameter->getName())) {
104 2
                $args[] = $parameterBag->get($parameter->getName());
105
106 2
                return true;
107
            }
108
        }
109
110
        return false;
111
    }
112
113 2
    private function resolveVariadic(array &$args, \ReflectionParameter $parameter): void
114
    {
115 2
        if ($parameter->getClass()) {
116 1
            foreach ($this->findAllImplementors($parameter->getClass()->getName()) as $implementor) {
117 1
                $args[] = $implementor;
118
            }
119
        } else {
120 1
            $args[] = null;
121
        }
122 2
    }
123
124 3
    private function resolveClass(string $className): object
125
    {
126 3
        if ($this->itemProvider->getContainer()->has($className)) {
127 3
            return $this->itemProvider->getContainer()->get($className);
128
        } else {
129 1
            return $this->resolveId($className);
130
        }
131
    }
132
}
133