PimpleContainerAdapter::addServiceToContainer()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace TomPHP\ContainerConfigurator\Pimple;
4
5
use Assert\Assertion;
6
use Closure;
7
use Pimple\Container;
8
use TomPHP\ContainerConfigurator\ApplicationConfig;
9
use TomPHP\ContainerConfigurator\Configurator;
10
use TomPHP\ContainerConfigurator\ContainerAdapter;
11
use TomPHP\ContainerConfigurator\InflectorConfig;
12
use TomPHP\ContainerConfigurator\InflectorDefinition;
13
use TomPHP\ContainerConfigurator\ServiceConfig;
14
use TomPHP\ContainerConfigurator\ServiceDefinition;
15
16
/**
17
 * @internal
18
 */
19
final class PimpleContainerAdapter implements ContainerAdapter
20
{
21
    /**
22
     * @var Container
23
     */
24
    private $container;
25
26
    /**
27
     * @var Closure
28
     */
29
    private $inflectors = [];
30
31
    /**
32
     * @param Container $container
33
     */
34
    public function setContainer($container)
35
    {
36
        $this->container = $container;
37
    }
38
39
    public function addApplicationConfig(ApplicationConfig $config, $prefix = 'config')
40
    {
41
        Assertion::string($prefix);
42
43
        if (!empty($prefix)) {
44
            $prefix .= $config->getSeparator();
45
        }
46
47
        foreach ($config as $key => $value) {
48
            $this->container[$prefix . $key] = $value;
49
        }
50
    }
51
52
    public function addServiceConfig(ServiceConfig $config)
53
    {
54
        foreach ($config as $definition) {
55
            $this->addServiceToContainer($definition);
56
        }
57
    }
58
59
    public function addInflectorConfig(InflectorConfig $config)
60
    {
61
        foreach ($config as $definition) {
62
            $this->inflectors[$definition->getInterface()] = $this->createInflector($definition);
63
        }
64
    }
65
66
    private function addServiceToContainer(ServiceDefinition $definition)
67
    {
68
        $factory = $this->createFactory($definition);
69
70
        if (!$definition->isSingleton()) {
71
            $factory = $this->container->factory($factory);
72
        }
73
74
        $this->container[$definition->getName()] = $factory;
75
    }
76
77
    /**
78
     * @param ServiceDefinition $definition
79
     *
80
     * @return Closure
81
     */
82
    private function createFactory(ServiceDefinition $definition)
83
    {
84
        if ($definition->isFactory()) {
85
            return $this->applyInflectors($this->createFactoryFactory($definition));
86
        }
87
88
        if ($definition->isAlias()) {
89
            return $this->createAliasFactory($definition);
90
        }
91
92
        return $this->applyInflectors($this->createInstanceFactory($definition));
93
    }
94
95
    /**
96
     * @param ServiceDefinition $definition
97
     *
98
     * @return Closure
99
     */
100 View Code Duplication
    private function createFactoryFactory(ServiceDefinition $definition)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
101
    {
102
        return function () use ($definition) {
103
            $className = $definition->getClass();
104
            $factory   = new $className();
105
106
            return $factory(...$this->resolveArguments($definition->getArguments()));
107
        };
108
    }
109
110
    /**
111
     * @param ServiceDefinition $definition
112
     *
113
     * @return Closure
114
     */
115
    private function createAliasFactory(ServiceDefinition $definition)
116
    {
117
        return function () use ($definition) {
118
            return $this->container[$definition->getClass()];
119
        };
120
    }
121
122
    /**
123
     * @param ServiceDefinition $definition
124
     *
125
     * @return Closure
126
     */
127
    private function createInstanceFactory(ServiceDefinition $definition)
128
    {
129
        return function () use ($definition) {
130
            $className = $definition->getClass();
131
            $instance  = new $className(...$this->resolveArguments($definition->getArguments()));
132
133
            foreach ($definition->getMethods() as $name => $args) {
134
                $instance->$name(...$this->resolveArguments($args));
135
            }
136
137
            return $instance;
138
        };
139
    }
140
141
    /**
142
     * @param InflectorDefinition $definition
143
     *
144
     * @return Closure
145
     */
146
    private function createInflector(InflectorDefinition $definition)
147
    {
148
        return function ($subject) use ($definition) {
149
            foreach ($definition->getMethods() as $method => $arguments) {
150
                $subject->$method(...$this->resolveArguments($arguments));
151
            }
152
        };
153
    }
154
155
    /**
156
     * @param Closure $factory
157
     *
158
     * @return Closure
159
     */
160
    private function applyInflectors(Closure $factory)
161
    {
162
        return function () use ($factory) {
163
            $instance = $factory();
164
165
            foreach ($this->inflectors as $interface => $inflector) {
0 ignored issues
show
Bug introduced by
The expression $this->inflectors of type object<Closure> is not traversable.
Loading history...
166
                if ($instance instanceof $interface) {
167
                    $inflector($instance);
168
                }
169
            }
170
171
            return $instance;
172
        };
173
    }
174
175
    /**
176
     * @param array $arguments
177
     *
178
     * @return array
179
     */
180
    private function resolveArguments(array $arguments)
181
    {
182
        return array_map(
183
            function ($argument) {
184
                if (!is_string($argument)) {
185
                    return $argument;
186
                }
187
188
                if ($argument === Configurator::container()) {
189
                    return $this->container;
190
                }
191
192
                if (isset($this->container[$argument])) {
193
                    return $this->container[$argument];
194
                }
195
196
                return $argument;
197
            },
198
            $arguments
199
        );
200
    }
201
}
202