Completed
Push — master ( 2e915c...fdb3ab )
by Andrey
02:15
created

DependencyResolver::resolveParameterBag()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4.074

Importance

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