Completed
Push — master ( 44b412...e96d03 )
by Andrey
02:04
created

DependencyResolver::resolveId()   A

Complexity

Conditions 6
Paths 7

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 6

Importance

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