Environment   C
last analyzed

Coupling/Cohesion

Components 1
Dependencies 7

Complexity

Total Complexity 58

Size/Duplication

Total Lines 518
Duplicated Lines 0 %

Test Coverage

Coverage 94.57%

Importance

Changes 0
Metric Value
wmc 58
lcom 1
cbo 7
dl 0
loc 518
ccs 174
cts 184
cp 0.9457
rs 6.3005
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 getInlineParsers() 0 6 1
A getInlineParsersForCharacter() 0 10 2
A getInlineProcessors() 0 6 1
A getExtensions() 0 4 1
A getInlineParserCharacterRegex() 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 getDocumentProcessors() 0 6 1
A getBlockRendererForClass() 0 10 2
A getInlineRendererForClass() 0 10 2
A createInlineParserEngine() 0 6 1
A addExtension() 0 8 1
A initializeExtensions() 0 18 3
A initializeExtension() 0 9 1
A initalizeBlockParsers() 0 14 4
B 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 17 1
A buildInlineParserCharacterRegex() 0 12 2
A assertUninitialized() 0 6 2
A getMiscExtension() 0 12 3

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