Passed
Push — master ( 7f6a5c...a1085f )
by Herberto
02:24
created

Builder::instantiateDependencies()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 0
cts 8
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 3
crap 6
1
<?php
2
3
namespace Hgraca\MicroDI;
4
5
use Hgraca\Helper\InstanceHelper;
6
use Hgraca\MicroDI\DependencyResolver\DependencyResolverInterface;
7
use Hgraca\MicroDI\Exception\CanNotInstantiateDependenciesException;
8
use Hgraca\MicroDI\Port\ContainerInterface;
9
use InvalidArgumentException;
10
11
final class Builder implements BuilderInterface
12
{
13
    /** @var ContainerInterface */
14
    private $container;
15
16
    /** @var string[] */
17
    private $dependentStack = [];
18
19
    /** @var string[] */
20
    private $argumentNameStack = [];
21
22
    /** @var DependencyResolverInterface */
23
    private $dependencyResolver;
24
25 23
    public function __construct(ContainerInterface $container, DependencyResolverInterface $dependencyResolver)
26
    {
27 23
        $this->container = $container;
28 23
        $this->dependencyResolver = $dependencyResolver;
29 23
    }
30
31 4
    public function getInstance(string $class, array $arguments = [])
32
    {
33 4
        if (!$this->container->hasInstance($class)) {
34 3
            $instance = $this->buildInstance($class, $arguments);
35 3
            $this->container->addInstance($instance);
36
        }
37
38 4
        return $this->container->getInstance($class);
39
    }
40
41 4
    public function buildInstance(string $class, array $arguments = [])
42
    {
43 4
        return InstanceHelper::createInstance(
44
            $class,
45 4
            $this->buildDependencies([$class, '__construct'], $arguments)
46
        );
47
    }
48
49 3
    public function buildFromFactory(string $factoryClass, array $arguments = [])
50
    {
51 3
        if (!is_a($factoryClass, FactoryInterface::class, $allowString = true)) {
52 1
            throw new InvalidArgumentException(
53 1
                "The given factory class $factoryClass must implement " . FactoryInterface::class
54
            );
55
        }
56
57
        /** @var FactoryInterface $factory */
58 2
        $factory = $this->getInstance($factoryClass, $arguments);
59
60 2
        $context = $this->container->hasFactoryContext($factoryClass)
61 1
            ? $this->container->getFactoryContext($factoryClass)
62 2
            : [];
63
64 2
        return $factory->create($context);
65
    }
66
67 3
    public function buildDependencies(array $callable, array $arguments = []): array
68
    {
69 3
        $dependentClass = is_string($callable[0]) ? $callable[0] : get_class($callable[0]);
70 3
        $dependentMethod = $callable[1] ?? '__construct';
71
72 3
        $dependencies = $this->dependencyResolver->resolveDependencies($dependentClass, $dependentMethod);
73
74 3
        return $this->prepareDependencies($dependentClass, $dependencies, $arguments);
75
    }
76
77 3
    private function prepareDependencies(string $dependentClass, array $parameters, array $arguments): array
78
    {
79 3
        $dependencies = [];
80
81 3
        foreach ($parameters as $parameter) {
82 2
            $dependencies[$parameter['name']] = $this->getParameterArgument($dependentClass, $parameter, $arguments);
83
        }
84
85 3
        return $dependencies;
86
    }
87
88 2
    private function getParameterArgument(string $dependentClass, array $parameter, array $arguments)
89
    {
90 2
        $parameterName = $parameter['name'];
91
        switch (true) {
92 2
            case $this->isDependencyInGivenArguments($parameterName, $arguments):
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
93 1
                return $this->getDependencyFromArguments($parameterName, $arguments);
94 2
            case $this->isDependencyClassOrInterface($parameter):
95 2
                return $this->getDependencyRecursively($dependentClass, $parameter);
96 2
            case $this->isDependencyNameInContainer($parameterName):
97 1
                return $this->getDependencyFromParameterInContainer($parameterName);
98
            default:
99 1
                throw new CanNotInstantiateDependenciesException(
100 1
                    "Could not get dependency for class '$dependentClass', parameter '$parameterName'."
101
                );
102
        }
103
    }
104
105 2
    private function isDependencyInGivenArguments(string $parameterName, array $arguments): bool
106
    {
107 2
        return array_key_exists($parameterName, $arguments);
108
    }
109
110 1
    private function getDependencyFromArguments(string $parameterName, array $arguments)
111
    {
112 1
        return $arguments[$parameterName];
113
    }
114
115 2
    private function isDependencyClassOrInterface(array $parameter): bool
116
    {
117 2
        return isset($parameter['class']) &&
118 2
            (class_exists($parameter['class']) || interface_exists($parameter['class']));
119
    }
120
121 2
    private function getDependencyRecursively(
122
        string $dependentClass,
123
        array $parameter
124
    ) {
125 2
        $this->pushDependentClass($dependentClass);
126 2
        $this->pushArgumentName($parameter['name']);
127 2
        $dependency = $this->getInstance($parameter['class']);
128 2
        $this->popDependentClass();
129 2
        $this->popArgumentName();
130
131 2
        return $dependency;
132
    }
133
134 2
    private function isDependencyNameInContainer(string $parameterName): bool
135
    {
136 2
        return $this->container->hasArgument($parameterName);
137
    }
138
139 1
    private function getDependencyFromParameterInContainer(string $parameterName)
140
    {
141 1
        return $this->container->getArgument($parameterName);
142
    }
143
144 2
    private function pushDependentClass(string $dependentClass)
145
    {
146 2
        $this->dependentStack[] = $dependentClass;
147 2
    }
148
149 2
    private function popDependentClass(): string
150
    {
151 2
        return array_pop($this->dependentStack);
152
    }
153
154 2
    private function pushArgumentName(string $argumentName)
155
    {
156 2
        $this->argumentNameStack[] = $argumentName;
157 2
    }
158
159 2
    private function popArgumentName(): string
160
    {
161 2
        return array_pop($this->argumentNameStack);
162
    }
163
}
164