Completed
Push — master ( a91c7b...483fcf )
by Alice
18:08
created

ServiceContainer::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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
	public function __construct()
27
	{
28
		$this->serviceInstances = [];
29
	}
30
31
	/**
32
	 * @param ServiceDefinitionInterface $serviceDefinition
33
	 */
34
	public function addService(ServiceDefinitionInterface $serviceDefinition)
35
	{
36
		$this->services[$serviceDefinition->getServiceName()] = $serviceDefinition;
37
	}
38
39
	/**
40
	 * @param ServiceInstanceInterface $definition
41
	 * @throws DuplicatedServiceException
42
	 */
43
	public function addServiceInstance(ServiceInstanceInterface $definition)
44
	{
45
		if (true === array_key_exists($definition->getServiceName(), $this->serviceInstances)) {
46
			throw new DuplicatedServiceException(
47
				'The service' . $definition->getServiceName() . ' is already registered in the container'
48
			);
49
		}
50
51
		$this->serviceInstances[$definition->getServiceName()] = $definition->getInstance();
52
	}
53
54
	/**
55
	 * @param string $index
56
	 * @param bool $new
57
	 * @return mixed|null
58
	 */
59
	public function get($index, $new = false)
60
	{
61
		// if service is shared true and already exists we return it
62
		if (isset($this->serviceInstances[$index]) && false === $new) {
63
			return $this->serviceInstances[$index];
64
		}
65
66
		// is service not defined return null
67
		if (!isset($this->services[$index])) {
68
			return null;
69
		}
70
71
		// we create a new instance
72
		$instance = $this->create($this->services[$index]);
73
74
		// if shared true we set it in the current instances
75
		if (false === $new) {
76
			$this->serviceInstances[$index] = $instance;
77
		}
78
79
		return $instance;
80
	}
81
82
	/**
83
	 * Returns true if the container can return an entry for the given identifier.
84
	 * Returns false otherwise.
85
	 *
86
	 * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
87
	 * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
88
	 *
89
	 * @param string $index Identifier of the entry to look for.
90
	 *
91
	 * @return bool
92
	 */
93
	public function has($index)
94
	{
95
		// if service is shared true and already exists we return it
96
		if (isset($this->serviceInstances[$index])) {
97
			return true;
98
		}
99
100
		// is service not defined return null
101
		if (isset($this->services[$index])) {
102
			return true;
103
		}
104
105
		return false;
106
	}
107
108
	/**
109
	 * @param ServiceDefinitionInterface $serviceDefinition
110
	 * @return mixed
111
	 */
112
	private function create(ServiceDefinitionInterface $serviceDefinition)
113
	{
114
		// we get the class name in a var for the new later
115
		$newClass = $serviceDefinition->getClass();
116
117
		// we get the construct args
118
		$args = $this->checkArgsServices($serviceDefinition->getConstructArgs());
119
		$instance = new $newClass(...$args);
120
121
		// we call the injection methods after we instance the object
122
		$calls = $this->checkCallsServices($serviceDefinition->getCalls());
123
		foreach ($calls as $call => $params) {
124
			call_user_func_array([$instance, $call], $params);
125
		}
126
127
		// we create the new instance
128
		return $instance;
129
	}
130
131
	/**
132
	 * @param array $args
133
	 * @return array
134
	 */
135
	private function checkArgsServices(array $args)
136
	{
137
		// we check if any of the construct args are services themself and we create them
138
		foreach ($args as $k => $arg) {
139
			if (true === $this->has($arg)) {
140
				$args[$k] = $this->get($arg);
141
			}
142
		}
143
144
		return $args;
145
	}
146
147
	/**
148
	 * @param array $calls
149
	 * @return array
150
	 */
151
	private function checkCallsServices(array $calls)
152
	{
153
		// we check if any of the calls args are services themself and we create them
154
		foreach ($calls as $callArgs) {
155
			foreach ($callArgs as $k => $arg) {
156
				if (true === $this->has($arg)) {
157
					$args[$k] = $this->get($arg);
158
				}
159
			}
160
		}
161
162
		return $calls;
163
	}
164
165
}
166