Completed
Pull Request — master (#72)
by Tom
02:56
created

PimpleContainerAdapter::createInflector()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 10
Code Lines 4

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 4
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
                // @todo - method does not exist
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
148
149
                $subject->$method(...$arguments);
150
            }
151
        };
152
    }
153
154
    /**
155
     * @param Closure $factory
156
     *
157
     * @return Closure
158
     */
159
    private function applyInflectors(Closure $factory)
160
    {
161
        return function () use ($factory) {
162
            $instance = $factory();
163
164
            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...
165
                if ($instance instanceof $interface) {
166
                    $inflector($instance);
167
                }
168
            }
169
170
            return $instance;
171
        };
172
    }
173
174
    /**
175
     * @param array $arguments
176
     *
177
     * @return array
178
     */
179
    private function resolveArguments(array $arguments)
180
    {
181
        return array_map(
182
            function ($argument) {
183
                if (!is_string($argument)) {
184
                    return $argument;
185
                }
186
187
                if ($argument === Configurator::container()) {
188
                    return $this->container;
189
                }
190
191
                if (isset($this->container[$argument])) {
192
                    return $this->container[$argument];
193
                }
194
195
                return $argument;
196
            },
197
            $arguments
198
        );
199
    }
200
}
201