Passed
Push — master ( 5ff919...5bf459 )
by Gabor
05:53
created

AbstractAdapter   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 225
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 1
dl 0
loc 225
ccs 63
cts 63
cp 1
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
has() 0 1 ?
get() 0 1 ?
A registerService() 0 16 2
A serviceIsInitialized() 0 5 2
C getServiceConfiguration() 0 38 8
A resolveServiceClassName() 0 19 3
A resolveServiceArguments() 0 6 1
A resolveMethodCalls() 0 6 1
A resolveShares() 0 6 1
registerServiceInstance() 0 1 ?
A registerModuleServices() 0 18 3
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 7.1
6
 *
7
 * @copyright 2012 - 2017 Gixx-web (http://www.gixx-web.com)
8
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
9
 *
10
 * @link      http://www.gixx-web.com
11
 */
12
declare(strict_types = 1);
13
14
namespace WebHemi\DependencyInjection\ServiceAdapter;
15
16
use Exception;
17
use RuntimeException;
18
use InvalidArgumentException;
19
use WebHemi\Configuration\ServiceInterface as ConfigurationInterface;
20
use WebHemi\DependencyInjection\ServiceInterface;
21
22
/**
23
 * Class AbstractAdapter
24
 */
25
abstract class AbstractAdapter implements ServiceInterface
26
{
27
    const SERVICE_CLASS = 'class';
28
    const SERVICE_ARGUMENTS = 'arguments';
29
    const SERVICE_METHOD_CALL = 'calls';
30
    const SERVICE_SHARE = 'shared';
31
    const SERVICE_SYNTHETIC = 'synthetic';
32
    const SERVICE_INHERIT = 'inherits';
33
    const SERVICE_INITIALIZED = 'initialized';
34
35
    /** @var ConfigurationInterface */
36
    protected $configuration;
37
    /** @var array */
38
    protected $registeredModules = [];
39
    /** @var array */
40
    protected $serviceLibrary = [];
41
    /** @var array */
42
    protected $serviceConfiguration = [];
43
44
    /**
45
     * AbstractAdapter constructor.
46
     *
47
     * @param ConfigurationInterface $configuration
48
     */
49 18
    public function __construct(ConfigurationInterface $configuration)
50
    {
51 18
        $this->configuration = $configuration->getConfig('dependencies');
52 18
    }
53
54
    /**
55
     * Returns true if the given service is registered.
56
     *
57
     * @param string $identifier
58
     * @return bool
59
     */
60
    abstract public function has(string $identifier) : bool;
61
62
    /**
63
     * Gets a service.
64
     *
65
     * @param string $identifier
66
     * @return object
67
     */
68
    abstract public function get(string $identifier);
69
70
    /**
71
     * Register the service.
72
     *
73
     * @param string $identifier
74
     * @return ServiceInterface
75
     */
76 18
    public function registerService(string $identifier) : ServiceInterface
77
    {
78
        // Check if the service is not initialized yet.
79 18
        if (!$this->serviceIsInitialized($identifier)) {
80
            // overwrite if it was registered earlier.
81 18
            $this->serviceLibrary[$identifier] = [
82 18
                self::SERVICE_INITIALIZED => false,
83 18
                self::SERVICE_ARGUMENTS => $this->resolveServiceArguments($identifier),
84 18
                self::SERVICE_METHOD_CALL => $this->resolveMethodCalls($identifier),
85 18
                self::SERVICE_SHARE => $this->resolveShares($identifier),
86 18
                self::SERVICE_CLASS => $this->resolveServiceClassName($identifier),
87
            ];
88
        }
89
90 18
        return $this;
91
    }
92
93
    /**
94
     * Checks if the service has been already initialized.
95
     *
96
     * @param string $identifier
97
     * @return bool
98
     */
99 18
    protected function serviceIsInitialized(string $identifier) : bool
100
    {
101 18
        return isset($this->serviceLibrary[$identifier])
102 18
            && $this->serviceLibrary[$identifier][self::SERVICE_INITIALIZED];
103
    }
104
105
    /**
106
     * Retrieves configuration for a service.
107
     *
108
     * @param string $identifier
109
     * @return array
110
     */
111 18
    private function getServiceConfiguration(string $identifier) : array
112
    {
113 18
        if (isset($this->serviceLibrary[$identifier])) {
114 8
            return $this->serviceLibrary[$identifier];
115
        }
116
117 18
        $configuration = [];
118
119
        // Get all registered module configurations and merge them together.
120 18
        foreach ($this->registeredModules as $moduleName) {
121 18
            if ($this->configuration->has($moduleName.'/'.$identifier)) {
122 18
                $moduleConfig = $this->configuration->getData($moduleName.'/'.$identifier);
123 18
                $configuration = merge_array_overwrite($configuration, $moduleConfig);
124
            }
125
        }
126
127
        // Resolve inheritance.
128 18
        if (isset($configuration[self::SERVICE_INHERIT])) {
129 8
            $parentConfiguration = $this->getServiceConfiguration($configuration[self::SERVICE_INHERIT]);
130
131 8
            foreach ($configuration as $key => $value) {
132 8
                $parentConfiguration[$key] = $value;
133
            }
134
135
            // If the class name is not explicitly defined but the identifier is a class, the inherited class name
136
            // should be overwritten.
137 8
            if (!isset($configuration[self::SERVICE_CLASS]) && class_exists($identifier)) {
138 8
                $parentConfiguration[self::SERVICE_CLASS] = $identifier;
139
            }
140
141 8
            $configuration = $parentConfiguration;
142 8
            unset($parentConfiguration, $configuration[self::SERVICE_INHERIT]);
143
        }
144
145 18
        $this->serviceConfiguration[$identifier] = $configuration;
146
147 18
        return $configuration;
148
    }
149
150
    /**
151
     * Retrieves real service class name.
152
     *
153
     * @param string $identifier
154
     * @return string
155
     */
156 18
    protected function resolveServiceClassName(string $identifier) : string
157
    {
158 18
        if (isset($this->serviceLibrary[$identifier])) {
159
            // Class is already registered in the library so it must have a resolved class name.
160 6
            $className = $this->serviceLibrary[$identifier][self::SERVICE_CLASS];
161
        } else {
162 18
            $serviceConfiguration = $this->getServiceConfiguration($identifier);
163 18
            $className = $serviceConfiguration[self::SERVICE_CLASS] ?? $identifier;
164
        }
165
166 18
        if (!class_exists($className)) {
167 1
            throw new RuntimeException(
168 1
                sprintf('The resolved class "%s" cannot be found.', $className),
169 1
                1002
170
            );
171
        }
172
173 18
        return $className;
174
    }
175
176
    /**
177
     * Gets argument list and resolves alias references.
178
     *
179
     * @param string $identifier
180
     * @return array
181
     */
182 18
    protected function resolveServiceArguments(string $identifier) : array
183
    {
184 18
        $serviceConfiguration = $this->getServiceConfiguration($identifier);
185
186 18
        return $serviceConfiguration[self::SERVICE_ARGUMENTS] ?? [];
187
    }
188
189
    /**
190
     * Returns the service post-init method calls.
191
     *
192
     * @param string $identifier
193
     * @return array
194
     */
195 18
    protected function resolveMethodCalls(string $identifier) : array
196
    {
197 18
        $serviceConfiguration = $this->getServiceConfiguration($identifier);
198
199 18
        return $serviceConfiguration[self::SERVICE_METHOD_CALL] ?? [];
200
    }
201
202
    /**
203
     * Returns the service share status.
204
     *
205
     * @param string $identifier
206
     * @return bool
207
     */
208 18
    protected function resolveShares(string $identifier) : bool
209
    {
210 18
        $serviceConfiguration = $this->getServiceConfiguration($identifier);
211
212 18
        return $serviceConfiguration[self::SERVICE_SHARE] ?? false;
213
    }
214
215
    /**
216
     * Register the service.
217
     *
218
     * @param string  $identifier
219
     * @param object  $serviceInstance
220
     * @return ServiceInterface
221
     */
222
    abstract public function registerServiceInstance(string $identifier, $serviceInstance) : ServiceInterface;
223
224
    /**
225
     * Register module specific services.
226
     * If a service is already registered in the Global namespace, it will be skipped.
227
     *
228
     * @param string $moduleName
229
     * @return ServiceInterface
230
     */
231 18
    public function registerModuleServices(string $moduleName) : ServiceInterface
232
    {
233 18
        if (!$this->configuration->has($moduleName)) {
234 1
            throw new InvalidArgumentException(
235 1
                sprintf('\'%s\' is not a valid module name', $moduleName),
236 1
                1002
237
            );
238
        }
239
240 18
        $this->registeredModules[] = $moduleName;
241 18
        $services = array_keys($this->configuration->getData($moduleName));
242
243 18
        while (key($services) !== null) {
244 18
            $this->registerService(current($services));
245 18
            next($services);
246
        }
247 18
        return $this;
248
    }
249
}
250