Passed
Pull Request — develop (#22)
by Kevin
02:38
created

Builder::buildConfigurationObject()   C

Complexity

Conditions 9
Paths 14

Size

Total Lines 49
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 49
ccs 29
cts 29
cp 1
rs 5.7446
c 0
b 0
f 0
cc 9
eloc 36
nc 14
nop 3
crap 9
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 16
    public function __construct(
24
        StorageInterface $cache,
25
        ConfigurationStorageInterface $storage,
26
        ConfigurationFileRepository $repository,
27
        ContainerInterface $container = null,
28
        $hashAlgo = 'sha1'
29
    )
30
    {
31 16
        $this->cache = $cache;
32 16
        $this->storage = $storage;
33 16
        $this->hashAlgo = $hashAlgo;
34 16
        $this->repository = $repository;
35 16
        $this->container = $container;
36 16
    }
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 4
    public function build($context = Config::CONTEXT_DEFAULT, ConfigInterface $config = null)
74
    {
75 4
        $files = $this->getRegisteredConfigurationFiles();
76 4
        if (!count($files)) {
77 1
            throw new MissingConfigurationException('No configuration files have been provided.  Please add via registerConfigurationFile()');
78
        }
79
80 3
        if (!$config instanceof Config) {
81 3
            $config = new Config('<config />');
82
        }
83 3
        $structure = null;
84 3
        foreach ($files as $file) {
85 2
            if (!$file instanceof AdapterInterface) {
86
                throw new InvalidFileException('Configuration file object must implement ' . AdapterInterface::class);
87
            }
88
89 2
            $simpleXml = $file->toXml();
90 2
            if (!$structure instanceof \SimpleXMLElement) {
91 2
                $structure = $simpleXml;
92
            } else {
93 2
                $this->mergeStructure($structure, $simpleXml);
94
            }
95
        }
96
97 2
        if (!$structure instanceof \SimpleXMLElement) {
98
            throw new InvalidConfigurationException('No configuration files provided');
99
        }
100
101 2
        $this->buildConfigurationObject($structure, $config, $context);
102
103 2
        return $config;
104
    }
105
106
    /**
107
     * @param \SimpleXMLElement $structure The object representing the merged configuration structure
108
     * @param \SimpleXmlElement $config An empty config object to be populated
109
     * @return Config The resulting configuration object
110
     */
111
112 11
    public function buildConfigurationObject(
113
        \SimpleXMLElement $structure,
114
        ConfigInterface $config,
115
        $context = Config::CONTEXT_DEFAULT
116
    )
117
    {
118 11
        $structure->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
119 11
        $elements = $structure->xpath('/*/s:section/s:group/s:element');
120 11
        foreach ($elements as $element) {
121 11
            if ($element instanceof \SimpleXMLElement) {
122 11
                $elementId = $element['id'];
123 11
                $group = $element->xpath('..')[0];
124 11
                $groupId = $group['id'];
125 11
                $section = $group->xpath('..')[0];
126 11
                $sectionId = $section['id'];
127 11
                $configPath = sprintf('%s/%s/%s', $sectionId, $groupId, $elementId);
128 11
                $value = $this->storage->getValue($configPath, $context);
129 11
                if ($value) {
130 5
                    if (isset($element['callbackFromStorage'])) {
131 4
                        $callbackString = (string)$element['callbackFromStorage'];
132 4
                        $callback = explode('::', $callbackString);
133 4
                        if (count($callback) == 2) {
134 2
                            $callback[0] = $this->getContainer()->get($element['callbackFromStorage']);
135
                        } else {
136 2
                            $callback = array_shift($callback);
137
                        }
138 3
                        if (!is_callable($callback)) {
139 1
                            throw new UncallableCallbackException('Unable to execute callback: ' . $callbackString);
140
                        }
141 3
                        $value = call_user_func($callback, $value);
142
                    }
143
                } else {
144 6
                    $xpath = sprintf('/*/s:section[@id="%s"]/s:group[@id="%s"]/s:element[@id="%s"]/s:value',
145
                        $sectionId,
146
                        $groupId,
147
                        $elementId
148
                    );
149 6
                    $result = $structure->xpath($xpath);
150 6
                    if (!empty($result)) {
151 6
                        $value = trim((string)$result[0]);
152
                    }
153
                }
154
155 9
                if ($value) {
156 9
                    $config->$sectionId->$groupId->$elementId = $value;
157
                }
158
            }
159
        }
160 9
    }
161
162 3 View Code Duplication
    public function mergeStructure(\SimpleXMLElement $base, \SimpleXMLElement $new)
163
    {
164 3
        $base->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
165 3
        foreach ($new as $item) {
166 3
            if ($item instanceof \SimpleXMLElement) {
167 3
                $xpath = sprintf('/*/s:section[@id="%s"]', $item['id']);
168 3
                $sectionExists = $base->xpath($xpath);
169
170 3
                if (!empty($sectionExists) && $sectionExists[0] instanceof \SimpleXMLElement) {
171 2
                    $section = $sectionExists[0];
172
                } else {
173 1
                    $section = $base->addChild('section');
174
                }
175
176 3
                foreach ($item->attributes() as $name => $value) {
177 3
                    $section[$name] = $value;
178
                }
179 3
                if ($item->group) {
180 3
                    $this->mergeGroup($section, $item->group);
181
                }
182
            }
183
        }
184 3
    }
185
186 3 View Code Duplication
    protected function mergeGroup(\SimpleXMLElement $section, \SimpleXMLElement $newGroups)
187
    {
188 3
        $section->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
189 3
        foreach ($newGroups as $newGroup) {
190 3
            if ($newGroup instanceof \SimpleXMLElement) {
191 3
                $xpath = sprintf('./s:group[@id="%s"]', $newGroup['id']);
192 3
                $groupExists = $section->xpath($xpath);
193
194 3
                if (!empty($groupExists) && $groupExists[0] instanceof \SimpleXMLElement) {
195 2
                    $group = $groupExists[0];
196
                } else {
197 2
                    $group = $section->addChild('group');
198
                }
199 3
                foreach ($newGroup->attributes() as $name => $value) {
200 3
                    $group[$name] = $value;
201
                }
202 3
                $this->mergeElements($group, $newGroup->element);
203
            }
204
        }
205 3
    }
206
207 3
    protected function mergeElements(\SimpleXMLElement $group, \SimpleXMLElement $newElements)
208
    {
209 3
        $group->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
210 3
        foreach ($newElements as $newElement) {
211 3
            if ($newElement instanceof \SimpleXMLElement) {
212 3
                $xpath = sprintf('./s:element[@id="%s"]', $newElement['id']);
213 3
                $elementExists = $group->xpath($xpath);
214
215 3
                if (!empty($elementExists) && $elementExists[0] instanceof \SimpleXMLElement) {
216 1
                    $element = $elementExists[0];
217
                } else {
218 3
                    $element = $group->addChild('element');
219
                }
220 3
                foreach ($newElement->attributes() as $name => $value) {
221 3
                    $element[$name] = $value;
222
                }
223 3
                foreach ($newElement->children() as $key => $item) {
224 3
                    $element->$key = $item;
225
                }
226
            }
227
        }
228 3
    }
229
230
}
231