Passed
Push — master ( 539d3e...2172fa )
by Alexander
01:31
created

ArrayBuilder::resolveDependencies()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 4
b 0
f 0
nc 2
nop 2
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 2
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
A ArrayBuilder::getDependencies() 0 7 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Factory\Definitions;
6
7
use Psr\Container\ContainerInterface;
8
use Yiisoft\Factory\Exceptions\NotInstantiableException;
9
use Yiisoft\Factory\Extractors\DefinitionExtractor;
10
11
/**
12
 * Builds object by ArrayDefinition.
13
 */
14
class ArrayBuilder
15
{
16
    private static ?DefinitionExtractor $extractor = null;
17
    private static array $dependencies = [];
18
19 42
    public function build(ContainerInterface $container, ArrayDefinition $definition)
20
    {
21 42
        $class = $definition->getClass();
22 42
        $dependencies = $this->getDependencies($class);
23 39
        $parameters = $definition->getParams();
24
25 39
        if (!empty($parameters)) {
26 10
            $this->validateParameters($parameters);
27
28 8
            foreach ($parameters as $index => $parameter) {
29 8
                if ($parameter instanceof ReferenceInterface || is_array($parameter)) {
30 2
                    $this->injectParameter($dependencies, $index, $parameter);
31
                } else {
32 6
                    $this->injectParameter($dependencies, $index, new ValueDefinition($parameter));
33
                }
34
            }
35
        }
36
37 37
        $resolved = DefinitionResolver::resolveArray($container, $dependencies);
38 35
        $object = new $class(...array_values($resolved));
39 35
        return $this->configure($container, $object, $definition->getConfig());
40
    }
41
42 10
    private function validateParameters(array $parameters): void
43
    {
44 10
        $hasStringParameter = false;
45 10
        $hasIntParameter = false;
46 10
        foreach ($parameters as $index => $parameter) {
47 10
            if (is_string($index)) {
48 6
                $hasStringParameter = true;
49 6
                if ($hasIntParameter) {
50 6
                    break;
51
                }
52
            } else {
53 6
                $hasIntParameter = true;
54 6
                if ($hasStringParameter) {
55 2
                    break;
56
                }
57
            }
58
        }
59 10
        if ($hasIntParameter && $hasStringParameter) {
60 2
            throw new \InvalidArgumentException(
61 2
                'Parameters indexed by name and by position in the same array are not allowed.'
62
            );
63
        }
64 8
    }
65
66 8
    private function injectParameter(array &$dependencies, $index, $parameter): void
67
    {
68 8
        if (is_string($index)) {
69 4
            $dependencies[$index] = $parameter;
70
        } else {
71 4
            reset($dependencies);
72 4
            $dependencyIndex = 0;
73 4
            while (current($dependencies)) {
74 4
                if ($index === $dependencyIndex) {
75 4
                    $dependencies[key($dependencies)] = $parameter;
76 4
                    break;
77
                }
78 2
                next($dependencies);
79 2
                $dependencyIndex++;
80
            }
81
        }
82 8
    }
83
84
    /**
85
     * Returns the dependencies of the specified class.
86
     * @param string $class class name, interface name or alias name
87
     * @return DefinitionInterface[] the dependencies of the specified class.
88
     * @throws NotInstantiableException
89
     * @internal
90
     */
91 42
    private function getDependencies(string $class): array
92
    {
93 42
        if (!isset(self::$dependencies[$class])) {
94 11
            self::$dependencies[$class] = $this->getExtractor()->fromClassName($class);
95
        }
96
97 39
        return self::$dependencies[$class];
98
    }
99
100 11
    private function getExtractor(): DefinitionExtractor
101
    {
102 11
        if (static::$extractor === null) {
0 ignored issues
show
Bug introduced by
Since $extractor is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $extractor to at least protected.
Loading history...
103
            // For now use hard coded extractor.
104 1
            static::$extractor = new DefinitionExtractor();
105
        }
106
107 11
        return static::$extractor;
0 ignored issues
show
Bug Best Practice introduced by
The expression return static::extractor could return the type null which is incompatible with the type-hinted return Yiisoft\Factory\Extractors\DefinitionExtractor. Consider adding an additional type-check to rule them out.
Loading history...
108
    }
109
110
    /**
111
     * Configures an object with the given configuration.
112
     * @param ContainerInterface $container
113
     * @param object $object the object to be configured
114
     * @param iterable $config property values and methods to call
115
     * @return object the object itself
116
     */
117 35
    private function configure(ContainerInterface $container, $object, iterable $config)
118
    {
119 35
        foreach ($config as $action => $arguments) {
120 5
            if (substr($action, -2) === '()') {
121
                // method call
122 5
                $setter = \call_user_func_array([$object, substr($action, 0, -2)], $arguments);
123 5
                if ($setter instanceof $object) {
124 5
                    $object = $setter;
125
                }
126
            } else {
127
                // property
128
                if ($arguments instanceof DefinitionInterface) {
129
                    $arguments = $arguments->resolve($container);
130
                }
131
                $object->$action = $arguments;
132
            }
133
        }
134
135 35
        return $object;
136
    }
137
}
138