Passed
Push — master ( e14a05...ed0db1 )
by Gabor
05:13
created

ServiceAdapter::get()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 8
nop 1
dl 0
loc 17
ccs 9
cts 9
cp 1
crap 5
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 7.1
6
 *
7
 * @copyright 2012 - 2018 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\Base;
15
16
use ReflectionClass;
17
use ReflectionException;
18
use InvalidArgumentException;
19
use RuntimeException;
20
use WebHemi\Configuration\ServiceInterface as ConfigurationInterface;
21
use WebHemi\DependencyInjection\ServiceInterface;
22
use WebHemi\DependencyInjection\ServiceAdapter\AbstractAdapter;
23
24
/**
25
 * Class ServiceAdapter.
26
 */
27
class ServiceAdapter extends AbstractAdapter
28
{
29
    /**
30
     * @var array
31
     */
32
    private $container = [];
33
34
    /**
35
     * ServiceAdapter constructor.
36
     *
37
     * @param ConfigurationInterface $configuration
38
     */
39 5
    public function __construct(ConfigurationInterface $configuration)
40
    {
41 5
        parent::__construct($configuration);
42 5
    }
43
44
    /**
45
     * Returns true if the given service is registered.
46
     *
47
     * @param  string $identifier
48
     * @return bool
49
     */
50 1
    public function has(string $identifier) : bool
51
    {
52 1
        return isset($this->container[$identifier])
53 1
            || isset($this->serviceLibrary[$identifier])
54 1
            || class_exists($identifier);
55
    }
56
57
    /**
58
     * Gets a service.
59
     *
60
     * @param  string $identifier
61
     * @throws RuntimeException
62
     * @throws ReflectionException
63
     * @return object
64
     */
65 4
    public function get(string $identifier)
66
    {
67
        // Not registered but valid class name, so register it
68 4
        if (!isset($this->serviceLibrary[$identifier]) && class_exists($identifier)) {
69 2
            $this->registerService($identifier);
70
        }
71
72
        // The service is registered in the library but not in the container, so register it into the container too.
73 4
        if (!isset($this->container[$identifier])) {
74 4
            $this->registerServiceToContainer($identifier);
75
        }
76
77 3
        $service = $this->serviceLibrary[$identifier][self::SERVICE_SHARE]
78 1
            ? $this->container[$identifier]
79 3
            : clone $this->container[$identifier];
80
81 3
        return $service;
82
    }
83
84
    /**
85
     * Registers the service into the container AKA create the instance.
86
     *
87
     * @param  string $identifier
88
     * @throws ReflectionException
89
     * @return ServiceAdapter
90
     */
91 4
    private function registerServiceToContainer(string $identifier) : ServiceAdapter
92
    {
93
        // At this point the service must be in the library
94 4
        if (!isset($this->serviceLibrary[$identifier])) {
95 1
            throw new InvalidArgumentException(
96 1
                sprintf('Invalid service name: %s, service is not in the library.', $identifier),
97 1
                1000
98
            );
99
        }
100
101
        // Check arguments.
102
        $argumentList = $this
103 4
            ->setArgumentListReferences($this->serviceLibrary[$identifier][self::SERVICE_ARGUMENTS]);
104
105
        // Create new instance.
106 4
        $className = $this->serviceLibrary[$identifier][self::SERVICE_CLASS];
107 4
        $reflectionClass = new ReflectionClass($className);
108 4
        $serviceInstance = $reflectionClass->newInstanceArgs($argumentList);
109
110
        // Perform post init method calls.
111 3
        foreach ($this->serviceLibrary[$identifier][self::SERVICE_METHOD_CALL] as $methodCallList) {
112 1
            $method = $methodCallList[0];
113 1
            $argumentList = $this->setArgumentListReferences($methodCallList[1] ?? []);
114
115 1
            call_user_func_array([$serviceInstance, $method], $argumentList);
116
        }
117
118
        // Register sevice.
119 3
        $this->container[$identifier] = $serviceInstance;
120
121
        // Mark as initialized.
122 3
        $this->serviceLibrary[$identifier][self::SERVICE_INITIALIZED] = true;
123
124 3
        return $this;
125
    }
126
127
    /**
128
     * Tries to identify referce services in the argument list.
129
     *
130
     * @param  array $argumentList
131
     * @throws ReflectionException
132
     * @return array
133
     */
134 4
    private function setArgumentListReferences(array $argumentList) : array
135
    {
136 4
        foreach ($argumentList as $key => &$value) {
137
            // Associative array keys marks literal values
138 3
            if (!is_numeric($key)) {
139 3
                continue;
140
            }
141
142 2
            $value = $this->get($value);
143
        }
144
145 4
        return $argumentList;
146
    }
147
148
    /**
149
     * Register the service object instance.
150
     *
151
     * @param  string $identifier
152
     * @param  object $serviceInstance
153
     * @return ServiceInterface
154
     */
155 3
    public function registerServiceInstance(string $identifier, $serviceInstance) : ServiceInterface
156
    {
157
        // Check if the service is not initialized yet.
158 3
        if (!$this->serviceIsInitialized($identifier)) {
159 3
            $instanceType = gettype($serviceInstance);
160
161
            // Register synthetic services
162 3
            if ('object' !== $instanceType) {
163 1
                throw new InvalidArgumentException(
164 1
                    sprintf('The second parameter must be an object instance, %s given.', $instanceType),
165 1
                    1001
166
                );
167
            }
168
169
            // Register sevice.
170 2
            $this->container[$identifier] = $serviceInstance;
171
172
            // Overwrite any previous settings.
173 2
            $this->serviceLibrary[$identifier] = [
174 2
                self::SERVICE_INITIALIZED => true,
175 2
                self::SERVICE_ARGUMENTS => [],
176 2
                self::SERVICE_METHOD_CALL => [],
177 2
                self::SERVICE_SHARE => true,
178 2
                self::SERVICE_CLASS => get_class($serviceInstance),
179
            ];
180
        }
181
182 2
        return $this;
183
    }
184
}
185