Completed
Branch master (9571d9)
by Alice
02:41
created

ServiceContainer   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 154
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 40
dl 0
loc 154
ccs 46
cts 46
cp 1
rs 10
c 0
b 0
f 0
wmc 21

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A has() 0 13 3
A checkCallsServices() 0 12 4
A checkArgsServices() 0 10 3
A addServiceInstance() 0 11 2
A create() 0 17 2
A addService() 0 5 1
A get() 0 21 5
1
<?php
2
3
namespace Wonderland\Container;
4
5
use Wonderland\Container\Service\ServiceDefinitionInterface;
6
use Wonderland\Container\Exception\DuplicatedServiceException;
7
use Psr\Container\ContainerInterface;
8
use Wonderland\Container\Service\ServiceInstanceInterface;
9
10
/**
11
 * Class ServiceContainer
12
 * @package Wonderland\Container\Container
13
 * @author Alice Praud <[email protected]>
14
 */
15
class ServiceContainer implements ContainerInterface
16
{
17
	/** @var ServiceDefinitionInterface[] */
18
	private $services;
19
20
	/** @var array */
21
	private $serviceInstances;
22
23
	/**
24
	 * ServiceContainer constructor.
25
	 */
26 5
	public function __construct()
27
	{
28 5
		$this->serviceInstances = [];
29 5
	}
30
31
	/**
32
	 * @param ServiceDefinitionInterface $serviceDefinition
33
	 * @return ServiceContainer
34
	 */
35 6
	public function addService(ServiceDefinitionInterface $serviceDefinition)
36
	{
37 6
		$this->services[$serviceDefinition->getServiceName()] = $serviceDefinition;
38
39 6
		return $this;
40
	}
41
42
	/**
43
	 * @param ServiceInstanceInterface $definition
44
	 * @return ServiceContainer
45
	 * @throws DuplicatedServiceException
46
	 */
47 3
	public function addServiceInstance(ServiceInstanceInterface $definition)
48
	{
49 3
		if (true === array_key_exists($definition->getServiceName(), $this->serviceInstances)) {
50 1
			throw new DuplicatedServiceException(
51 1
				'The service' . $definition->getServiceName() . ' is already registered in the container'
52
			);
53
		}
54
55 3
		$this->serviceInstances[$definition->getServiceName()] = $definition->getInstance();
56
57 3
		return $this;
58
	}
59
60
	/**
61
	 * @param string $index
62
	 * @param bool $new
63
	 * @return mixed|null
64
	 */
65 1
	public function get($index, $new = false)
66
	{
67
		// if service is shared true and already exists we return it
68 1
		if (isset($this->serviceInstances[$index]) && false === $new) {
69 1
			return $this->serviceInstances[$index];
70
		}
71
72
		// is service not defined return null
73 1
		if (!isset($this->services[$index])) {
74 1
			return null;
75
		}
76
77
		// we create a new instance
78 1
		$instance = $this->create($this->services[$index]);
79
80
		// if shared true we set it in the current instances
81 1
		if (false === $new) {
82 1
			$this->serviceInstances[$index] = $instance;
83
		}
84
85 1
		return $instance;
86
	}
87
88
	/**
89
	 * Returns true if the container can return an entry for the given identifier.
90
	 * Returns false otherwise.
91
	 *
92
	 * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
93
	 * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
94
	 *
95
	 * @param string $index Identifier of the entry to look for.
96
	 *
97
	 * @return bool
98
	 */
99 2
	public function has($index)
100
	{
101
		// if service is shared true and already exists we return it
102 2
		if (isset($this->serviceInstances[$index])) {
103 1
			return true;
104
		}
105
106
		// is service not defined return null
107 2
		if (isset($this->services[$index])) {
108 1
			return true;
109
		}
110
111 2
		return false;
112
	}
113
114
	/**
115
	 * @param ServiceDefinitionInterface $serviceDefinition
116
	 * @return mixed
117
	 */
118 1
	private function create(ServiceDefinitionInterface $serviceDefinition)
119
	{
120
		// we get the class name in a var for the new later
121 1
		$newClass = $serviceDefinition->getClass();
122
123
		// we get the construct args
124 1
		$args = $this->checkArgsServices($serviceDefinition->getConstructArgs());
125 1
		$instance = new $newClass(...$args);
126
127
		// we call the injection methods after we instance the object
128 1
		$calls = $this->checkCallsServices($serviceDefinition->getCalls());
129 1
		foreach ($calls as $call => $params) {
130 1
			call_user_func_array([$instance, $call], $params);
131
		}
132
133
		// we create the new instance
134 1
		return $instance;
135
	}
136
137
	/**
138
	 * @param array $args
139
	 * @return array
140
	 */
141 1
	private function checkArgsServices(array $args)
142
	{
143
		// we check if any of the construct args are services themself and we create them
144 1
		foreach ($args as $k => $arg) {
145 1
			if (true === $this->has($arg)) {
146 1
				$args[$k] = $this->get($arg);
147
			}
148
		}
149
150 1
		return $args;
151
	}
152
153
	/**
154
	 * @param array $calls
155
	 * @return array
156
	 */
157 1
	private function checkCallsServices(array $calls)
158
	{
159
		// we check if any of the calls args are services themself and we create them
160 1
		foreach ($calls as $callArgs) {
161 1
			foreach ($callArgs as $k => $arg) {
162 1
				if (true === $this->has($arg)) {
163 1
					$args[$k] = $this->get($arg);
164
				}
165
			}
166
		}
167
168 1
		return $calls;
169
	}
170
171
}
172