Passed
Push — master ( c3251a...5f288c )
by Gabor
03:06
created

AbstractAdapter   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 250
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 1
dl 0
loc 250
ccs 67
cts 67
cp 1
rs 10
c 0
b 0
f 0

14 Methods

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