ConfigBuilder   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 323
Duplicated Lines 0 %

Test Coverage

Coverage 61.65%

Importance

Changes 4
Bugs 1 Features 1
Metric Value
eloc 112
c 4
b 1
f 1
dl 0
loc 323
ccs 82
cts 133
cp 0.6165
rs 8.64
wmc 47

24 Methods

Rating   Name   Duplication   Size   Complexity  
A withCache() 0 5 1
A withThemesPath() 0 5 1
A with() 0 5 1
A withProcessor() 0 5 1
A setValue() 0 14 5
A withConfluenceDelete() 0 5 1
A withThemesDirectory() 0 5 1
A normalizeThemePath() 0 9 2
A getConfigurationOverride() 0 13 3
A normalizeDocumentationPath() 0 9 2
A initializeConfiguration() 0 33 5
A fromFile() 0 3 1
A withConfigurationOverride() 0 5 1
A __construct() 0 5 1
A withMode() 0 6 1
A loadConfiguration() 0 15 4
A withValidContentExtensions() 0 5 1
A withFormat() 0 5 1
A withDocumentationDirectory() 0 5 1
A withValues() 0 5 1
A loadBaseConfiguration() 0 15 1
A build() 0 9 2
A resolveThemeVariant() 0 24 4
A findLocation() 0 24 5

How to fix   Complexity   

Complex Class

Complex classes like ConfigBuilder 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.

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 ConfigBuilder, and based on these observations, apply Extract Interface, too.

1
<?php namespace Todaymade\Daux;
2
3
class ConfigBuilder
4
{
5
    /** @var Config */
6
    private $config;
7
8
    /** @var array */
9
    private $overrideValues = [];
10 55
11 55
    private $configuration_override_file;
12 55
13 55
    private function __construct(string $mode)
14 55
    {
15
        $this->config = new Config();
16
        $this->config['mode'] = $mode;
17
        $this->config['local_base'] = dirname(__DIR__);
18
    }
19
20 55
    public static function fromFile($file): Config
21 55
    {
22 55
        return unserialize(file_get_contents($file));
23 55
    }
24
25
    public static function withMode($mode = Daux::STATIC_MODE): ConfigBuilder
26 15
    {
27 15
        $builder = new ConfigBuilder($mode);
28 15
        $builder->loadBaseConfiguration();
29
30
        return $builder;
31
    }
32
33
    public function with(array $values): ConfigBuilder
34
    {
35
        $this->config->merge($values);
36
37
        return $this;
38
    }
39
40
    private function setValue(Config $array, $key, $value)
41
    {
42
        if (is_null($key)) {
43
            return $array = $value;
0 ignored issues
show
Unused Code introduced by Stéphane Goetz
The assignment to $array is dead and can be removed.
Loading history...
44
        }
45
        $keys = explode('.', $key);
46
        while (count($keys) > 1) {
47
            $key = array_shift($keys);
48
            if (!isset($array[$key]) || !is_array($array[$key])) {
49
                $array[$key] = [];
50
            }
51
            $array = &$array[$key];
52
        }
53
        $array[array_shift($keys)] = $value;
54 21
    }
55 21
56 21
    public function withValues(array $values): ConfigBuilder
57
    {
58
        $this->overrideValues = $values;
59 31
60 31
        return $this;
61 31
    }
62
63
    public function withDocumentationDirectory($dir): ConfigBuilder
64 55
    {
65 55
        $this->config['docs_directory'] = $dir;
66 55
67
        return $this;
68
    }
69
70
    public function withValidContentExtensions(array $value): ConfigBuilder
71
    {
72
        $this->config['valid_content_extensions'] = $value;
73
74
        return $this;
75
    }
76
77
    public function withThemesPath($themePath): ConfigBuilder
78
    {
79
        $this->config['themes_path'] = $themePath;
80
81
        return $this;
82
    }
83
84
    public function withThemesDirectory($directory): ConfigBuilder
85
    {
86
        $this->config['themes_directory'] = $directory;
87
88
        return $this;
89
    }
90
91
    public function withCache(bool $value): ConfigBuilder
92
    {
93
        $this->config['cache'] = $value;
94
95
        return $this;
96
    }
97
98
    public function withFormat($format): ConfigBuilder
99 55
    {
100 55
        $this->config['format'] = $format;
101
102 55
        return $this;
103
    }
104
105 55
    public function withConfigurationOverride($file): ConfigBuilder
106 55
    {
107 55
        $this->configuration_override_file = $file;
108
109
        return $this;
110 55
    }
111
112
    public function withProcessor($value): ConfigBuilder
113
    {
114 55
        $this->config['processor'] = $value;
115 55
116
        return $this;
117
    }
118 55
119 55
    public function withConfluenceDelete($value): ConfigBuilder
120 55
    {
121
        $this->config['confluence']['delete'] = $value;
122
123 55
        return $this;
124
    }
125
126
    public function build(): Config
127 55
    {
128
        $this->initializeConfiguration();
129
130
        foreach ($this->overrideValues as $value) {
131
            $this->setValue($this->config, $value[0], $value[1]);
132
        }
133
134 55
        return $this->config;
135
    }
136 55
137 55
    private function resolveThemeVariant()
138
    {
139
        $theme = $this->config->getHTML()->getTheme();
140 55
        $themesPath = $this->config->getThemesPath() . DIRECTORY_SEPARATOR;
141
142
        // If theme directory exists, we're good with that
143 55
        if (is_dir(($themesPath . $theme))) {
144 55
            return [$theme, ''];
145
        }
146
147
        $themePieces = explode('-', $theme);
148
        $variant = '';
149 55
150
        // Do we have a variant or only a theme ?
151
        if (count($themePieces) > 1) {
152 55
            $variant = array_pop($themePieces);
153 55
            $theme = implode('-', $themePieces);
154 55
        }
155
156
        if (!is_dir($themesPath . $theme)) {
157 55
            throw new \RuntimeException("Theme '{$theme}' not found");
158 55
        }
159
160
        return [$theme, $variant];
161
    }
162
163
    /**
164 55
     * @param string $override_file
165 1
     *
166
     * @throws Exception
167 55
     */
168
    private function initializeConfiguration()
169 55
    {
170 55
        // Validate and set theme path
171
        $docs_path = $this->normalizeDocumentationPath($this->config->getDocumentationDirectory());
172 55
        $this->config['docs_directory'] = $docs_path;
173
174
        // Read documentation overrides
175
        $this->loadConfiguration($docs_path . DIRECTORY_SEPARATOR . 'config.json');
176 55
177
        // Read command line overrides
178
        $override_file = $this->getConfigurationOverride($this->configuration_override_file);
179 55
        if ($override_file !== null) {
180 55
            $this->loadConfiguration($override_file);
181
        }
182 55
183
        // Validate and set theme path
184
        $this->withThemesPath($this->normalizeThemePath($this->config->getThemesDirectory()));
185
186 55
        // Resolve variant once
187
        $theme = $this->resolveThemeVariant();
188
        $this->config['html']['theme'] = $theme[0];
189
        $this->config['html']['theme-variant'] = $theme[1];
190
191
        // Set a valid default timezone
192
        if ($this->config->hasTimezone()) {
193
            date_default_timezone_set($this->config->getTimezone());
194 55
        } elseif (!ini_get('date.timezone')) {
195
            date_default_timezone_set('GMT');
196 55
        }
197 55
198
        // Text search would be too slow on live server
199
        if ($this->config->isLive()) {
200
            $this->config['html']['search'] = false;
201
        }
202
    }
203
204
    private function normalizeThemePath($path)
205
    {
206
        $validPath = $this->findLocation($path, $this->config->getLocalBase(), 'dir');
207 55
208 55
        if (!$validPath) {
209
            throw new Exception('The Themes directory does not exist. Check the path again : ' . $path);
210
        }
211
212
        return $validPath;
213
    }
214
215 55
    private function normalizeDocumentationPath($path)
216 55
    {
217 21
        $validPath = $this->findLocation($path, $this->config->getLocalBase(), 'dir');
218 21
219
        if (!$validPath) {
220
            throw new Exception('The Docs directory does not exist. Check the path again : ' . $path);
221
        }
222
223
        return $validPath;
224 55
    }
225 55
226
    /**
227
     * Load and validate the global configuration.
228 55
     *
229 55
     * @throws Exception
230
     */
231
    private function loadBaseConfiguration()
232
    {
233
        // Set the default configuration
234
        $this->config->merge([
235
            'docs_directory' => 'docs',
236
            'valid_content_extensions' => ['md', 'markdown'],
237
238 55
            //Paths and tree
239 55
            'templates' => 'templates',
240
241 55
            'base_url' => '',
242 55
        ]);
243
244
        // Load the global configuration
245
        $this->loadConfiguration($this->config->getLocalBase() . DIRECTORY_SEPARATOR . 'global.json', false);
246
    }
247
248
    /**
249
     * @param string $config_file
250
     * @param bool $optional
251
     *
252
     * @throws Exception
253
     */
254
    private function loadConfiguration($config_file, $optional = true)
255
    {
256
        if (!file_exists($config_file)) {
257
            if ($optional) {
258 55
                return;
259
            }
260 55
261 55
            throw new Exception('The configuration file is missing. Check path : ' . $config_file);
262
        }
263
264
        $config = json_decode(file_get_contents($config_file), true);
265 55
        if (!isset($config)) {
266 21
            throw new Exception('The configuration file "' . $config_file . '" is corrupt. Is your JSON well-formed ?');
267
        }
268
        $this->config->merge($config);
269
    }
270
271 55
    /**
272 55
     * Get the file requested for configuration overrides.
273
     *
274
     * @param null|string $path
275
     *
276
     * @throws Exception
277
     *
278
     * @return null|string the path to a file to load for configuration overrides
279
     */
280
    private function getConfigurationOverride($path)
281
    {
282
        $validPath = $this->findLocation($path, $this->config->getLocalBase(), 'file');
283
284
        if ($validPath === null) {
285
            return null;
286
        }
287
288
        if (!$validPath) {
289
            throw new Exception('The configuration override file does not exist. Check the path again : ' . $path);
290
        }
291
292
        return $validPath;
293
    }
294
295
    /**
296
     * @param null|string $path
297
     * @param string $basedir
298
     * @param string $type
299
     *
300
     * @return null|false|string
301
     */
302
    private function findLocation($path, $basedir, $type)
303
    {
304
        // If Path is explicitly null, it's useless to go further
305
        if ($path === null) {
306
            return null;
307
        }
308
309
        // VFS, used only in tests
310
        if (substr($path, 0, 6) == 'vfs://') {
311
            return $path;
312
        }
313
314
        // Check if it's relative to the current directory or an absolute path
315
        if (DauxHelper::is($path, $type)) {
316
            return realpath(DauxHelper::getAbsolutePath($path));
317
        }
318
319
        // Check if it exists relative to Daux's root
320
        $newPath = $basedir . DIRECTORY_SEPARATOR . $path;
321
        if (DauxHelper::is($newPath, $type)) {
322
            return realpath($newPath);
323
        }
324
325
        return false;
326
    }
327
}
328