Completed
Push — fix/appveyor-failures ( 26fc47 )
by Colin
03:15
created

Environment::initializeDocumentProcessors()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.0175

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 10
ccs 7
cts 8
cp 0.875
rs 9.4285
cc 3
eloc 5
nc 3
nop 1
crap 3.0175
1
<?php
2
3
/*
4
 * This file is part of the league/commonmark package.
5
 *
6
 * (c) Colin O'Dell <[email protected]>
7
 *
8
 * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
9
 *  - (c) John MacFarlane
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace League\CommonMark;
16
17
use League\CommonMark\Block\Parser\BlockParserInterface;
18
use League\CommonMark\Block\Renderer\BlockRendererInterface;
19
use League\CommonMark\Extension\CommonMarkCoreExtension;
20
use League\CommonMark\Extension\ExtensionInterface;
21
use League\CommonMark\Extension\MiscExtension;
22
use League\CommonMark\Inline\Parser\InlineParserInterface;
23
use League\CommonMark\Inline\Processor\InlineProcessorInterface;
24
use League\CommonMark\Inline\Renderer\InlineRendererInterface;
25
use League\CommonMark\Util\Configuration;
26
use League\CommonMark\Util\ConfigurationAwareInterface;
27
28
class Environment
29
{
30
    const HTML_INPUT_STRIP = 'strip';
31
    const HTML_INPUT_ESCAPE = 'escape';
32
    const HTML_INPUT_ALLOW = 'allow';
33
34
    /**
35
     * @var ExtensionInterface[]
36
     */
37
    protected $extensions = [];
38
39
    /**
40
     * @var bool
41
     */
42
    protected $extensionsInitialized = false;
43
44
    /**
45
     * @var BlockParserInterface[]
46
     */
47
    protected $blockParsers = [];
48
49
    /**
50
     * @var InlineParserInterface[]
51
     */
52
    protected $inlineParsers = [];
53
54
    /**
55
     * @var array
56
     */
57
    protected $inlineParsersByCharacter = [];
58
59
    /**
60
     * @var DocumentProcessorInterface[]
61
     */
62
    protected $documentProcessors = [];
63
64
    /**
65
     * @var InlineProcessorInterface[]
66
     */
67
    protected $inlineProcessors = [];
68
69
    /**
70
     * @var BlockRendererInterface[]
71
     */
72
    protected $blockRenderersByClass = [];
73
74
    /**
75
     * @var InlineRendererInterface[]
76
     */
77
    protected $inlineRenderersByClass = [];
78
79
    /**
80
     * @var Configuration
81
     */
82
    protected $config;
83
84
    /**
85
     * @var string
86
     */
87
    protected $inlineParserCharacterRegex;
88
89 2016
    public function __construct(array $config = [])
90
    {
91 2016
        $this->config = new Configuration($config);
92 2016
    }
93
94
    /**
95
     * @param array $config
96
     */
97 1926
    public function mergeConfig(array $config = [])
98 2
    {
99 1926
        $this->assertUninitialized('Failed to modify configuration.');
100
101 1923
        $this->config->mergeConfig($config);
102 1923
    }
103
104
    /**
105
     * @param array $config
106
     */
107 6
    public function setConfig(array $config = [])
108
    {
109 6
        $this->assertUninitialized('Failed to modify configuration.');
110
111 3
        $this->config->setConfig($config);
112 3
    }
113
114
    /**
115
     * @param string|null $key
116
     * @param mixed       $default
117
     *
118
     * @return mixed
119
     */
120 1932
    public function getConfig($key = null, $default = null)
121
    {
122 1932
        return $this->config->getConfig($key, $default);
123
    }
124
125
    /**
126
     * @param BlockParserInterface $parser
127
     *
128
     * @return $this
129
     */
130 6
    public function addBlockParser(BlockParserInterface $parser)
131
    {
132 6
        $this->assertUninitialized('Failed to add block parser.');
133
134 3
        $this->getMiscExtension()->addBlockParser($parser);
135
136 3
        return $this;
137
    }
138
139
    /**
140
     * @param InlineParserInterface $parser
141
     *
142
     * @return $this
143
     */
144 15
    public function addInlineParser(InlineParserInterface $parser)
145
    {
146 15
        $this->assertUninitialized('Failed to add inline parser.');
147
148 12
        $this->getMiscExtension()->addInlineParser($parser);
149
150 12
        return $this;
151
    }
152
153
    /**
154
     * @param InlineProcessorInterface $processor
155
     *
156
     * @return $this
157
     */
158 6
    public function addInlineProcessor(InlineProcessorInterface $processor)
159
    {
160 6
        $this->assertUninitialized('Failed to add inline processor.');
161
162 3
        $this->getMiscExtension()->addInlineProcessor($processor);
163
164 3
        return $this;
165
    }
166
167
    /**
168
     * @param DocumentProcessorInterface $processor
169
     *
170
     * @return $this
171
     */
172 9
    public function addDocumentProcessor(DocumentProcessorInterface $processor)
173
    {
174 9
        $this->assertUninitialized('Failed to add document processor.');
175
176 6
        $this->getMiscExtension()->addDocumentProcessor($processor);
177
178 6
        return $this;
179
    }
180
181
    /**
182
     * @param string                 $blockClass
183
     * @param BlockRendererInterface $blockRenderer
184
     *
185
     * @return $this
186
     */
187 9
    public function addBlockRenderer($blockClass, BlockRendererInterface $blockRenderer)
188
    {
189 9
        $this->assertUninitialized('Failed to add block renderer.');
190
191 6
        $this->getMiscExtension()->addBlockRenderer($blockClass, $blockRenderer);
192
193 6
        return $this;
194
    }
195
196
    /**
197
     * @param string                  $inlineClass
198
     * @param InlineRendererInterface $renderer
199
     *
200
     * @return $this
201
     */
202 9
    public function addInlineRenderer($inlineClass, InlineRendererInterface $renderer)
203
    {
204 9
        $this->assertUninitialized('Failed to add inline renderer.');
205
206 6
        $this->getMiscExtension()->addInlineRenderer($inlineClass, $renderer);
207
208 6
        return $this;
209
    }
210
211
    /**
212
     * @return BlockParserInterface[]
213
     */
214 1923
    public function getBlockParsers()
215
    {
216 1923
        $this->initializeExtensions();
217
218 1923
        return $this->blockParsers;
219
    }
220
221
    /**
222
     * @param string $name
223
     *
224
     * @return InlineParserInterface
225
     */
226 3
    public function getInlineParser($name)
227
    {
228 3
        $this->initializeExtensions();
229
230 3
        return $this->inlineParsers[$name];
231
    }
232
233
    /**
234
     * @return InlineParserInterface[]
235
     */
236 6
    public function getInlineParsers()
237
    {
238 6
        $this->initializeExtensions();
239
240 6
        return $this->inlineParsers;
241
    }
242
243
    /**
244
     * @param string $character
245
     *
246
     * @return InlineParserInterface[]|null
247
     */
248 1641
    public function getInlineParsersForCharacter($character)
249
    {
250 1641
        $this->initializeExtensions();
251
252 1641
        if (!isset($this->inlineParsersByCharacter[$character])) {
253 1524
            return;
254
        }
255
256 1227
        return $this->inlineParsersByCharacter[$character];
257
    }
258
259
    /**
260
     * @return InlineProcessorInterface[]
261
     */
262 1644
    public function getInlineProcessors()
263
    {
264 1644
        $this->initializeExtensions();
265
266 1644
        return $this->inlineProcessors;
267
    }
268
269
    /**
270
     * @return DocumentProcessorInterface[]
271
     */
272 1920
    public function getDocumentProcessors()
273
    {
274 1920
        $this->initializeExtensions();
275
276 1920
        return $this->documentProcessors;
277
    }
278
279
    /**
280
     * @param string $blockClass
281
     *
282
     * @return BlockRendererInterface|null
283
     */
284 1926
    public function getBlockRendererForClass($blockClass)
285
    {
286 1926
        $this->initializeExtensions();
287
288 1926
        if (!isset($this->blockRenderersByClass[$blockClass])) {
289 6
            return;
290
        }
291
292 1920
        return $this->blockRenderersByClass[$blockClass];
293
    }
294
295
    /**
296
     * @param string $inlineClass
297
     *
298
     * @return InlineRendererInterface|null
299
     */
300 1650
    public function getInlineRendererForClass($inlineClass)
301
    {
302 1650
        $this->initializeExtensions();
303
304 1650
        if (!isset($this->inlineRenderersByClass[$inlineClass])) {
305 9
            return;
306
        }
307
308 1641
        return $this->inlineRenderersByClass[$inlineClass];
309
    }
310
311 6
    public function createInlineParserEngine()
312
    {
313 6
        $this->initializeExtensions();
314
315 6
        return new InlineParserEngine($this);
316
    }
317
318
    /**
319
     * Get all registered extensions
320
     *
321
     * @return ExtensionInterface[]
322
     */
323 15
    public function getExtensions()
324
    {
325 15
        return $this->extensions;
326
    }
327
328
    /**
329
     * Add a single extension
330
     *
331
     * @param ExtensionInterface $extension
332
     *
333
     * @return $this
334
     */
335 1962
    public function addExtension(ExtensionInterface $extension)
336
    {
337 1962
        $this->assertUninitialized('Failed to add extension.');
338
339 1959
        $this->extensions[] = $extension;
340
341 1959
        return $this;
342
    }
343
344 1986
    protected function initializeExtensions()
345
    {
346
        // Only initialize them once
347 1986
        if ($this->extensionsInitialized) {
348 1920
            return;
349
        }
350
351 1986
        $this->extensionsInitialized = true;
352
353
        // Initialize all the registered extensions
354 1986
        foreach ($this->extensions as $extension) {
355 1944
            $this->initializeExtension($extension);
356 1324
        }
357
358
        // Lastly, let's build a regex which matches non-inline characters
359
        // This will enable a huge performance boost with inline parsing
360 1986
        $this->buildInlineParserCharacterRegex();
361 1986
    }
362
363
    /**
364
     * @param ExtensionInterface $extension
365
     */
366 1944
    protected function initializeExtension(ExtensionInterface $extension)
367
    {
368 1944
        $this->initalizeBlockParsers($extension->getBlockParsers());
369 1944
        $this->initializeInlineParsers($extension->getInlineParsers());
370 1944
        $this->initializeInlineProcessors($extension->getInlineProcessors());
371 1944
        $this->initializeDocumentProcessors($extension->getDocumentProcessors());
372 1944
        $this->initializeBlockRenderers($extension->getBlockRenderers());
373 1944
        $this->initializeInlineRenderers($extension->getInlineRenderers());
374 1944
    }
375
376
    /**
377
     * @param BlockParserInterface[] $blockParsers
378
     */
379 1944
    private function initalizeBlockParsers($blockParsers)
380
    {
381 1944
        foreach ($blockParsers as $blockParser) {
382 1917
            if ($blockParser instanceof EnvironmentAwareInterface) {
383
                $blockParser->setEnvironment($this);
384
            }
385
386 1917
            if ($blockParser instanceof ConfigurationAwareInterface) {
387
                $blockParser->setConfiguration($this->config);
388
            }
389
390 1917
            $this->blockParsers[$blockParser->getName()] = $blockParser;
391 1296
        }
392 1944
    }
393
394
    /**
395
     * @param InlineParserInterface[] $inlineParsers
396
     */
397 1944
    private function initializeInlineParsers($inlineParsers)
398
    {
399 1944
        foreach ($inlineParsers as $inlineParser) {
400 1923
            if ($inlineParser instanceof EnvironmentAwareInterface) {
401 1914
                $inlineParser->setEnvironment($this);
402 1276
            }
403
404 1923
            if ($inlineParser instanceof ConfigurationAwareInterface) {
405
                $inlineParser->setConfiguration($this->config);
406
            }
407
408 1923
            $this->inlineParsers[$inlineParser->getName()] = $inlineParser;
409
410 1923
            foreach ($inlineParser->getCharacters() as $character) {
411 1923
                $this->inlineParsersByCharacter[$character][] = $inlineParser;
412 1282
            }
413 1296
        }
414 1944
    }
415
416
    /**
417
     * @param InlineProcessorInterface[] $inlineProcessors
418
     */
419 1944
    private function initializeInlineProcessors($inlineProcessors)
420
    {
421 1944
        foreach ($inlineProcessors as $inlineProcessor) {
422 1917
            $this->inlineProcessors[] = $inlineProcessor;
423
424 1917
            if ($inlineProcessor instanceof ConfigurationAwareInterface) {
425 639
                $inlineProcessor->setConfiguration($this->config);
426
            }
427 1296
        }
428 1944
    }
429
430
    /**
431
     * @param DocumentProcessorInterface[] $documentProcessors
432
     */
433 1944
    private function initializeDocumentProcessors($documentProcessors)
434
    {
435 1944
        foreach ($documentProcessors as $documentProcessor) {
436 3
            $this->documentProcessors[] = $documentProcessor;
437
438 3
            if ($documentProcessor instanceof ConfigurationAwareInterface) {
439 1
                $documentProcessor->setConfiguration($this->config);
440
            }
441 1296
        }
442 1944
    }
443
444
    /**
445
     * @param BlockRendererInterface[] $blockRenderers
446
     */
447 1944
    private function initializeBlockRenderers($blockRenderers)
448
    {
449 1944
        foreach ($blockRenderers as $class => $blockRenderer) {
450 1920
            $this->blockRenderersByClass[$class] = $blockRenderer;
451
452 1920
            if ($blockRenderer instanceof ConfigurationAwareInterface) {
453 1916
                $blockRenderer->setConfiguration($this->config);
454 1276
            }
455 1296
        }
456 1944
    }
457
458
    /**
459
     * @param InlineRendererInterface[] $inlineRenderers
460
     */
461 1944
    private function initializeInlineRenderers($inlineRenderers)
462
    {
463 1944
        foreach ($inlineRenderers as $class => $inlineRenderer) {
464 1920
            $this->inlineRenderersByClass[$class] = $inlineRenderer;
465
466 1920
            if ($inlineRenderer instanceof ConfigurationAwareInterface) {
467 1916
                $inlineRenderer->setConfiguration($this->config);
468 1276
            }
469 1296
        }
470 1944
    }
471
472
    /**
473
     * @return Environment
474
     */
475 1920
    public static function createCommonMarkEnvironment()
476
    {
477 1920
        $environment = new static();
478 1920
        $environment->addExtension(new CommonMarkCoreExtension());
479 1920
        $environment->mergeConfig([
480
            'renderer' => [
481 1280
                'block_separator' => "\n",
482 1280
                'inner_separator' => "\n",
483 1280
                'soft_break'      => "\n",
484 1920
            ],
485 1280
            'safe'               => false, // deprecated option
486 1920
            'html_input'         => self::HTML_INPUT_ALLOW,
487 1280
            'allow_unsafe_links' => true,
488 1280
        ]);
489
490 1920
        return $environment;
491
    }
492
493
    /**
494
     * Regex which matches any character which doesn't indicate an inline element
495
     *
496
     * This allows us to parse multiple non-special characters at once
497
     *
498
     * @return string
499
     */
500 1524
    public function getInlineParserCharacterRegex()
501
    {
502 1524
        return $this->inlineParserCharacterRegex;
503
    }
504
505 1986
    private function buildInlineParserCharacterRegex()
506
    {
507 1986
        $chars = array_keys($this->inlineParsersByCharacter);
508
509 1986
        if (empty($chars)) {
510
            // If no special inline characters exist then parse the whole line
511 63
            $this->inlineParserCharacterRegex = '/^.+$/u';
512 42
        } else {
513
            // Match any character which inline parsers are not interested in
514 1923
            $this->inlineParserCharacterRegex = '/^[^' . preg_quote(implode('', $chars)) . ']+/u';
515
        }
516 1986
    }
517
518
    /**
519
     * @param string $message
520
     *
521
     * @throws \RuntimeException
522
     */
523 1992
    private function assertUninitialized($message)
524
    {
525 1992
        if ($this->extensionsInitialized) {
526 27
            throw new \RuntimeException($message . ' Extensions have already been initialized.');
527
        }
528 1965
    }
529
530
    /**
531
     * @return MiscExtension
532
     */
533 33
    private function getMiscExtension()
534
    {
535 33
        $lastExtension = end($this->extensions);
536 33
        if ($lastExtension !== false && $lastExtension instanceof MiscExtension) {
537 6
            return $lastExtension;
538
        }
539
540 33
        $miscExtension = new MiscExtension();
541 33
        $this->addExtension($miscExtension);
542
543 33
        return $miscExtension;
544
    }
545
}
546