Completed
Push — master ( 7f33bf...9b09f6 )
by Adam
04:00
created

ContainerFactory::getWorkingDirectory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
4
namespace Genesis\Config;
5
6
7
use Genesis\ContainerFactoryException;
8
use Genesis\Exception;
9
use Genesis\NotSupportedException;
10
use Nette\Neon\Decoder;
11
12
13
/**
14
 * @author Adam Bisek <[email protected]>
15
 */
16
class ContainerFactory
17
{
18
19
	private $configs;
20
21
	/** @var Container[] */
22
	private $containersToMerge;
23
24
	private $workingDirectory;
25
26
27 18
	public function addConfig($file)
28
	{
29 18
		$this->configs[] = $file;
30 18
	}
31
32
33 1
	public function getConfigs()
34
	{
35 1
		return $this->configs;
36
	}
37
38
39 13
	public function addContainerToMerge(Container $container)
40
	{
41 13
		$this->containersToMerge[] = $container;
42 13
	}
43
44
45 1
	public function getContainersToMerge()
46
	{
47 1
		return $this->containersToMerge;
48
	}
49
50
51 1
	public function getWorkingDirectory()
52
	{
53 1
		return $this->workingDirectory;
54
	}
55
56
57 19
	public function setWorkingDirectory($workingDirectory)
58
	{
59 19
		$this->workingDirectory = $workingDirectory;
60 19
	}
61
62
63 18
	public function create()
64
	{
65 18
		if (!class_exists('Nette\Neon\Decoder', TRUE)) {
66
			throw new Exception("Neon is not loaded.");
67
		}
68 18
		if (empty($this->configs)) {
69 1
			throw new ContainerFactoryException("No config added.");
70
		}
71
		$config = [
72
			'parameters' => [
73 17
				'workingDirectory' => $this->workingDirectory,
74
			]
75 17
		];
76 17
		if ($this->containersToMerge !== NULL) {
77 12
			foreach ($this->containersToMerge as $containerToMerge) {
78 12
				foreach ($containerToMerge->getParameters() as $k => $v) {
79 12
					$config['parameters'][$k] = $v;
80 12
				}
81 12
				foreach ($containerToMerge->getServices() as $k => $v) {
82 11
					$config['services'][$k] = $v;
83 12
				}
84 12
			}
85 12
		}
86 17
		$configs = $this->resolveFiles($this->configs);
87 15
		$config = $this->readConfigs($configs, $config);
88 15
		$config = $this->parseValues($config);
89
90
		// BC break check
91 15
		$mainSections = ['includes', 'class', 'parameters', 'services'];
92 15
		foreach($config as $key => $val){
93 15
			if(!in_array($key, $mainSections)) {
94 1
				throw new NotSupportedException("Since version 2.0 are supported main only these sections: " . implode(", ", $mainSections) . ". Section '$key' found. Move your variables into parameters section.");
95
			}
96 15
		}
97
98 14
		$container = new Container();
99 14
		$container->setClass($config['class']);
100 14
		if(isset($config['parameters'])){
101 14
			$container->setParameters($config['parameters']);
102 14
		}
103 14
		if(isset($config['services'])){
104 13
			foreach($config['services'] as $name => $config){
105 13
				if(!is_array($config)){
106 1
					$container->addService($name, $config); // is directly service object from merged container
107 1
					continue;
108
				}
109 13
				$class = $config['class'];
110 13
				$arguments = [];
111 13
				if($config['class'] instanceof \Nette\Neon\Entity){
112 13
					$class = $config['class']->value;
113 13
					$arguments = $config['class']->attributes;
114 13
				}
115 13
				$reflectionClass = new \ReflectionClass($class);
116 13
				$service = $reflectionClass->newInstanceArgs($arguments);
117 13
				if(isset($config['setup'])){
118 4
					foreach($config['setup'] as $neonEntity){
119 4
						if(!method_exists($service, $neonEntity->value)){
120 1
							throw new ContainerFactoryException("Class $class does not have method $neonEntity->value().");
121
						}
122 3
						call_user_func_array(array($service, $neonEntity->value), $neonEntity->attributes);
123 3
					}
124 3
				}
125 12
				$container->addService($name, $service);
126 12
			}
127 12
		}
128
129 13
		return $container;
130
	}
131
132
133 17
	private function resolveFiles(array $files)
134
	{
135 17
		$return = [];
136 17
		foreach ($files as $file) {
137 17
			$array = $this->readFile($file);
138 15
			if ($array !== NULL) {
139 15
				if (isset($array['includes'])) {
140 2
					foreach ($array['includes'] as $include) {
141 2
						$return[] = dirname($file) . DIRECTORY_SEPARATOR . $include;
142 2
					}
143 2
				}
144 15
				$return[] = $file;
145 15
			}
146 15
		}
147 15
		return $return;
148
	}
149
150
151 15
	private function readConfigs($files, $config)
152
	{
153 15
		foreach ($files as $file) {
154 15
			$array = $this->readFile($file);
155 15
			if ($array !== NULL) {
156 15
				$config = array_replace_recursive($config, $array);
157 15
			}
158 15
		}
159 15
		return $config;
160
	}
161
162
163 17
	private function readFile($file)
164
	{
165 17
		$neonDecoder = new Decoder;
166 17
		if (!is_file($file)) {
167 1
			throw new ContainerFactoryException("Config file '$file' not found.");
168
		}
169 16
		if (!is_readable($file)) {
170
			throw new ContainerFactoryException("Config file '$file' not readable.");
171
		}
172 16
		return $neonDecoder->decode(file_get_contents($file));
173
	}
174
175
176 15
	private function parseValues($config, & $allConfig = [], $keysPath = [])
177
	{
178 15
		$config = $this->resolveUnmergables($config);
179 15
		foreach ($config as $key => $value) {
180 15
			if($value instanceof \Nette\Neon\Entity){
181 13
				$value->value = $this->parseValue($value->value, $allConfig);
182 13
				foreach($value->attributes as $k => $v){
183 13
					if(is_array($v)){
184 9
						$value->attributes[$k] = $this->parseValues($v, $allConfig, array_merge($keysPath, [$key]));
185 9
					} else{
186 4
						$value->attributes[$k] = $this->parseValue($v, $allConfig);
187
					}
188 13
				}
189 15
			} elseif (is_array($value)) {
190 15
				$value = $this->parseValues($value, $allConfig, array_merge($keysPath, [$key]));
191 15
			} elseif(!is_object($value)) {
192 15
				$value = $this->parseValue($value, $allConfig);
193 15
			}
194
195
			// get new key name, and replace it
196 15
			$newKey = $this->parseValue($key, $allConfig);
197 15
			unset($config[$key]);
198 15
			$config[$newKey] = $value;
199
200
			// write to global config
201 15
			$v = & $allConfig;
202 15
			foreach ($keysPath as $kp) {
203 15
				$v = & $v[$kp];
204 15
			}
205 15
			if (!($value instanceof \Nette\Neon\Entity)) {
206 15
				$v[$newKey] = $value;
207 15
			}
208 15
		}
209 15
		return $config;
210
	}
211
212
213 15
	private function parseValue($value, $config)
214
	{
215 15
		if (preg_match_all('#%([^%]+)%#', $value, $matches)) {
216 3
			foreach ($matches[1] as $match) {
217 3
				$parameter = $config['parameters'];
218 3
				foreach(explode(".", $match) as $m){
219 3
					if (!array_key_exists($m, $parameter)) {
220
						throw new ContainerFactoryException("Cannot find variable '$match', part '$m'.");
221
					}
222 3
					$parameter = $parameter[$m];
223 3
				}
224 3
				if(is_array($parameter)){
225
					if("%$match%" !== $value){ // if is variable value an array, must not be part of a string
226
						throw new ContainerFactoryException("Array value cannot be part of a string.");
227
					}
228
					return $parameter;
229
				}
230 3
				$value = str_replace("%$match%", $parameter, $value);
231 3
			}
232 3
		}
233 15
		return $value;
234
	}
235
236
237 15
	private function resolveUnmergables($config)
238
	{
239 15
		foreach ($config as $key => $value) {
240 15
			if (preg_match('#!$#', $key)) {
241 2
				$newKey = substr($key, 0, strlen($key) - 1);
242 2
				$config[$newKey] = $value;
243 2
				unset($config[$key]);
244 2
			}
245 15
		}
246 15
		return $config;
247
	}
248
249
}