Completed
Push — master ( 29a8a8...41c355 )
by Stéphane
03:46
created

Daux::write()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 3
dl 0
loc 2
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
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
    /**
297
     * @return \Todaymade\Daux\Format\Base\Generator
298
     */
299
    public function getGenerator()
300
    {
301
        if ($this->generator) {
302
            return $this->generator;
303
        }
304
305
        $generators = $this->getGenerators();
306
307
        $format = $this->getParams()->getFormat();
308
309
        if (!array_key_exists($format, $generators)) {
310
            throw new \RuntimeException("The format '$format' doesn't exist, did you forget to set your processor ?");
311
        }
312
313
        $class = $generators[$format];
314
        if (!class_exists($class)) {
315
            throw new \RuntimeException("Class '$class' not found. We cannot use it as a Generator");
316
        }
317
318
        $interface = 'Todaymade\Daux\Format\Base\Generator';
319
        if (!in_array('Todaymade\Daux\Format\Base\Generator', class_implements($class))) {
320
            throw new \RuntimeException("The class '$class' does not implement the '$interface' interface");
321
        }
322
323
        return $this->generator = new $class($this);
324
    }
325
326
    public function getContentTypeHandler()
327
    {
328
        if ($this->typeHandler) {
329
            return $this->typeHandler;
330
        }
331
332
        $base_types = $this->getGenerator()->getContentTypes();
333
334
        $extended = $this->getProcessor()->addContentType();
335
336
        $types = array_merge($base_types, $extended);
337
338
        return $this->typeHandler = new ContentTypeHandler($types);
339
    }
340
341
    /**
342
     * Get all content file extensions
343
     *
344
     * @return string[]
345
     */
346
    public function getContentExtensions()
347
    {
348
        if (!empty($this->validExtensions)) {
349
            return $this->validExtensions;
350
        }
351
352
        return $this->validExtensions = $this->getContentTypeHandler()->getContentExtensions();
353
    }
354
355
    public static function getOutput() {
356
        if (!Daux::$output) {
357
            Daux:$output = new NullOutput();
0 ignored issues
show
Unused Code introduced by Stéphane Goetz
The assignment to $output is dead and can be removed.
Loading history...
358
        }
359
360
        return Daux::$output;
361
    }
362
363
    /**
364
     * Writes a message to the output.
365
     *
366
     * @param string|array $messages The message as an array of lines or a single string
367
     * @param bool         $newline  Whether to add a newline
368
     * @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
369
     */
370
    public static function write($messages, $newline = false, $options = 0) {
371
        Daux::$output->write($messages, $newline, $options);
372
    }
373
374
    /**
375
     * Writes a message to the output and adds a newline at the end.
376
     *
377
     * @param string|array $messages The message as an array of lines of a single string
378
     * @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
379
     */
380
    public static function writeln($messages, $options = 0) {
381
        Daux::write($messages, true, $options);
382
    }
383
384
    public static function getVerbosity() {
385
        return Daux::getOutput()->getVerbosity();
386
    }
387
}
388