Passed
Push — develop ( a6220b...8d9e7b )
by Kevin
06:18 queued 02:39
created

Builder::getMergedStructure()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5.0113

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 12
cts 13
cp 0.9231
rs 8.6737
c 0
b 0
f 0
cc 5
eloc 14
nc 5
nop 0
crap 5.0113
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 2
    public function getContainer()
44
    {
45 2
        if (!$this->container instanceof ContainerInterface) {
46 1
            throw new MissingContainerException(
47
                'You are using functionality that requires either a DI Container or Service Locator.  '
48 1
                . 'The container, or container adapter, must implement Interop\Container\ContainerInterface'
49
            );
50
        }
51 1
        return $this->container;
52
    }
53
54
    /**
55
     * Retrieves a list of files that have been registered
56
     *
57
     * @return ConfigurationFileRepository
58
     */
59
60 4
    public function getRegisteredConfigurationFiles()
61
    {
62 4
        return $this->repository;
63
    }
64
65
    /**
66
     * @param Config|null $config
67
     * @return Config
68
     * @throws InvalidConfigurationLocationException
69
     * @throws InvalidFileException
70
     * @throws
71
     */
72
73 5
    public function build($context = Config::CONTEXT_DEFAULT, ConfigInterface $config = null)
74
    {
75
76 5
        if (!$config instanceof Config) {
77 5
            $config = new Config('<config />');
78
        }
79
80 5
        $structure = $this->getMergedStructure();
81
82 3
        if (!$structure instanceof \SimpleXMLElement) {
83 1
            throw new InvalidConfigurationException('No configuration files provided');
84
        }
85
86 2
        $this->buildConfigurationObject($structure, $config, $context);
87
88 2
        return $config;
89
    }
90
91 5
    public function getMergedStructure()
92
    {
93 5
        $files = $this->getRegisteredConfigurationFiles();
94 5
        if (!count($files)) {
95 1
            throw new MissingConfigurationException('No configuration files have been provided.  Please add via registerConfigurationFile()');
96
        }
97
98 4
        $structure = null;
99 4
        foreach ($files as $file) {
100 3
            if (!$file instanceof AdapterInterface) {
101
                throw new InvalidFileException('Configuration file object must implement ' . AdapterInterface::class);
102
            }
103
104 3
            $simpleXml = $file->toXml();
105 3
            if (!$structure instanceof \SimpleXMLElement) {
106 3
                $structure = $simpleXml;
107
            } else {
108 3
                $this->mergeStructure($structure, $simpleXml);
109
            }
110
        }
111 3
        return $structure;
112
    }
113
114
115
    /**
116
     * @param \SimpleXMLElement $structure The object representing the merged configuration structure
117
     * @param \SimpleXmlElement $config An empty config object to be populated
118
     * @return Config The resulting configuration object
119
     */
120
121 11
    public function buildConfigurationObject(
122
        \SimpleXMLElement $structure,
123
        ConfigInterface $config,
124
        $context = Config::CONTEXT_DEFAULT
125
    )
126
    {
127 11
        $structure->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
128 11
        $elements = $structure->xpath('/*/s:section/s:group/s:element');
129 11
        foreach ($elements as $element) {
130 11
            if ($element instanceof \SimpleXMLElement) {
131 11
                $elementId = $element['id'];
132 11
                $group = $element->xpath('..')[0];
133 11
                $groupId = $group['id'];
134 11
                $section = $group->xpath('..')[0];
135 11
                $sectionId = $section['id'];
136 11
                $configPath = sprintf('%s/%s/%s', $sectionId, $groupId, $elementId);
137 11
                $value = $this->storage->getValue($configPath, $context);
138 11
                if ($value) {
139 5
                    if (isset($element['callbackFromStorage'])) {
140 4
                        $callbackString = (string)$element['callbackFromStorage'];
141 4
                        $callback = explode('::', $callbackString);
142 4
                        if (count($callback) == 2) {
143 2
                            $callback[0] = $this->getContainer()->get($element['callbackFromStorage']);
144
                        } else {
145 2
                            $callback = array_shift($callback);
146
                        }
147 3
                        if (!is_callable($callback)) {
148 1
                            throw new UncallableCallbackException('Unable to execute callback: ' . $callbackString);
149
                        }
150 3
                        $value = call_user_func($callback, $value);
151
                    }
152
                } else {
153 6
                    $xpath = sprintf('/*/s:section[@id="%s"]/s:group[@id="%s"]/s:element[@id="%s"]/s:value',
154
                        $sectionId,
155
                        $groupId,
156
                        $elementId
157
                    );
158 6
                    $result = $structure->xpath($xpath);
159 6
                    if (!empty($result)) {
160 6
                        $value = trim((string)$result[0]);
161
                    }
162
                }
163
164 9
                if ($value) {
165 9
                    $config->$sectionId->$groupId->$elementId = $value;
166
                }
167
            }
168
        }
169 9
    }
170
171 4 View Code Duplication
    public function mergeStructure(\SimpleXMLElement $base, \SimpleXMLElement $new)
172
    {
173 4
        $base->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
174 4
        foreach ($new as $item) {
175 4
            if ($item instanceof \SimpleXMLElement) {
176 4
                $xpath = sprintf('/*/s:section[@id="%s"]', $item['id']);
177 4
                $sectionExists = $base->xpath($xpath);
178
179 4
                if (!empty($sectionExists) && $sectionExists[0] instanceof \SimpleXMLElement) {
180 3
                    $section = $sectionExists[0];
181
                } else {
182 1
                    $section = $base->addChild('section');
183
                }
184
185 4
                foreach ($item->attributes() as $name => $value) {
186 4
                    $section[$name] = $value;
187
                }
188 4
                if ($item->group) {
189 4
                    $this->mergeGroup($section, $item->group);
190
                }
191
            }
192
        }
193 4
    }
194
195 4 View Code Duplication
    protected function mergeGroup(\SimpleXMLElement $section, \SimpleXMLElement $newGroups)
196
    {
197 4
        $section->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
198 4
        foreach ($newGroups as $newGroup) {
199 4
            if ($newGroup instanceof \SimpleXMLElement) {
200 4
                $xpath = sprintf('./s:group[@id="%s"]', $newGroup['id']);
201 4
                $groupExists = $section->xpath($xpath);
202
203 4
                if (!empty($groupExists) && $groupExists[0] instanceof \SimpleXMLElement) {
204 3
                    $group = $groupExists[0];
205
                } else {
206 2
                    $group = $section->addChild('group');
207
                }
208 4
                foreach ($newGroup->attributes() as $name => $value) {
209 4
                    $group[$name] = $value;
210
                }
211 4
                $this->mergeElements($group, $newGroup->element);
212
            }
213
        }
214 4
    }
215
216 4
    protected function mergeElements(\SimpleXMLElement $group, \SimpleXMLElement $newElements)
217
    {
218 4
        $group->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
219 4
        foreach ($newElements as $newElement) {
220 4
            if ($newElement instanceof \SimpleXMLElement) {
221 4
                $xpath = sprintf('./s:element[@id="%s"]', $newElement['id']);
222 4
                $elementExists = $group->xpath($xpath);
223
224 4
                if (!empty($elementExists) && $elementExists[0] instanceof \SimpleXMLElement) {
225 1
                    $element = $elementExists[0];
226
                } else {
227 4
                    $element = $group->addChild('element');
228
                }
229 4
                foreach ($newElement->attributes() as $name => $value) {
230 4
                    $element[$name] = $value;
231
                }
232 4
                foreach ($newElement->children() as $key => $item) {
233 4
                    $element->$key = $item;
234
                }
235
            }
236
        }
237 4
    }
238
239
}
240