1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Magium\Configuration; |
4
|
|
|
|
5
|
|
|
use Magium\Configuration\Config\BuilderFactory; |
6
|
|
|
use Magium\Configuration\Config\BuilderFactoryInterface; |
7
|
|
|
use Magium\Configuration\Config\BuilderInterface; |
8
|
|
|
use Magium\Configuration\Config\Context; |
9
|
|
|
use Magium\Configuration\Config\MissingConfigurationException; |
10
|
|
|
use Magium\Configuration\Config\Repository\ConfigurationRepository; |
11
|
|
|
use Magium\Configuration\File\Context\AbstractContextConfigurationFile; |
12
|
|
|
use Magium\Configuration\Manager\CacheFactory; |
13
|
|
|
use Magium\Configuration\Manager\Manager; |
14
|
|
|
use Magium\Configuration\Manager\ManagerInterface; |
15
|
|
|
|
16
|
|
|
class MagiumConfigurationFactory implements MagiumConfigurationFactoryInterface |
17
|
|
|
{ |
18
|
|
|
|
19
|
|
|
protected $file; |
20
|
|
|
protected $xml; |
21
|
|
|
|
22
|
|
|
protected $manager; |
23
|
|
|
protected $builder; |
24
|
|
|
protected $baseDir; |
25
|
|
|
protected $contextFile; |
26
|
|
|
protected $builderFactory; |
27
|
|
|
protected $context = ConfigurationRepository::CONTEXT_DEFAULT; |
28
|
|
|
|
29
|
|
|
protected static $me; |
30
|
|
|
|
31
|
18 |
|
public function __construct($magiumConfigurationFile = null, $context = ConfigurationRepository::CONTEXT_DEFAULT, $cwd = __DIR__) |
32
|
|
|
{ |
33
|
18 |
|
self::$me = $this; |
34
|
18 |
|
if ($context instanceof Context) { |
35
|
1 |
|
$context = $context->getContext(); |
36
|
|
|
} |
37
|
18 |
|
$this->context = $context; |
38
|
18 |
|
if (!$magiumConfigurationFile) { |
39
|
9 |
|
$baseDir = realpath(DIRECTORY_SEPARATOR); |
40
|
9 |
|
while ($cwd && $cwd != $baseDir && file_exists($cwd)) { |
41
|
9 |
|
$checkFile = $cwd . DIRECTORY_SEPARATOR . 'magium-configuration.xml'; |
42
|
9 |
|
if (file_exists($checkFile)) { |
43
|
8 |
|
$magiumConfigurationFile = $checkFile; |
44
|
8 |
|
break; |
45
|
|
|
} |
46
|
9 |
|
$lastPos = strrpos($cwd, DIRECTORY_SEPARATOR); |
47
|
9 |
|
$cwd = substr($cwd, 0, $lastPos); |
48
|
|
|
} |
49
|
|
|
} |
50
|
|
|
|
51
|
18 |
|
if (file_exists($magiumConfigurationFile)) { |
52
|
17 |
|
$this->file = realpath($magiumConfigurationFile); |
53
|
|
|
} else { |
54
|
1 |
|
throw new InvalidConfigurationFileException('Unable to file configuration file: ' . $magiumConfigurationFile); |
55
|
|
|
} |
56
|
17 |
|
$this->baseDir = dirname($this->file); |
57
|
17 |
|
chdir($this->baseDir); |
58
|
17 |
|
$this->xml = simplexml_load_file($magiumConfigurationFile); |
59
|
17 |
|
$this->overrideWithEnvironmentVariables(); |
60
|
17 |
|
} |
61
|
|
|
|
62
|
17 |
|
protected function overrideWithEnvironmentVariables() |
63
|
|
|
{ |
64
|
17 |
|
$document = dom_import_simplexml($this->xml)->ownerDocument; |
65
|
17 |
|
$xpath = new \DOMXPath($document); |
66
|
17 |
|
$elements = $xpath->query('//*'); |
67
|
17 |
|
foreach ($elements as $element) { |
68
|
17 |
|
if ($element instanceof \DOMElement) { |
69
|
17 |
|
$paths = []; |
70
|
|
|
do { |
71
|
17 |
|
$paths[] = $element->nodeName; |
72
|
17 |
|
} while ($element = $element->parentNode); |
73
|
|
|
|
74
|
|
|
// Get rid of the base node and base document. They mean nothing to us. |
75
|
17 |
|
array_pop($paths); |
76
|
17 |
|
array_pop($paths); |
77
|
17 |
|
if ($paths) { |
|
|
|
|
78
|
14 |
|
$paths = array_reverse($paths); |
79
|
14 |
|
$path = implode('/', $paths); |
80
|
14 |
|
$value = $this->getEnvironmentVariableOverride($path); |
81
|
14 |
|
if ($value !== null) { |
82
|
1 |
|
foreach ($paths as &$path) { |
83
|
1 |
|
$path = 's:' . $path; |
84
|
|
|
} |
85
|
1 |
|
$path = implode('/', $paths); |
86
|
1 |
|
$xpathExpression = '/s:magiumBase/' . $path; |
87
|
1 |
|
$this->xml->registerXPathNamespace('s', 'http://www.magiumlib.com/BaseConfiguration'); |
88
|
1 |
|
$simpleXmlElement = $this->xml->xpath($xpathExpression); |
89
|
1 |
|
if ($simpleXmlElement) { |
|
|
|
|
90
|
1 |
|
$simpleXmlElement = $simpleXmlElement[0]; |
91
|
1 |
|
$simpleXmlElement[0] = $value; // self reference |
92
|
|
|
} |
93
|
|
|
} |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
} |
97
|
|
|
} |
98
|
17 |
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* @return \SimpleXMLElement |
102
|
|
|
*/ |
103
|
|
|
|
104
|
1 |
|
public function getXml() |
105
|
|
|
{ |
106
|
1 |
|
return $this->xml; |
107
|
|
|
} |
108
|
|
|
|
109
|
2 |
|
public function getMagiumConfigurationFilePath() |
110
|
|
|
{ |
111
|
2 |
|
return $this->file; |
112
|
|
|
} |
113
|
|
|
|
114
|
2 |
|
protected static function getInstance($magiumConfigurationFile = null, $context = ConfigurationRepository::CONTEXT_DEFAULT) |
115
|
|
|
{ |
116
|
2 |
|
if (!self::$me instanceof self) { |
117
|
|
|
new self($magiumConfigurationFile, $context); |
118
|
|
|
} |
119
|
2 |
|
return self::$me; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
public static function configurationFactory($magiumConfigurationFile = null, $context = ConfigurationRepository::CONTEXT_DEFAULT) |
123
|
|
|
{ |
124
|
|
|
$me = self::getInstance($magiumConfigurationFile, $context); |
125
|
|
|
return $me->getConfiguration($context); |
126
|
|
|
} |
127
|
|
|
|
128
|
1 |
|
public static function builderFactory($magiumConfigurationFile = null, $context = ConfigurationRepository::CONTEXT_DEFAULT) |
129
|
|
|
{ |
130
|
1 |
|
$me = self::getInstance($magiumConfigurationFile, $context); |
131
|
1 |
|
return $me->getBuilder(); |
132
|
|
|
} |
133
|
|
|
|
134
|
1 |
|
public static function managerFactory($magiumConfigurationFile = null, $context = ConfigurationRepository::CONTEXT_DEFAULT) |
135
|
|
|
{ |
136
|
1 |
|
$me = self::getInstance($magiumConfigurationFile, $context); |
137
|
1 |
|
return $me->getManager(); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
public function getConfiguration($context = ConfigurationRepository::CONTEXT_DEFAULT) |
141
|
|
|
{ |
142
|
|
|
return $this->getManager()->getConfiguration($context); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
public function setContext($context) |
146
|
|
|
{ |
147
|
|
|
$this->context = $context; |
148
|
|
|
} |
149
|
|
|
|
150
|
15 |
|
public function getEnvironmentVariableOverride($path) |
151
|
|
|
{ |
152
|
15 |
|
$pathTranslated = 'MCM_' . str_replace('/', '_', strtoupper($path)); |
153
|
15 |
|
$value = getenv($pathTranslated); |
154
|
15 |
|
if (!$value) { |
155
|
14 |
|
return null; |
156
|
|
|
} |
157
|
2 |
|
return $value; |
158
|
|
|
} |
159
|
|
|
|
160
|
5 |
|
protected function buildContextFile() |
161
|
|
|
{ |
162
|
5 |
|
chdir($this->baseDir); |
163
|
5 |
|
$contextFileCheck = (string)$this->xml->contextConfigurationFile['file']; |
164
|
5 |
|
$contextFileType = (string)$this->xml->contextConfigurationFile['type']; |
165
|
5 |
|
$contextFile = realpath($contextFileCheck); |
166
|
5 |
|
if (!$contextFile) { |
167
|
1 |
|
throw new MissingConfigurationException('Unable to find context file: ' . $contextFileCheck); |
168
|
|
|
} |
169
|
4 |
|
$class = 'Magium\Configuration\File\Context\\' . ucfirst($contextFileType) . 'File'; |
170
|
4 |
|
$reflectionClass = new \ReflectionClass($class); |
171
|
4 |
|
if ($reflectionClass->isSubclassOf(AbstractContextConfigurationFile::class)) { |
172
|
3 |
|
$instance = $reflectionClass->newInstance($contextFile); |
173
|
3 |
|
if ($instance instanceof AbstractContextConfigurationFile) { |
174
|
3 |
|
return $instance; |
175
|
|
|
} |
176
|
|
|
} |
177
|
1 |
|
throw new InvalidConfigurationException('Unable to load context configuration file: ' . $contextFileCheck); |
178
|
|
|
} |
179
|
|
|
|
180
|
5 |
|
public function getContextFile() |
181
|
|
|
{ |
182
|
5 |
|
if (!$this->contextFile instanceof AbstractContextConfigurationFile) { |
183
|
5 |
|
$this->contextFile = $this->buildContextFile(); |
184
|
|
|
} |
185
|
3 |
|
return $this->contextFile; |
186
|
|
|
} |
187
|
|
|
|
188
|
2 |
|
public function validateConfigurationFile() |
189
|
|
|
{ |
190
|
2 |
|
$result = false; |
191
|
|
|
try { |
192
|
2 |
|
$doc = new \DOMDocument(); |
193
|
2 |
|
$doc->load($this->file); |
194
|
2 |
|
$result = $doc->schemaValidate(__DIR__ . '/../assets/magium-configuration.xsd'); |
195
|
1 |
|
} catch (\Exception $e) { |
196
|
|
|
// $result value is already set |
197
|
|
|
} |
198
|
2 |
|
return $result; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Retrieves an instance of the cache based off of the XML cache configuration |
203
|
|
|
* |
204
|
|
|
* @param \SimpleXMLElement $element |
205
|
|
|
* @return \Zend\Cache\Storage\StorageInterface |
206
|
|
|
*/ |
207
|
|
|
|
208
|
3 |
|
protected function getCache(\SimpleXMLElement $element) |
209
|
|
|
{ |
210
|
3 |
|
$cacheFactory = new CacheFactory(); |
211
|
3 |
|
return $cacheFactory->getCache($element); |
212
|
|
|
} |
213
|
|
|
|
214
|
4 |
|
public function getBuilderFactory() |
215
|
|
|
{ |
216
|
4 |
|
if (!$this->builderFactory instanceof BuilderFactoryInterface) { |
217
|
4 |
|
$builderFactoryConfig = $this->xml->builderFactory; |
218
|
4 |
|
$class = (string)$builderFactoryConfig['class']; |
219
|
4 |
|
if (!$class) { |
220
|
3 |
|
$class = BuilderFactory::class; // das default |
221
|
|
|
} |
222
|
4 |
|
$reflection = new \ReflectionClass($class); |
223
|
4 |
|
if (!$reflection->implementsInterface(BuilderFactoryInterface::class)) { |
224
|
1 |
|
throw new InvalidConfigurationException($class . ' must implement ' . BuilderFactoryInterface::class); |
225
|
|
|
} |
226
|
3 |
|
$this->builderFactory = $reflection->newInstance(new \SplFileInfo($this->baseDir), $this->xml, $this->getContextFile()); |
227
|
|
|
} |
228
|
3 |
|
return $this->builderFactory; |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* @return BuilderInterface |
233
|
|
|
*/ |
234
|
|
|
|
235
|
4 |
|
public function getBuilder() |
236
|
|
|
{ |
237
|
4 |
|
if (!$this->builder instanceof BuilderInterface) { |
238
|
3 |
|
$this->builder = $this->getBuilderFactory()->getBuilder(); |
239
|
|
|
} |
240
|
3 |
|
return $this->builder; |
241
|
|
|
} |
242
|
|
|
|
243
|
5 |
|
protected function getRemoteCache() |
244
|
|
|
{ |
245
|
5 |
|
$cacheConfig = $this->xml->cache; |
246
|
5 |
|
$globalAdapter = $this->getCache($cacheConfig); |
247
|
5 |
|
return $globalAdapter; |
248
|
|
|
} |
249
|
|
|
|
250
|
4 |
|
protected function getLocalCache() |
251
|
|
|
{ |
252
|
4 |
|
$localCache = null; |
253
|
4 |
|
$localCacheConfig = $this->xml->localCache; |
254
|
4 |
|
if ($localCacheConfig) { |
255
|
2 |
|
$localCache = $this->getCache($localCacheConfig); |
256
|
|
|
} |
257
|
4 |
|
return $localCache; |
258
|
|
|
} |
259
|
|
|
|
260
|
7 |
|
public function getManager() |
261
|
|
|
{ |
262
|
7 |
|
if (!$this->manager instanceof ManagerInterface) { |
263
|
6 |
|
$managerClass = Manager::class; |
264
|
6 |
|
if (isset($this->xml->manager['class'])) { |
265
|
2 |
|
$managerClass = (string)$this->xml->manager['class']; |
266
|
|
|
} |
267
|
6 |
|
$reflectionClass = new \ReflectionClass($managerClass); |
268
|
6 |
|
if ($managerClass == Manager::class) { |
269
|
|
|
// just a shortcut so I don't have to rewrite some complicated unit tests. I'm just lazy. |
270
|
4 |
|
$this->manager = new Manager($this->getRemoteCache(), $this->getBuilder(), $this->getLocalCache()); |
271
|
3 |
|
return $this->manager; |
272
|
|
|
} |
273
|
2 |
|
if (!$reflectionClass->implementsInterface(ManagerInterface::class)) { |
274
|
1 |
|
throw new InvalidConfigurationException('Manager class must implement ' . ManagerInterface::class); |
275
|
|
|
} |
276
|
1 |
|
$manager = $reflectionClass->newInstance(); |
277
|
|
|
/* @var $manager ManagerInterface */ |
278
|
1 |
|
$manager->setBuilder($this->getBuilder()); |
279
|
1 |
|
$manager->setLocalCache($this->getLocalCache()); |
280
|
1 |
|
$manager->setRemoteCache($this->getRemoteCache()); |
281
|
1 |
|
$this->manager = $manager; |
282
|
|
|
} |
283
|
2 |
|
return $this->manager; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
} |
287
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.