Passed
Push — master ( 4d9c37...7cec18 )
by Dominik
13:53
created

Container::checkType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
crap 1
1
<?php
2
3
/**
4
 * Container.php
5
 *
6
 * @author Dominik Kocuj
7
 * @license https://opensource.org/licenses/MIT The MIT License
8
 * @copyright Copyright (c) 2017-2018 kocuj.pl
9
 * @package kocuj_di
10
 */
11
namespace Kocuj\Di\Container;
12
13
use Kocuj\Di\Service\ServiceFactoryInterface;
14
use Kocuj\Di\Service\ServiceType;
15
use Kocuj\Di\ServiceIdDecorator\ServiceIdDecoratorInterface;
16
17
/**
18
 * Dependency injection container for services
19
 */
20
class Container implements ContainerInterface
21
{
22
23
    /**
24
     * Service identifier decorator
25
     *
26
     * @var ServiceIdDecoratorInterface
27
     */
28
    private $serviceIdDecorator;
29
30
    /**
31
     * Service factory
32
     *
33
     * @var ServiceFactoryInterface
34
     */
35
    private $serviceFactory;
36
37
    /**
38
     * Definitions
39
     *
40
     * @var array
41
     */
42
    private $definitions = [];
43
44
    /**
45
     * Constructor
46
     *
47
     * @param ServiceIdDecoratorInterface $serviceIdDecorator
48
     *            Service identifier decorator
49
     * @param ServiceFactoryInterface $serviceFactory
50
     *            Service factory
51
     */
52 108
    public function __construct(ServiceIdDecoratorInterface $serviceIdDecorator, ServiceFactoryInterface $serviceFactory)
53
    {
54
        // remember arguments
55 108
        $this->serviceIdDecorator = $serviceIdDecorator;
56 108
        $this->serviceFactory = $serviceFactory;
57 108
    }
58
59
    /**
60
     * Cloning container
61
     *
62
     * @return void
63
     */
64 6
    public function __clone()
65
    {
66
        // copy services definitions
67 6
        $oldDefinitions = $this->definitions;
68
        // recreate services
69 6
        $this->definitions = [];
70 6
        foreach ($oldDefinitions as $definition) {
71 6
            $this->add($definition['type'], $definition['clonedata']['id'], $definition['clonedata']['source'], $definition['clonedata']['arguments']);
72
        }
73 6
    }
74
75
    /**
76
     * Add standard or shared service
77
     *
78
     * @param ServiceType $serviceType
79
     *            Service type
80
     * @param string $id
81
     *            Service identifier
82
     * @param string $source
83
     *            Service to create
84
     * @param array $arguments
85
     *            Service arguments to inject into constructor
86
     * @return ContainerInterface This object
87
     * @throws Exception
88
     * @see \Kocuj\Di\Container\ContainerInterface::add()
89
     */
90 96
    public function add(ServiceType $serviceType, string $id, string $source, array $arguments = []): ContainerInterface
91
    {
92
        // decorate service identifier
93 96
        $decoratedId = $this->serviceIdDecorator->decorate($id);
94
        // check if service does not exist already
95 96
        if (isset($this->definitions[$decoratedId])) {
96 6
            throw new Exception(sprintf('Service "%s" already exists', $decoratedId));
97
        }
98
        // set service definition
99 96
        $this->definitions[$decoratedId] = [
100 96
            'service' => $this->serviceFactory->create($this, $serviceType, $decoratedId, $source, $arguments),
101 96
            'type' => $serviceType,
102
            'clonedata' => [
103 96
                'id' => $id,
104 96
                'source' => $source,
105 96
                'arguments' => $arguments
106
            ]
107
        ];
108
        // exit
109 96
        return $this;
110
    }
111
112
    /**
113
     * Add standard service
114
     *
115
     * @param string $id
116
     *            Service identifier
117
     * @param string $source
118
     *            Service to create
119
     * @param array $arguments
120
     *            Service arguments to inject into constructor
121
     * @return ContainerInterface This object
122
     * @see \Kocuj\Di\Container\ContainerInterface::addStandard() @codeCoverageIgnore
123
     */
124
    public function addStandard(string $id, string $source, array $arguments = []): ContainerInterface
125
    {
126
        // exit
127
        return $this->add(new ServiceType(ServiceType::STANDARD), $id, $source, $arguments);
128
    }
129
130
    /**
131
     * Add shared service
132
     *
133
     * @param string $id
134
     *            Service identifier
135
     * @param string $source
136
     *            Service to create
137
     * @param array $arguments
138
     *            Service arguments to inject into constructor
139
     * @return ContainerInterface This object
140
     * @see \Kocuj\Di\Container\ContainerInterface::addShared() @codeCoverageIgnore
141
     */
142
    public function addShared(string $id, string $source, array $arguments = []): ContainerInterface
143
    {
144
        // exit
145
        return $this->add(new ServiceType(ServiceType::SHARED), $id, $source, $arguments);
146
    }
147
148
    /**
149
     * Get service definition
150
     *
151
     * @param string $id
152
     *            Service identifier
153
     * @return array Service definition
154
     * @throws NotFoundException
155
     */
156 84
    private function getServiceDefinition(string $id)
157
    {
158
        // decorate service identifier
159 84
        $decoratedId = $this->serviceIdDecorator->decorate($id);
160
        // check if service exists
161 84
        if (! $this->has($decoratedId)) {
162 6
            throw new NotFoundException(sprintf('Service "%s" does not exist', $decoratedId));
163
        }
164
        // exit
165 78
        return $this->definitions[$decoratedId];
166
    }
167
168
    /**
169
     * Get service
170
     *
171
     * @param string $id
172
     *            Service identifier
173
     * @return object Service object
174
     * @throws NotFoundException
175
     * @see \Psr\Container\ContainerInterface::get()
176
     */
177 84
    public function get($id)
178
    {
179
        // exit
180 84
        return $this->getServiceDefinition($id)['service']->getService();
181
    }
182
183
    /**
184
     * Check service type
185
     *
186
     * @param string $id
187
     *            Service identifier
188
     * @param ServiceType $serviceType
189
     *            Service type
190
     * @return bool This service has selected type (true) or not (false)
191
     * @throws NotFoundException
192
     * @see \Kocuj\Di\Container\ContainerInterface::checkType()
193
     */
194 36
    public function checkType(string $id, ServiceType $serviceType): bool
195
    {
196
        // exit
197 36
        return $this->getServiceDefinition($id)['type']->getValue() === $serviceType->getValue();
198
    }
199
200
    /**
201
     * Get service type - for compatibility with 1.2.0
202
     *
203
     * @param string $id
204
     *            Service identifier
205
     * @return ServiceType Service type
206
     * @throws NotFoundException
207
     * @deprecated
208
     * @see \Kocuj\Di\Container\Container::checkType() @codeCoverageIgnore
209
     */
210
    public function getType(string $id): ServiceType
211
    {
212
        // set information about deprecated method
213
        trigger_error('Method ' . __METHOD__ . ' is deprecated and will be removed in version 2.0.0; please use checkType() method instead', E_USER_NOTICE);
214
        // exit
215
        return $this->getServiceDefinition($id)['type'];
216
    }
217
218
    /**
219
     * Check if service exists
220
     *
221
     * @param string $id
222
     *            Service
223
     * @return bool Service exists (true) or not (false)
224
     * @see \Psr\Container\ContainerInterface::has()
225
     */
226 84
    public function has($id): bool
227
    {
228
        // decorate service identifier
229 84
        $decoratedId = $this->serviceIdDecorator->decorate($id);
230
        // exit
231 84
        return isset($this->definitions[$decoratedId]);
232
    }
233
234
    /**
235
     * Call service by method get*(), where "*" is service identifier written in camel case
236
     *
237
     * @param string $method
238
     *            Method to call
239
     * @param array $arguments
240
     *            Arguments for called method
241
     * @return object Service object
242
     * @throws Exception
243
     */
244 30
    public function __call(string $method, array $arguments)
245
    {
246
        // disallow any arguments
247 30
        if (! empty($arguments)) {
248 6
            throw new Exception('Service must be get without arguments');
249
        }
250
        // check prefix
251 24
        $prefix = substr($method, 0, 3);
252 24
        if ($prefix !== 'get') {
253 6
            trigger_error(sprintf('Call to undefined method %s()', __CLASS__ . '::' . $method), E_USER_ERROR);
254
        }
255
        // get service
256 18
        $service = $this->get(substr($method, 3));
257
        // exit
258 18
        return $service;
259
    }
260
}
261