Passed
Push — develop ( 16aa6b...1646d8 )
by Kevin
06:20 queued 02:58
created

Builder::getStorage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
3
namespace Magium\Configuration\Config;
4
5
use Interop\Container\ContainerInterface;
6
use Magium\Configuration\Config\Storage\StorageInterface as ConfigurationStorageInterface;
7
use Magium\Configuration\File\AdapterInterface;
8
use Magium\Configuration\File\Configuration\ConfigurationFileRepository;
9
use Magium\Configuration\File\Configuration\UnsupportedFileTypeException;
10
use Magium\Configuration\File\InvalidFileException;
11
use Magium\Configuration\InvalidConfigurationException;
12
use Zend\Cache\Storage\StorageInterface;
13
14
class Builder implements BuilderInterface
15
{
16
17
    protected $repository;
18
    protected $cache;
19
    protected $container;
20
    protected $hashAlgo;
21
    protected $storage;
22
23 17
    public function __construct(
24
        StorageInterface $cache,
25
        ConfigurationStorageInterface $storage,
26
        ConfigurationFileRepository $repository,
27
        ContainerInterface $container = null,
28
        $hashAlgo = 'sha1'
29
    )
30
    {
31 17
        $this->cache = $cache;
32 17
        $this->storage = $storage;
33 17
        $this->hashAlgo = $hashAlgo;
34 17
        $this->repository = $repository;
35 17
        $this->container = $container;
36 17
    }
37
38 2
    public function getConfigurationRepository()
39
    {
40 2
        return $this->repository;
41
    }
42
43
    public function getStorage()
44
    {
45
        return $this->storage;
46
    }
47
48 6
    public function setValue($path, $value, $requestedContext = ConfigurationRepository::CONTEXT_DEFAULT)
49
    {
50 6
        $parts = explode('/', $path);
51 6
        if (count($parts) != 3) {
52 3
            throw new InvalidArgumentException('Path must be in the structure of section/group/element');
53
        }
54
55 3
        $merged = $this->getMergedStructure();
56 3
        $merged->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
57
58 3
        $xpath = sprintf('//s:section[@id="%s"]/s:group[@id="%s"]/s:element[@id="%s"]/s:permittedValues/s:value',
59 3
            $parts[0],
60 3
            $parts[1],
61 3
            $parts[2]
62
        );
63
64 3
        $elements = $merged->xpath($xpath);
65 3
        if ($elements) {
66 3
            $check = [];
67 3
            foreach ($elements as $element) {
68 3
                $check[] = (string)$element;
69
            }
70 3
            if (!in_array($value, $check)) {
71 1
                throw new InvalidArgumentException('The value must be one of: ' . implode(', ', $check));
72
            }
73
        }
74
75 2
        return $this->getStorage()->setValue($path, $value, $requestedContext);
76
    }
77
78 2
    public function getContainer()
79
    {
80 2
        if (!$this->container instanceof ContainerInterface) {
81 1
            throw new MissingContainerException(
82
                'You are using functionality that requires either a DI Container or Service Locator.  '
83 1
                . 'The container, or container adapter, must implement Interop\Container\ContainerInterface'
84
            );
85
        }
86 1
        return $this->container;
87
    }
88
89
    /**
90
     * Retrieves a list of files that have been registered
91
     *
92
     * @return ConfigurationFileRepository
93
     */
94
95 4
    public function getRegisteredConfigurationFiles()
96
    {
97 4
        return $this->repository;
98
    }
99
100
    /**
101
     * @param ConfigurationRepository|null $config
102
     * @return ConfigurationRepository
103
     * @throws InvalidConfigurationLocationException
104
     * @throws InvalidFileException
105
     * @throws
106
     */
107
108 5
    public function build($context = ConfigurationRepository::CONTEXT_DEFAULT, ConfigInterface $config = null)
109
    {
110
111 5
        if (!$config instanceof ConfigurationRepository) {
112 5
            $config = new ConfigurationRepository('<config />');
113
        }
114
115 5
        $structure = $this->getMergedStructure();
116
117 3
        if (!$structure instanceof \SimpleXMLElement) {
118 1
            throw new InvalidConfigurationException('No configuration files provided');
119
        }
120
121 2
        $this->buildConfigurationObject($structure, $config, $context);
122
123 2
        return $config;
124
    }
125
126
    /**
127
     * @return \SimpleXMLElement
128
     * @throws InvalidFileException
129
     * @throws MissingConfigurationException
130
     */
131
132 5
    public function getMergedStructure()
133
    {
134 5
        $files = $this->getRegisteredConfigurationFiles();
135 5
        if (!count($files)) {
136 1
            throw new MissingConfigurationException('No configuration files have been provided.  Please add via registerConfigurationFile()');
137
        }
138
139 4
        $structure = null;
140 4
        foreach ($files as $file) {
141 3
            if (!$file instanceof AdapterInterface) {
142
                throw new InvalidFileException('Configuration file object must implement ' . AdapterInterface::class);
143
            }
144
145 3
            $simpleXml = $file->toXml();
146 3
            if (!$structure instanceof \SimpleXMLElement) {
147 3
                $structure = $simpleXml;
148
            } else {
149 3
                $this->mergeStructure($structure, $simpleXml);
150
            }
151
        }
152 3
        return $structure;
153
    }
154
155
156
    /**
157
     * @param \SimpleXMLElement $structure The object representing the merged configuration structure
158
     * @param \SimpleXmlElement $config An empty config object to be populated
159
     * @return ConfigurationRepository The resulting configuration object
160
     */
161
162 11
    public function buildConfigurationObject(
163
        \SimpleXMLElement $structure,
164
        ConfigInterface $config,
165
        $context = ConfigurationRepository::CONTEXT_DEFAULT
166
    )
167
    {
168 11
        $structure->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
169 11
        $elements = $structure->xpath('/*/s:section/s:group/s:element');
170 11
        foreach ($elements as $element) {
171 11
            if ($element instanceof \SimpleXMLElement) {
172 11
                $elementId = $element['id'];
173 11
                $group = $element->xpath('..')[0];
174 11
                $groupId = $group['id'];
175 11
                $section = $group->xpath('..')[0];
176 11
                $sectionId = $section['id'];
177 11
                $configPath = sprintf('%s/%s/%s', $sectionId, $groupId, $elementId);
178 11
                $value = $this->storage->getValue($configPath, $context);
179 11
                if ($value) {
180 5
                    if (isset($element['callbackFromStorage'])) {
181 4
                        $callbackString = (string)$element['callbackFromStorage'];
182 4
                        $callback = explode('::', $callbackString);
183 4
                        if (count($callback) == 2) {
184 2
                            $callback[0] = $this->getContainer()->get($element['callbackFromStorage']);
185
                        } else {
186 2
                            $callback = array_shift($callback);
187
                        }
188 3
                        if (!is_callable($callback)) {
189 1
                            throw new UncallableCallbackException('Unable to execute callback: ' . $callbackString);
190
                        }
191 3
                        $value = call_user_func($callback, $value);
192
                    }
193
                } else {
194 6
                    $xpath = sprintf('/*/s:section[@id="%s"]/s:group[@id="%s"]/s:element[@id="%s"]/s:value',
195
                        $sectionId,
196
                        $groupId,
197
                        $elementId
198
                    );
199 6
                    $result = $structure->xpath($xpath);
200 6
                    if (!empty($result)) {
201 6
                        $value = trim((string)$result[0]);
202
                    }
203
                }
204
205 9
                if ($value) {
206 9
                    $config->$sectionId->$groupId->$elementId = $value;
207
                }
208
            }
209
        }
210 9
    }
211
212 4 View Code Duplication
    public function mergeStructure(\SimpleXMLElement $base, \SimpleXMLElement $new)
213
    {
214 4
        $base->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
215 4
        foreach ($new as $item) {
216 4
            if ($item instanceof \SimpleXMLElement) {
217 4
                $xpath = sprintf('/*/s:section[@id="%s"]', $item['id']);
218 4
                $sectionExists = $base->xpath($xpath);
219
220 4
                if (!empty($sectionExists) && $sectionExists[0] instanceof \SimpleXMLElement) {
221 3
                    $section = $sectionExists[0];
222
                } else {
223 1
                    $section = $base->addChild('section');
224
                }
225
226 4
                foreach ($item->attributes() as $name => $value) {
227 4
                    $section[$name] = $value;
228
                }
229 4
                if ($item->group) {
230 4
                    $this->mergeGroup($section, $item->group);
231
                }
232
            }
233
        }
234 4
    }
235
236 4 View Code Duplication
    protected function mergeGroup(\SimpleXMLElement $section, \SimpleXMLElement $newGroups)
237
    {
238 4
        $section->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
239 4
        foreach ($newGroups as $newGroup) {
240 4
            if ($newGroup instanceof \SimpleXMLElement) {
241 4
                $xpath = sprintf('./s:group[@id="%s"]', $newGroup['id']);
242 4
                $groupExists = $section->xpath($xpath);
243
244 4
                if (!empty($groupExists) && $groupExists[0] instanceof \SimpleXMLElement) {
245 3
                    $group = $groupExists[0];
246
                } else {
247 2
                    $group = $section->addChild('group');
248
                }
249 4
                foreach ($newGroup->attributes() as $name => $value) {
250 4
                    $group[$name] = $value;
251
                }
252 4
                $this->mergeElements($group, $newGroup->element);
253
            }
254
        }
255 4
    }
256
257 4
    protected function mergeElements(\SimpleXMLElement $group, \SimpleXMLElement $newElements)
258
    {
259 4
        $group->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
260 4
        foreach ($newElements as $newElement) {
261 4
            if ($newElement instanceof \SimpleXMLElement) {
262 4
                $xpath = sprintf('./s:element[@id="%s"]', $newElement['id']);
263 4
                $elementExists = $group->xpath($xpath);
264
265 4
                if (!empty($elementExists) && $elementExists[0] instanceof \SimpleXMLElement) {
266 1
                    $element = $elementExists[0];
267
                } else {
268 4
                    $element = $group->addChild('element');
269
                }
270 4
                foreach ($newElement->attributes() as $name => $value) {
271 4
                    $element[$name] = $value;
272
                }
273 4
                foreach ($newElement->children() as $key => $item) {
274 4
                    $element->$key = $item;
275
                }
276
            }
277
        }
278 4
    }
279
280
}
281