Container   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 229
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 42
c 1
b 0
f 0
dl 0
loc 229
rs 10
ccs 42
cts 42
cp 1
wmc 16

11 Methods

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