Environment   C
last analyzed

Complexity

Total Complexity 57

Size/Duplication

Total Lines 519
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 95.7%

Importance

Changes 0
Metric Value
wmc 57
lcom 1
cbo 7
dl 0
loc 519
ccs 178
cts 186
cp 0.957
rs 5.04
c 0
b 0
f 0

34 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A mergeConfig() 0 6 1
A setConfig() 0 6 1
A getConfig() 0 4 1
A addBlockParser() 0 8 1
A addInlineParser() 0 8 1
A addInlineProcessor() 0 8 1
A addDocumentProcessor() 0 8 1
A addBlockRenderer() 0 8 1
A addInlineRenderer() 0 8 1
A getBlockParsers() 0 6 1
A getInlineParser() 0 6 1
A getInlineParsers() 0 6 1
A getInlineParsersForCharacter() 0 10 2
A getInlineProcessors() 0 6 1
A getDocumentProcessors() 0 6 1
A getBlockRendererForClass() 0 10 2
A getInlineRendererForClass() 0 10 2
A createInlineParserEngine() 0 6 1
A getExtensions() 0 4 1
A addExtension() 0 8 1
A initializeExtensions() 0 18 3
A initializeExtension() 0 9 1
A initalizeBlockParsers() 0 14 4
A initializeInlineParsers() 0 18 5
A initializeInlineProcessors() 0 10 3
A initializeDocumentProcessors() 0 10 3
A initializeBlockRenderers() 0 10 3
A initializeInlineRenderers() 0 10 3
A createCommonMarkEnvironment() 0 18 1
A getInlineParserCharacterRegex() 0 4 1
A buildInlineParserCharacterRegex() 0 12 2
A assertUninitialized() 0 6 2
A getMiscExtension() 0 12 2

How to fix   Complexity   

Complex Class

Complex classes like Environment 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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

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 2052
    public function __construct(array $config = [])
90
    {
91 2052
        $this->config = new Configuration($config);
92 2052
    }
93
94
    /**
95
     * @param array $config
96
     */
97 1953
    public function mergeConfig(array $config = [])
98 1
    {
99 1953
        $this->assertUninitialized('Failed to modify configuration.');
100
101 1950
        $this->config->mergeConfig($config);
102 1950
    }
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 1959
    public function getConfig($key = null, $default = null)
121
    {
122 1959
        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 18
    public function addInlineParser(InlineParserInterface $parser)
145
    {
146 18
        $this->assertUninitialized('Failed to add inline parser.');
147
148 15
        $this->getMiscExtension()->addInlineParser($parser);
149
150 15
        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 1950
    public function getBlockParsers()
215
    {
216 1950
        $this->initializeExtensions();
217
218 1950
        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 9
    public function getInlineParsers()
237
    {
238 9
        $this->initializeExtensions();
239
240 9
        return $this->inlineParsers;
241
    }
242
243
    /**
244
     * @param string $character
245
     *
246
     * @return InlineParserInterface[]
247
     */
248 1665
    public function getInlineParsersForCharacter($character)
249
    {
250 1665
        $this->initializeExtensions();
251
252 1665
        if (!isset($this->inlineParsersByCharacter[$character])) {
253 1545
            return [];
254
        }
255
256 1242
        return $this->inlineParsersByCharacter[$character];
257
    }
258
259
    /**
260
     * @return InlineProcessorInterface[]
261
     */
262 1668
    public function getInlineProcessors()
263
    {
264 1668
        $this->initializeExtensions();
265
266 1668
        return $this->inlineProcessors;
267
    }
268
269
    /**
270
     * @return DocumentProcessorInterface[]
271
     */
272 1947
    public function getDocumentProcessors()
273
    {
274 1947
        $this->initializeExtensions();
275
276 1947
        return $this->documentProcessors;
277
    }
278
279
    /**
280
     * @param string $blockClass
281
     *
282
     * @return BlockRendererInterface|null
283
     */
284 1953
    public function getBlockRendererForClass($blockClass)
285
    {
286 1953
        $this->initializeExtensions();
287
288 1953
        if (!isset($this->blockRenderersByClass[$blockClass])) {
289 6
            return;
290
        }
291
292 1947
        return $this->blockRenderersByClass[$blockClass];
293
    }
294
295
    /**
296
     * @param string $inlineClass
297
     *
298
     * @return InlineRendererInterface|null
299
     */
300 1674
    public function getInlineRendererForClass($inlineClass)
301
    {
302 1674
        $this->initializeExtensions();
303
304 1674
        if (!isset($this->inlineRenderersByClass[$inlineClass])) {
305 9
            return;
306
        }
307
308 1665
        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 1992
    public function addExtension(ExtensionInterface $extension)
336
    {
337 1992
        $this->assertUninitialized('Failed to add extension.');
338
339 1989
        $this->extensions[] = $extension;
340
341 1989
        return $this;
342
    }
343
344 2016
    protected function initializeExtensions()
345
    {
346
        // Only initialize them once
347 2016
        if ($this->extensionsInitialized) {
348 1947
            return;
349
        }
350
351 2016
        $this->extensionsInitialized = true;
352
353
        // Initialize all the registered extensions
354 2016
        foreach ($this->extensions as $extension) {
355 1974
            $this->initializeExtension($extension);
356 672
        }
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 2016
        $this->buildInlineParserCharacterRegex();
361 2016
    }
362
363
    /**
364
     * @param ExtensionInterface $extension
365
     */
366 1974
    protected function initializeExtension(ExtensionInterface $extension)
367
    {
368 1974
        $this->initalizeBlockParsers($extension->getBlockParsers());
369 1974
        $this->initializeInlineParsers($extension->getInlineParsers());
370 1974
        $this->initializeInlineProcessors($extension->getInlineProcessors());
371 1974
        $this->initializeDocumentProcessors($extension->getDocumentProcessors());
372 1974
        $this->initializeBlockRenderers($extension->getBlockRenderers());
373 1974
        $this->initializeInlineRenderers($extension->getInlineRenderers());
374 1974
    }
375
376
    /**
377
     * @param BlockParserInterface[] $blockParsers
378
     */
379 1974
    private function initalizeBlockParsers($blockParsers)
380
    {
381 1974
        foreach ($blockParsers as $blockParser) {
382 1944
            if ($blockParser instanceof EnvironmentAwareInterface) {
383
                $blockParser->setEnvironment($this);
384
            }
385
386 1944
            if ($blockParser instanceof ConfigurationAwareInterface) {
387
                $blockParser->setConfiguration($this->config);
388
            }
389
390 1944
            $this->blockParsers[$blockParser->getName()] = $blockParser;
391 658
        }
392 1974
    }
393
394
    /**
395
     * @param InlineParserInterface[] $inlineParsers
396
     */
397 1974
    private function initializeInlineParsers($inlineParsers)
398
    {
399 1974
        foreach ($inlineParsers as $inlineParser) {
400 1953
            if ($inlineParser instanceof EnvironmentAwareInterface) {
401 1941
                $inlineParser->setEnvironment($this);
402 647
            }
403
404 1953
            if ($inlineParser instanceof ConfigurationAwareInterface) {
405
                $inlineParser->setConfiguration($this->config);
406
            }
407
408 1953
            $this->inlineParsers[$inlineParser->getName()] = $inlineParser;
409
410 1953
            foreach ($inlineParser->getCharacters() as $character) {
411 1953
                $this->inlineParsersByCharacter[$character][] = $inlineParser;
412 651
            }
413 658
        }
414 1974
    }
415
416
    /**
417
     * @param InlineProcessorInterface[] $inlineProcessors
418
     */
419 1974
    private function initializeInlineProcessors($inlineProcessors)
420
    {
421 1974
        foreach ($inlineProcessors as $inlineProcessor) {
422 1944
            $this->inlineProcessors[] = $inlineProcessor;
423
424 1944
            if ($inlineProcessor instanceof ConfigurationAwareInterface) {
425 1296
                $inlineProcessor->setConfiguration($this->config);
426
            }
427 658
        }
428 1974
    }
429
430
    /**
431
     * @param DocumentProcessorInterface[] $documentProcessors
432
     */
433 1974
    private function initializeDocumentProcessors($documentProcessors)
434
    {
435 1974
        foreach ($documentProcessors as $documentProcessor) {
436 3
            $this->documentProcessors[] = $documentProcessor;
437
438 3
            if ($documentProcessor instanceof ConfigurationAwareInterface) {
439 2
                $documentProcessor->setConfiguration($this->config);
440
            }
441 658
        }
442 1974
    }
443
444
    /**
445
     * @param BlockRendererInterface[] $blockRenderers
446
     */
447 1974
    private function initializeBlockRenderers($blockRenderers)
448
    {
449 1974
        foreach ($blockRenderers as $class => $blockRenderer) {
450 1947
            $this->blockRenderersByClass[$class] = $blockRenderer;
451
452 1947
            if ($blockRenderer instanceof ConfigurationAwareInterface) {
453 1945
                $blockRenderer->setConfiguration($this->config);
454 647
            }
455 658
        }
456 1974
    }
457
458
    /**
459
     * @param InlineRendererInterface[] $inlineRenderers
460
     */
461 1974
    private function initializeInlineRenderers($inlineRenderers)
462
    {
463 1974
        foreach ($inlineRenderers as $class => $inlineRenderer) {
464 1947
            $this->inlineRenderersByClass[$class] = $inlineRenderer;
465
466 1947
            if ($inlineRenderer instanceof ConfigurationAwareInterface) {
467 1945
                $inlineRenderer->setConfiguration($this->config);
468 647
            }
469 658
        }
470 1974
    }
471
472
    /**
473
     * @return Environment
474
     */
475 1947
    public static function createCommonMarkEnvironment()
476
    {
477 1947
        $environment = new static();
478 1947
        $environment->addExtension(new CommonMarkCoreExtension());
479 1947
        $environment->mergeConfig([
480 1298
            'renderer' => [
481 649
                'block_separator' => "\n",
482 649
                'inner_separator' => "\n",
483 649
                'soft_break'      => "\n",
484 649
            ],
485 649
            'safe'               => false, // deprecated option
486 1947
            'html_input'         => self::HTML_INPUT_ALLOW,
487 649
            'allow_unsafe_links' => true,
488 1947
            'max_nesting_level'  => INF,
489 649
        ]);
490
491 1947
        return $environment;
492
    }
493
494
    /**
495
     * Regex which matches any character which doesn't indicate an inline element
496
     *
497
     * This allows us to parse multiple non-special characters at once
498
     *
499
     * @return string
500
     */
501 1548
    public function getInlineParserCharacterRegex()
502
    {
503 1548
        return $this->inlineParserCharacterRegex;
504
    }
505
506 2016
    private function buildInlineParserCharacterRegex()
507
    {
508 2016
        $chars = array_keys($this->inlineParsersByCharacter);
509
510 2016
        if (empty($chars)) {
511
            // If no special inline characters exist then parse the whole line
512 63
            $this->inlineParserCharacterRegex = '/^.+$/u';
513 21
        } else {
514
            // Match any character which inline parsers are not interested in
515 1953
            $this->inlineParserCharacterRegex = '/^[^' . preg_quote(implode('', $chars), '/') . ']+/u';
516
        }
517 2016
    }
518
519
    /**
520
     * @param string $message
521
     *
522
     * @throws \RuntimeException
523
     */
524 2022
    private function assertUninitialized($message)
525
    {
526 2022
        if ($this->extensionsInitialized) {
527 27
            throw new \RuntimeException($message . ' Extensions have already been initialized.');
528
        }
529 1995
    }
530
531
    /**
532
     * @return MiscExtension
533
     */
534 36
    private function getMiscExtension()
535
    {
536 36
        $lastExtension = end($this->extensions);
537 36
        if ($lastExtension instanceof MiscExtension) {
538 6
            return $lastExtension;
539
        }
540
541 36
        $miscExtension = new MiscExtension();
542 36
        $this->addExtension($miscExtension);
543
544 36
        return $miscExtension;
545
    }
546
}
547