Completed
Push — master ( 15e8d5...9ebb39 )
by Colin
04:59
created

Environment::getMiscExtension()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

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