Completed
Push — master ( 402feb...3309aa )
by Tom
8s
created

PimpleContainerAdapter::applyInflectors()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 1
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
final class PimpleContainerAdapter implements ContainerAdapter
17
{
18
    /**
19
     * @var Container
20
     */
21
    private $container;
22
23
    /**
24
     * @var Closure
25
     */
26
    private $inflectors = [];
27
28
    /**
29
     * @param Container $container
30
     */
31
    public function setContainer($container)
32
    {
33
        $this->container = $container;
34
    }
35
36
    public function addApplicationConfig(ApplicationConfig $config, $prefix = 'config')
37
    {
38
        Assertion::string($prefix);
39
40
        if (!empty($prefix)) {
41
            $prefix .= $config->getSeparator();
42
        }
43
44
        foreach ($config as $key => $value) {
45
            $this->container[$prefix . $key] = $value;
46
        }
47
    }
48
49
    public function addServiceConfig(ServiceConfig $config)
50
    {
51
        foreach ($config as $definition) {
52
            $this->addServiceToContainer($definition);
53
        }
54
    }
55
56
    public function addInflectorConfig(InflectorConfig $config)
57
    {
58
        foreach ($config as $definition) {
59
            $this->inflectors[$definition->getInterface()] = $this->createInflector($definition);
60
        }
61
    }
62
63
    private function addServiceToContainer(ServiceDefinition $definition)
64
    {
65
        $factory = $this->createFactory($definition);
66
67
        if (!$definition->isSingleton()) {
68
            $factory = $this->container->factory($factory);
69
        }
70
71
        $this->container[$definition->getName()] = $factory;
72
    }
73
74
    /**
75
     * @param ServiceDefinition $definition
76
     *
77
     * @return Closure
78
     */
79
    private function createFactory(ServiceDefinition $definition)
80
    {
81
        if ($definition->isFactory()) {
82
            return $this->applyInflectors($this->createFactoryFactory($definition));
83
        }
84
85
        if ($definition->isAlias()) {
86
            return $this->createAliasFactory($definition);
87
        }
88
89
        return $this->applyInflectors($this->createInstanceFactory($definition));
90
    }
91
92
    /**
93
     * @param ServiceDefinition $definition
94
     *
95
     * @return Closure
96
     */
97 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...
98
    {
99
        return function () use ($definition) {
100
            $className = $definition->getClass();
101
            $factory   = new $className();
102
103
            return $factory(...$this->resolveArguments($definition->getArguments()));
104
        };
105
    }
106
107
    /**
108
     * @param ServiceDefinition $definition
109
     *
110
     * @return Closure
111
     */
112
    private function createAliasFactory(ServiceDefinition $definition)
113
    {
114
        return function () use ($definition) {
115
            return $this->container[$definition->getClass()];
116
        };
117
    }
118
119
    /**
120
     * @param ServiceDefinition $definition
121
     *
122
     * @return Closure
123
     */
124
    private function createInstanceFactory(ServiceDefinition $definition)
125
    {
126
        return function () use ($definition) {
127
            $className = $definition->getClass();
128
            $instance  = new $className(...$this->resolveArguments($definition->getArguments()));
129
130
            foreach ($definition->getMethods() as $name => $args) {
131
                $instance->$name(...$this->resolveArguments($args));
132
            }
133
134
            return $instance;
135
        };
136
    }
137
138
    /**
139
     * @param InflectorDefinition $definition
140
     *
141
     * @return Closure
142
     */
143
    private function createInflector(InflectorDefinition $definition)
144
    {
145
        return function ($subject) use ($definition) {
146
            foreach ($definition->getMethods() as $method => $arguments) {
147
                $subject->$method(...$this->resolveArguments($arguments));
148
            }
149
        };
150
    }
151
152
    /**
153
     * @param Closure $factory
154
     *
155
     * @return Closure
156
     */
157
    private function applyInflectors(Closure $factory)
158
    {
159
        return function () use ($factory) {
160
            $instance = $factory();
161
162
            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...
163
                if ($instance instanceof $interface) {
164
                    $inflector($instance);
165
                }
166
            }
167
168
            return $instance;
169
        };
170
    }
171
172
    /**
173
     * @param array $arguments
174
     *
175
     * @return array
176
     */
177
    private function resolveArguments(array $arguments)
178
    {
179
        return array_map(
180
            function ($argument) {
181
                if (!is_string($argument)) {
182
                    return $argument;
183
                }
184
185
                if ($argument === Configurator::container()) {
186
                    return $this->container;
187
                }
188
189
                if (isset($this->container[$argument])) {
190
                    return $this->container[$argument];
191
                }
192
193
                return $argument;
194
            },
195
            $arguments
196
        );
197
    }
198
}
199