Passed
Push — master ( 2172fa...eaad1b )
by Alexander
07:07
created

ArrayBuilder::configure()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5.675

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 10
nc 5
nop 3
dl 0
loc 19
ccs 7
cts 10
cp 0.7
crap 5.675
rs 9.6111
c 1
b 0
f 0
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
                $this->injectParameter($dependencies, $index, DefinitionResolver::ensureResolvable($parameter));
30
            }
31
        }
32
33 37
        $resolved = DefinitionResolver::resolveArray($container, $dependencies);
34 35
        $object = new $class(...array_values($resolved));
35 35
        return $this->configure($container, $object, $definition->getConfig());
36
    }
37
38 10
    private function validateParameters(array $parameters): void
39
    {
40 10
        $hasStringParameter = false;
41 10
        $hasIntParameter = false;
42 10
        foreach ($parameters as $index => $parameter) {
43 10
            if (is_string($index)) {
44 6
                $hasStringParameter = true;
45 6
                if ($hasIntParameter) {
46 6
                    break;
47
                }
48
            } else {
49 6
                $hasIntParameter = true;
50 6
                if ($hasStringParameter) {
51 2
                    break;
52
                }
53
            }
54
        }
55 10
        if ($hasIntParameter && $hasStringParameter) {
56 2
            throw new \InvalidArgumentException(
57 2
                'Parameters indexed by name and by position in the same array are not allowed.'
58
            );
59
        }
60 8
    }
61
62 8
    private function injectParameter(array &$dependencies, $index, $parameter): void
63
    {
64 8
        if (is_string($index)) {
65 4
            $dependencies[$index] = $parameter;
66
        } else {
67 4
            reset($dependencies);
68 4
            $dependencyIndex = 0;
69 4
            while (current($dependencies)) {
70 4
                if ($index === $dependencyIndex) {
71 4
                    $dependencies[key($dependencies)] = $parameter;
72 4
                    break;
73
                }
74 2
                next($dependencies);
75 2
                $dependencyIndex++;
76
            }
77
        }
78 8
    }
79
80
    /**
81
     * Returns the dependencies of the specified class.
82
     * @param string $class class name, interface name or alias name
83
     * @return DefinitionInterface[] the dependencies of the specified class.
84
     * @throws NotInstantiableException
85
     * @internal
86
     */
87 42
    private function getDependencies(string $class): array
88
    {
89 42
        if (!isset(self::$dependencies[$class])) {
90 10
            self::$dependencies[$class] = $this->getExtractor()->fromClassName($class);
91
        }
92
93 39
        return self::$dependencies[$class];
94
    }
95
96 10
    private function getExtractor(): DefinitionExtractor
97
    {
98 10
        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...
99
            // For now use hard coded extractor.
100 1
            static::$extractor = new DefinitionExtractor();
101
        }
102
103 10
        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...
104
    }
105
106
    /**
107
     * Configures an object with the given configuration.
108
     * @param ContainerInterface $container
109
     * @param object $object the object to be configured
110
     * @param iterable $config property values and methods to call
111
     * @return object the object itself
112
     */
113 35
    private function configure(ContainerInterface $container, $object, iterable $config)
114
    {
115 35
        foreach ($config as $action => $arguments) {
116 5
            if (substr($action, -2) === '()') {
117
                // method call
118 5
                $setter = \call_user_func_array([$object, substr($action, 0, -2)], $arguments);
119 5
                if ($setter instanceof $object) {
120 5
                    $object = $setter;
121
                }
122
            } else {
123
                // property
124
                if ($arguments instanceof DefinitionInterface) {
125
                    $arguments = $arguments->resolve($container);
126
                }
127
                $object->$action = $arguments;
128
            }
129
        }
130
131 35
        return $object;
132
    }
133
}
134