Daux   C
last analyzed

Complexity

Total Complexity 53

Size/Duplication

Total Lines 401
Duplicated Lines 0 %

Test Coverage

Coverage 4.55%

Importance

Changes 0
Metric Value
eloc 144
dl 0
loc 401
ccs 7
cts 154
cp 0.0455
rs 6.96
c 0
b 0
f 0
wmc 53

21 Methods

Rating   Name   Duplication   Size   Complexity  
A getProcessorClass() 0 18 4
A __construct() 0 9 1
A setProcessor() 0 8 1
A getParams() 0 16 6
A getProcessor() 0 7 2
A loadConfiguration() 0 15 4
A normalizeDocumentationPath() 0 9 2
A normalizeThemePath() 0 9 2
A initializeConfiguration() 0 27 4
A getGenerators() 0 11 1
A loadBaseConfiguration() 0 21 1
A getConfigurationOverride() 0 13 3
A generateTree() 0 21 3
A findAlternatives() 0 12 3
A getContentTypeHandler() 0 13 2
A getVerbosity() 0 2 1
B getGenerator() 0 37 7
A getContentExtensions() 0 7 2
A write() 0 2 1
A getOutput() 0 6 2
A writeln() 0 2 1

How to fix   Complexity   

Complex Class

Complex classes like Daux 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 Daux, and based on these observations, apply Extract Interface, too.

1
<?php namespace Todaymade\Daux;
2
3
use Symfony\Component\Console\Output\NullOutput;
4
use Symfony\Component\Console\Output\OutputInterface;
5
use Todaymade\Daux\ContentTypes\ContentTypeHandler;
6
use Todaymade\Daux\Tree\Builder;
7
use Todaymade\Daux\Tree\Content;
8
use Todaymade\Daux\Tree\Directory;
9
use Todaymade\Daux\Tree\Root;
10
11
class Daux
12
{
13
    const STATIC_MODE = 'DAUX_STATIC';
14
    const LIVE_MODE = 'DAUX_LIVE';
15
16
    public static $output;
17
18
    /** @var string */
19
    public $local_base;
20
21
    /** @var \Todaymade\Daux\Format\Base\Generator */
22
    protected $generator;
23
24
    /** @var ContentTypeHandler */
25
    protected $typeHandler;
26
27
    /**
28
     * @var string[]
29
     */
30
    protected $validExtensions;
31
32
    /** @var Processor */
33
    protected $processor;
34
35
    /** @var Tree\Root */
36
    public $tree;
37
38
    /** @var Config */
39
    public $options;
40
41
    /** @var string */
42
    private $mode;
43
44
    /** @var bool */
45
    private $merged_tree = false;
46
47
    /**
48
     * @param string $mode
49
     */
50
    public function __construct($mode, OutputInterface $output)
51
    {
52
        Daux::$output = $output;
53
        $this->mode = $mode;
54
55
        $this->local_base = dirname(__DIR__);
56
57
        // global.json
58
        $this->loadBaseConfiguration();
59
    }
60
61
    /**
62
     * @param string $override_file
63
     * @throws Exception
64
     */
65
    public function initializeConfiguration($override_file = null)
66
    {
67
        $params = $this->getParams();
68
69
        // Validate and set theme path
70
        $params->setDocumentationDirectory(
71
            $docs_path = $this->normalizeDocumentationPath($this->getParams()->getDocumentationDirectory())
72
        );
73
74
        // Read documentation overrides
75
        $this->loadConfiguration($docs_path . DIRECTORY_SEPARATOR . 'config.json');
76
77
        // Read command line overrides
78
        $override_file = $this->getConfigurationOverride($override_file);
79
        if ($override_file !== null) {
80
            $params->setConfigurationOverrideFile($override_file);
81
            $this->loadConfiguration($override_file);
82
        }
83
84
        // Validate and set theme path
85
        $params->setThemesPath($this->normalizeThemePath($params->getThemesDirectory()));
86
87
        // Set a valid default timezone
88
        if ($params->hasTimezone()) {
89
            date_default_timezone_set($params->getTimezone());
90
        } elseif (!ini_get('date.timezone')) {
91
            date_default_timezone_set('GMT');
92
        }
93
    }
94
95
    /**
96
     * Get the file requested for configuration overrides
97
     *
98
     * @param string|null $path
99
     * @return string|null the path to a file to load for configuration overrides
100
     * @throws Exception
101
     */
102
    public function getConfigurationOverride($path)
103
    {
104
        $validPath = DauxHelper::findLocation($path, $this->local_base, 'DAUX_CONFIGURATION', 'file');
105
106
        if ($validPath === null) {
107
            return null;
108
        }
109
110
        if (!$validPath) {
111
            throw new Exception('The configuration override file does not exist. Check the path again : ' . $path);
112
        }
113
114
        return $validPath;
115
    }
116
117
    public function normalizeThemePath($path)
118
    {
119
        $validPath = DauxHelper::findLocation($path, $this->local_base, 'DAUX_THEME', 'dir');
120
121
        if (!$validPath) {
122
            throw new Exception('The Themes directory does not exist. Check the path again : ' . $path);
123
        }
124
125
        return $validPath;
126
127
    }
128
129
    public function normalizeDocumentationPath($path)
130
    {
131
        $validPath = DauxHelper::findLocation($path, $this->local_base, 'DAUX_SOURCE', 'dir');
132
133
        if (!$validPath) {
134
            throw new Exception('The Docs directory does not exist. Check the path again : ' . $path);
135
        }
136
137
        return $validPath;
138
    }
139
140
    /**
141
     * Load and validate the global configuration
142
     *
143
     * @throws Exception
144
     */
145
    protected function loadBaseConfiguration()
146
    {
147
        $this->options = new Config();
148
149
        // Set the default configuration
150
        $this->options->merge([
151
            'docs_directory' => 'docs',
152
            'valid_content_extensions' => ['md', 'markdown'],
153
154
            //Paths and tree
155
            'mode' => $this->mode,
156
            'local_base' => $this->local_base,
157
            'templates' => 'templates',
158
159
            'index_key' => 'index.html',
160
            'base_page' => '',
161
            'base_url' => '',
162
        ]);
163
164
        // Load the global configuration
165
        $this->loadConfiguration($this->local_base . DIRECTORY_SEPARATOR . 'global.json', false);
166
    }
167
168
    /**
169
     * @param string $config_file
170
     * @param bool $optional
171
     * @throws Exception
172
     */
173
    protected function loadConfiguration($config_file, $optional = true)
174
    {
175
        if (!file_exists($config_file)) {
176
            if ($optional) {
177
                return;
178
            }
179
180
            throw new Exception('The configuration file is missing. Check path : ' . $config_file);
181
        }
182
183
        $config = json_decode(file_get_contents($config_file), true);
184
        if (!isset($config)) {
185
            throw new Exception('The configuration file "' . $config_file . '" is corrupt. Is your JSON well-formed ?');
186
        }
187
        $this->options->merge($config);
188
    }
189
190
    /**
191
     * Generate the tree that will be used
192
     */
193
    public function generateTree()
194
    {
195
        $this->options['valid_content_extensions'] = $this->getContentExtensions();
196
197
        $this->tree = new Root($this->getParams());
198
        Builder::build($this->tree, $this->options['ignore']);
199
200
        // Apply the language name as Section title
201
        if ($this->options->isMultilanguage()) {
202
            foreach ($this->options['languages'] as $key => $node) {
203
                $this->tree->getEntries()[$key]->setTitle($node);
204
            }
205
        }
206
207
        // Enhance the tree with processors
208
        $this->getProcessor()->manipulateTree($this->tree);
209
210
        // Sort the tree one last time before it is finalized
211
        Builder::sortTree($this->tree);
212
213
        Builder::finalizeTree($this->tree);
214
    }
215
216
    /**
217
     * @return Config
218
     */
219
    public function getParams()
220
    {
221
        if ($this->tree && !$this->merged_tree) {
222
            $this->options['tree'] = $this->tree;
223
            $this->options['index'] = $this->tree->getIndexPage() ?: $this->tree->getFirstPage();
224
            if ($this->options->isMultilanguage()) {
225
                foreach ($this->options['languages'] as $key => $name) {
226
                    $this->options['entry_page'][$key] = $this->tree->getEntries()[$key]->getFirstPage();
0 ignored issues
show
Bug introduced by Stéphane Goetz
The method getFirstPage() does not exist on Todaymade\Daux\Tree\Entry. It seems like you code against a sub-type of Todaymade\Daux\Tree\Entry such as Todaymade\Daux\Tree\Directory. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

226
                    /** @scrutinizer ignore-call */ 
227
                    $this->options['entry_page'][$key] = $this->tree->getEntries()[$key]->getFirstPage();
Loading history...
227
                }
228
            } else {
229
                $this->options['entry_page'] = $this->tree->getFirstPage();
230
            }
231
            $this->merged_tree = true;
232
        }
233
234
        return $this->options;
235
    }
236
237
    /**
238
     * @return Processor
239
     */
240
    public function getProcessor()
241
    {
242
        if (!$this->processor) {
243
            $this->processor = new Processor($this, Daux::getOutput(), 0);
244
        }
245
246
        return $this->processor;
247
    }
248
249
    /**
250
     * @param Processor $processor
251
     */
252
    public function setProcessor(Processor $processor)
253
    {
254
        $this->processor = $processor;
255
256
        // This is not the cleanest but it's
257
        // the best i've found to use the
258
        // processor in very remote places
259
        $this->options['processor_instance'] = $processor;
260
    }
261
262
    public function getGenerators()
263
    {
264
        $default = [
265
            'confluence' => '\Todaymade\Daux\Format\Confluence\Generator',
266
            'html-file' => '\Todaymade\Daux\Format\HTMLFile\Generator',
267
            'html' => '\Todaymade\Daux\Format\HTML\Generator',
268
        ];
269
270
        $extended = $this->getProcessor()->addGenerators();
271
272
        return array_replace($default, $extended);
273
    }
274
275
276
    public function getProcessorClass()
277
    {
278
        $processor = $this->getParams()['processor'];
279
280
        if (empty($processor)) {
281
            return null;
282
        }
283
284
        $class = '\\Todaymade\\Daux\\Extension\\' . $processor;
285
        if (!class_exists($class)) {
286
            throw new \RuntimeException("Class '$class' not found. We cannot use it as a Processor");
287
        }
288
289
        if (!array_key_exists('Todaymade\\Daux\\Processor', class_parents($class))) {
290
            throw new \RuntimeException("Class '$class' invalid, should extend '\\Todaymade\\Daux\\Processor'");
291
        }
292
293
        return $class;
294
    }
295
296
    protected function findAlternatives($input, $words) {
297
        $alternatives = [];
298
299
        foreach ($words as $word) {
300
            $lev = levenshtein($input, $word);
301
302
            if ($lev <= \strlen($word) / 3) {
303
                $alternatives[] = $word;
304
            }
305
        }
306
307
        return $alternatives;
308
    }
309
310
    /**
311
     * @return \Todaymade\Daux\Format\Base\Generator
312
     */
313
    public function getGenerator()
314
    {
315
        if ($this->generator) {
316
            return $this->generator;
317
        }
318
319
        $generators = $this->getGenerators();
320
321
        $format = $this->getParams()->getFormat();
322
323
        if (!array_key_exists($format, $generators)) {
324
            $message = "The format '$format' doesn't exist, did you forget to set your processor ?";
325
326
            $alternatives = $this->findAlternatives($format, array_keys($generators));
327
328
            if (0 == \count($alternatives)) {
329
                $message .= "\n\nAvailable formats are \n    " . implode("\n    ", array_keys($generators));
330
            } elseif (1 == \count($alternatives)) {
331
                $message .= "\n\nDid you mean this?\n    " . implode("\n    ", $alternatives);
332
            } else {
333
                $message .= "\n\nDid you mean one of these?\n    " . implode("\n    ", $alternatives);
334
            }
335
336
            throw new \RuntimeException($message);
337
        }
338
339
        $class = $generators[$format];
340
        if (!class_exists($class)) {
341
            throw new \RuntimeException("Class '$class' not found. We cannot use it as a Generator");
342
        }
343
344
        $interface = 'Todaymade\Daux\Format\Base\Generator';
345
        if (!in_array('Todaymade\Daux\Format\Base\Generator', class_implements($class))) {
346
            throw new \RuntimeException("The class '$class' does not implement the '$interface' interface");
347
        }
348
349
        return $this->generator = new $class($this);
350
    }
351
352
    public function getContentTypeHandler()
353
    {
354
        if ($this->typeHandler) {
355
            return $this->typeHandler;
356
        }
357
358
        $base_types = $this->getGenerator()->getContentTypes();
359
360
        $extended = $this->getProcessor()->addContentType();
361
362
        $types = array_merge($base_types, $extended);
363
364
        return $this->typeHandler = new ContentTypeHandler($types);
365
    }
366
367
    /**
368
     * Get all content file extensions
369
     *
370
     * @return string[]
371
     */
372
    public function getContentExtensions()
373
    {
374
        if (!empty($this->validExtensions)) {
375
            return $this->validExtensions;
376
        }
377
378
        return $this->validExtensions = $this->getContentTypeHandler()->getContentExtensions();
379
    }
380
381 2
    public static function getOutput() {
382 2
        if (!Daux::$output) {
383 1
            Daux::$output = new NullOutput();
384
        }
385
386 2
        return Daux::$output;
387
    }
388
389
    /**
390
     * Writes a message to the output.
391
     *
392
     * @param string|array $messages The message as an array of lines or a single string
393
     * @param bool         $newline  Whether to add a newline
394
     * @param int          $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
395
     */
396
    public static function write($messages, $newline = false, $options = 0) {
397
        Daux::getOutput()->write($messages, $newline, $options);
398
    }
399
400
    /**
401
     * Writes a message to the output and adds a newline at the end.
402
     *
403
     * @param string|array $messages The message as an array of lines of a single string
404
     * @param int          $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
405
     */
406 2
    public static function writeln($messages, $options = 0) {
407 2
        Daux::getOutput()->write($messages, true, $options);
408 2
    }
409
410
    public static function getVerbosity() {
411
        return Daux::getOutput()->getVerbosity();
412
    }
413
}
414