Passed
Branch develop (0ae1ad)
by Kevin
02:45
created

Builder::build()   C

Complexity

Conditions 7
Paths 15

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7.0671

Importance

Changes 0
Metric Value
dl 0
loc 32
ccs 16
cts 18
cp 0.8889
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 19
nc 15
nop 2
crap 7.0671
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
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, Config $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(\SimpleXMLElement $structure, Config $config, $context = Config::CONTEXT_DEFAULT)
113
    {
114 11
        $structure->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
115 11
        $elements = $structure->xpath('/*/s:section/s:group/s:element');
116 11
        foreach ($elements as $element) {
117 11
            if ($element instanceof \SimpleXMLElement) {
118 11
                $elementId = $element['id'];
119 11
                $group = $element->xpath('..')[0];
120 11
                $groupId = $group['id'];
121 11
                $section = $group->xpath('..')[0];
122 11
                $sectionId = $section['id'];
123 11
                $configPath = sprintf('%s/%s/%s', $sectionId, $groupId, $elementId);
124 11
                $value = $this->storage->getValue($configPath, $context);
125 11
                if ($value) {
126 5
                    if (isset($element['callbackFromStorage'])) {
127 4
                        $callbackString = (string)$element['callbackFromStorage'];
128 4
                        $callback = explode('::', $callbackString);
129 4
                        if (count($callback) == 2) {
130 2
                            $callback[0] = $this->getContainer()->get($element['callbackFromStorage']);
131
                        } else {
132 2
                            $callback = array_shift($callback);
133
                        }
134 3
                        if (!is_callable($callback)) {
135 1
                            throw new UncallableCallbackException('Unable to execute callback: ' . $callbackString);
136
                        }
137 3
                        $value = call_user_func($callback, $value);
138
                    }
139
                } else {
140 6
                    $xpath = sprintf('/*/s:section[@id="%s"]/s:group[@id="%s"]/s:element[@id="%s"]/s:value',
141
                        $sectionId,
142
                        $groupId,
143
                        $elementId
144
                    );
145 6
                    $result = $structure->xpath($xpath);
146 6
                    if (!empty($result)) {
147 6
                        $value = trim((string)$result[0]);
148
                    }
149
                }
150
151 9
                if ($value) {
152 9
                    $config->$sectionId->$groupId->$elementId = $value;
153
                }
154
            }
155
        }
156 9
    }
157
158 3 View Code Duplication
    public function mergeStructure(\SimpleXMLElement $base, \SimpleXMLElement $new)
159
    {
160 3
        $base->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
161 3
        foreach ($new as $item) {
162 3
            if ($item instanceof \SimpleXMLElement) {
163 3
                $xpath = sprintf('/*/s:section[@id="%s"]', $item['id']);
164 3
                $sectionExists = $base->xpath($xpath);
165
166 3
                if (!empty($sectionExists) && $sectionExists[0] instanceof \SimpleXMLElement) {
167 2
                    $section = $sectionExists[0];
168
                } else {
169 1
                    $section = $base->addChild('section');
170
                }
171
172 3
                foreach ($item->attributes() as $name => $value) {
173 3
                    $section[$name] = $value;
174
                }
175 3
                if ($item->group) {
176 3
                    $this->mergeGroup($section, $item->group);
177
                }
178
            }
179
        }
180 3
    }
181
182 3 View Code Duplication
    protected function mergeGroup(\SimpleXMLElement $section, \SimpleXMLElement $newGroups)
183
    {
184 3
        $section->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
185 3
        foreach ($newGroups as $newGroup) {
186 3
            if ($newGroup instanceof \SimpleXMLElement) {
187 3
                $xpath = sprintf('./s:group[@id="%s"]', $newGroup['id']);
188 3
                $groupExists = $section->xpath($xpath);
189
190 3
                if (!empty($groupExists) && $groupExists[0] instanceof \SimpleXMLElement) {
191 2
                    $group = $groupExists[0];
192
                } else {
193 2
                    $group = $section->addChild('group');
194
                }
195 3
                foreach ($newGroup->attributes() as $name => $value) {
196 3
                    $group[$name] = $value;
197
                }
198 3
                $this->mergeElements($group, $newGroup->element);
199
            }
200
        }
201 3
    }
202
203 3
    protected function mergeElements(\SimpleXMLElement $group, \SimpleXMLElement $newElements)
204
    {
205 3
        $group->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
206 3
        foreach ($newElements as $newElement) {
207 3
            if ($newElement instanceof \SimpleXMLElement) {
208 3
                $xpath = sprintf('./s:element[@id="%s"]', $newElement['id']);
209 3
                $elementExists = $group->xpath($xpath);
210
211 3
                if (!empty($elementExists) && $elementExists[0] instanceof \SimpleXMLElement) {
212 1
                    $element = $elementExists[0];
213
                } else {
214 3
                    $element = $group->addChild('element');
215
                }
216 3
                foreach ($newElement->attributes() as $name => $value) {
217 3
                    $element[$name] = $value;
218
                }
219 3
                foreach ($newElement->children() as $key => $item) {
220 3
                    $element->$key = $item;
221
                }
222
            }
223
        }
224 3
    }
225
226
}
227