Passed
Pull Request — develop (#18)
by Kevin
03:58 queued 41s
created

Builder   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 213
Duplicated Lines 20.19 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
wmc 41
lcom 1
cbo 9
dl 43
loc 213
rs 8.2769
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 1
A getConfigurationRepository() 0 4 1
A getContainer() 0 10 2
A getRegisteredConfigurationFiles() 0 4 1
C build() 0 32 7
D buildConfigurationObject() 0 45 9
C mergeStructure() 23 23 7
B mergeGroup() 20 20 6
C mergeElements() 0 22 7

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Builder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Builder, and based on these observations, apply Extract Interface, too.

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
    public function __construct(
24
        StorageInterface $cache,
25
        ConfigurationStorageInterface $storage,
26
        ConfigurationFileRepository $repository,
27
        ContainerInterface $container = null,
28
        $hashAlgo = 'sha1'
29
    )
30
    {
31
        $this->cache = $cache;
32
        $this->storage = $storage;
33
        $this->hashAlgo = $hashAlgo;
34
        $this->repository = $repository;
35
        $this->container = $container;
36
    }
37
38
    public function getConfigurationRepository()
39
    {
40
        return $this->repository;
41
    }
42
43
    public function getContainer()
44
    {
45
        if (!$this->container instanceof ContainerInterface) {
46
            throw new MissingContainerException(
47
                'You are using functionality that requires either a DI Container or Service Locator.  '
48
                . 'The container, or container adapter, must implement Interop\Container\ContainerInterface'
49
            );
50
        }
51
        return $this->container;
52
    }
53
54
    /**
55
     * Retrieves a list of files that have been registered
56
     *
57
     * @return ConfigurationFileRepository
58
     */
59
60
    public function getRegisteredConfigurationFiles()
61
    {
62
        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
    public function build($context = Config::CONTEXT_DEFAULT, Config $config = null)
74
    {
75
        $files = $this->getRegisteredConfigurationFiles();
76
        if (!count($files)) {
77
            throw new MissingConfigurationException('No configuration files have been provided.  Please add via registerConfigurationFile()');
78
        }
79
80
        if (!$config instanceof Config) {
81
            $config = new Config('<config />');
82
        }
83
        $structure = null;
84
        foreach ($files as $file) {
85
            if (!$file instanceof AdapterInterface) {
86
                throw new InvalidFileException('Configuration file object must implement ' . AdapterInterface::class);
87
            }
88
89
            $simpleXml = $file->toXml();
90
            if (!$structure instanceof \SimpleXMLElement) {
91
                $structure = $simpleXml;
92
            } else {
93
                $this->mergeStructure($structure, $simpleXml);
94
            }
95
        }
96
97
        if (!$structure instanceof \SimpleXMLElement) {
98
            throw new InvalidConfigurationException('No configuration files provided');
99
        }
100
101
        $this->buildConfigurationObject($structure, $config, $context);
102
103
        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
    public function buildConfigurationObject(\SimpleXMLElement $structure, Config $config, $context = Config::CONTEXT_DEFAULT)
113
    {
114
        $structure->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
115
        $elements = $structure->xpath('/*/s:section/s:group/s:element');
116
        foreach ($elements as $element) {
117
            if ($element instanceof \SimpleXMLElement) {
118
                $elementId = $element['id'];
119
                $group = $element->xpath('..')[0];
120
                $groupId = $group['id'];
121
                $section = $group->xpath('..')[0];
122
                $sectionId = $section['id'];
123
                $configPath = sprintf('%s/%s/%s', $sectionId, $groupId, $elementId);
124
                $value = $this->storage->getValue($configPath, $context);
125
                if ($value) {
126
                    if (isset($element['callbackFromStorage'])) {
127
                        $callbackString = (string)$element['callbackFromStorage'];
128
                        $callback = explode('::', $callbackString);
129
                        if (count($callback) == 2) {
130
                            $callback[0] = $this->getContainer()->get($element['callbackFromStorage']);
131
                        } else {
132
                            $callback = array_shift($callback);
133
                        }
134
                        if (!is_callable($callback)) {
135
                            throw new UncallableCallbackException('Unable to execute callback: ' . $callbackString);
136
                        }
137
                        $value = call_user_func($callback, $value);
138
                    }
139
                } else {
140
                    $xpath = sprintf('/*/s:section[@id="%s"]/s:group[@id="%s"]/s:element[@id="%s"]/s:value',
141
                        $sectionId,
142
                        $groupId,
143
                        $elementId
144
                    );
145
                    $result = $structure->xpath($xpath);
146
                    if (!empty($result)) {
147
                        $value = trim((string)$result[0]);
148
                    }
149
                }
150
151
                if ($value) {
152
                    $config->$sectionId->$groupId->$elementId = $value;
153
                }
154
            }
155
        }
156
    }
157
158 View Code Duplication
    public function mergeStructure(\SimpleXMLElement $base, \SimpleXMLElement $new)
159
    {
160
        $base->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
161
        foreach ($new as $item) {
162
            if ($item instanceof \SimpleXMLElement) {
163
                $xpath = sprintf('/*/s:section[@id="%s"]', $item['id']);
164
                $sectionExists = $base->xpath($xpath);
165
166
                if (!empty($sectionExists) && $sectionExists[0] instanceof \SimpleXMLElement) {
167
                    $section = $sectionExists[0];
168
                } else {
169
                    $section = $base->addChild('section');
170
                }
171
172
                foreach ($item->attributes() as $name => $value) {
173
                    $section[$name] = $value;
174
                }
175
                if ($item->group) {
176
                    $this->mergeGroup($section, $item->group);
177
                }
178
            }
179
        }
180
    }
181
182 View Code Duplication
    protected function mergeGroup(\SimpleXMLElement $section, \SimpleXMLElement $newGroups)
183
    {
184
        $section->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
185
        foreach ($newGroups as $newGroup) {
186
            if ($newGroup instanceof \SimpleXMLElement) {
187
                $xpath = sprintf('./s:group[@id="%s"]', $newGroup['id']);
188
                $groupExists = $section->xpath($xpath);
189
190
                if (!empty($groupExists) && $groupExists[0] instanceof \SimpleXMLElement) {
191
                    $group = $groupExists[0];
192
                } else {
193
                    $group = $section->addChild('group');
194
                }
195
                foreach ($newGroup->attributes() as $name => $value) {
196
                    $group[$name] = $value;
197
                }
198
                $this->mergeElements($group, $newGroup->element);
199
            }
200
        }
201
    }
202
203
    protected function mergeElements(\SimpleXMLElement $group, \SimpleXMLElement $newElements)
204
    {
205
        $group->registerXPathNamespace('s', 'http://www.magiumlib.com/Configuration');
206
        foreach ($newElements as $newElement) {
207
            if ($newElement instanceof \SimpleXMLElement) {
208
                $xpath = sprintf('./s:element[@id="%s"]', $newElement['id']);
209
                $elementExists = $group->xpath($xpath);
210
211
                if (!empty($elementExists) && $elementExists[0] instanceof \SimpleXMLElement) {
212
                    $element = $elementExists[0];
213
                } else {
214
                    $element = $group->addChild('element');
215
                }
216
                foreach ($newElement->attributes() as $name => $value) {
217
                    $element[$name] = $value;
218
                }
219
                foreach ($newElement->children() as $key => $item) {
220
                    $element->$key = $item;
221
                }
222
            }
223
        }
224
    }
225
226
}
227