Passed
Push — master ( 125477...635d48 )
by Gabor
07:46
created

ServiceAdapter::has()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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