Completed
Push — master ( c68737...8a4625 )
by Colin
02:48 queued 11s
created

Environment::getInlineRenderersForClass()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
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\Inline\Parser\InlineParserInterface;
22
use League\CommonMark\Inline\Processor\InlineProcessorInterface;
23
use League\CommonMark\Inline\Renderer\InlineRendererInterface;
24
use League\CommonMark\Util\Configuration;
25
use League\CommonMark\Util\ConfigurationAwareInterface;
26
use League\CommonMark\Util\PrioritizedList;
27
28
final class Environment implements EnvironmentInterface, ConfigurableEnvironmentInterface
29
{
30
    /**
31
     * @var ExtensionInterface[]
32
     */
33
    private $extensions = [];
34
35
    /**
36
     * @var bool
37
     */
38
    private $extensionsInitialized = false;
39
40
    /**
41
     * @var PrioritizedList<BlockParserInterface>
42
     */
43
    private $blockParsers;
44
45
    /**
46
     * @var PrioritizedList<InlineParserInterface>
47
     */
48
    private $inlineParsers;
49
50
    /**
51
     * @var array<string, PrioritizedList<InlineParserInterface>>
52
     */
53
    private $inlineParsersByCharacter = [];
54
55
    /**
56
     * @var PrioritizedList<DocumentProcessorInterface>
57
     */
58
    private $documentProcessors;
59
60
    /**
61
     * @var PrioritizedList<InlineProcessorInterface>
62
     */
63
    private $inlineProcessors;
64
65
    /**
66
     * @var array<string, PrioritizedList<BlockRendererInterface>>
67
     */
68
    private $blockRenderersByClass = [];
69
70
    /**
71
     * @var array<string, PrioritizedList<InlineRendererInterface>>
72
     */
73
    private $inlineRenderersByClass = [];
74
75
    /**
76
     * @var Configuration
77
     */
78
    private $config;
79
80
    /**
81
     * @var string
82
     */
83
    private $inlineParserCharacterRegex;
84
85 2055
    public function __construct(array $config = [])
86
    {
87 2055
        $this->config = new Configuration($config);
88
89 2055
        $this->blockParsers = new PrioritizedList();
90 2055
        $this->inlineParsers = new PrioritizedList();
91 2055
        $this->documentProcessors = new PrioritizedList();
92 2055
        $this->inlineProcessors = new PrioritizedList();
93 2055
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98 1953
    public function mergeConfig(array $config = [])
99
    {
100 1953
        $this->assertUninitialized('Failed to modify configuration.');
101
102 1950
        $this->config->mergeConfig($config);
103 1950
    }
104
105
    /**
106
     * {@inheritdoc}
107
     */
108 6
    public function setConfig(array $config = [])
109
    {
110 6
        $this->assertUninitialized('Failed to modify configuration.');
111
112 3
        $this->config->setConfig($config);
113 3
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 1959
    public function getConfig($key = null, $default = null)
119
    {
120 1959
        return $this->config->getConfig($key, $default);
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126 1953
    public function addBlockParser(BlockParserInterface $parser, $priority = 0)
127
    {
128 1953
        $this->assertUninitialized('Failed to add block parser.');
129
130 1950
        $this->blockParsers->add($parser, $priority);
131 1950
        $this->injectEnvironmentAndConfigurationIfNeeded($parser);
132
133 1950
        return $this;
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139 1953
    public function addInlineParser(InlineParserInterface $parser, $priority = 0)
140
    {
141 1953
        $this->assertUninitialized('Failed to add inline parser.');
142
143 1950
        $this->inlineParsers->add($parser, $priority);
144 1950
        $this->injectEnvironmentAndConfigurationIfNeeded($parser);
145
146 1950
        foreach ($parser->getCharacters() as $character) {
147 1950
            if (!isset($this->inlineParsersByCharacter[$character])) {
148 1950
                $this->inlineParsersByCharacter[$character] = new PrioritizedList();
149
            }
150
151 1950
            $this->inlineParsersByCharacter[$character]->add($parser, $priority);
152
        }
153
154 1950
        return $this;
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160 1950
    public function addInlineProcessor(InlineProcessorInterface $processor, $priority = 0)
161
    {
162 1950
        $this->assertUninitialized('Failed to add inline processor.');
163
164 1947
        $this->inlineProcessors->add($processor, $priority);
165 1947
        $this->injectEnvironmentAndConfigurationIfNeeded($processor);
166
167 1947
        return $this;
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173 9
    public function addDocumentProcessor(DocumentProcessorInterface $processor, $priority = 0)
174
    {
175 9
        $this->assertUninitialized('Failed to add document processor.');
176
177 6
        $this->documentProcessors->add($processor, $priority);
178 6
        $this->injectEnvironmentAndConfigurationIfNeeded($processor);
179
180 6
        return $this;
181
    }
182
183
    /**
184
     * {@inheritdoc}
185
     */
186 1950
    public function addBlockRenderer($blockClass, BlockRendererInterface $blockRenderer, $priority = 0)
187
    {
188 1950
        $this->assertUninitialized('Failed to add block renderer.');
189
190 1947
        if (!isset($this->blockRenderersByClass[$blockClass])) {
191 1947
            $this->blockRenderersByClass[$blockClass] = new PrioritizedList();
192
        }
193
194 1947
        $this->blockRenderersByClass[$blockClass]->add($blockRenderer, $priority);
195 1947
        $this->injectEnvironmentAndConfigurationIfNeeded($blockRenderer);
196
197 1947
        return $this;
198
    }
199
200
    /**
201
     * {@inheritdoc}
202
     */
203 1950
    public function addInlineRenderer($inlineClass, InlineRendererInterface $renderer, $priority = 0)
204
    {
205 1950
        $this->assertUninitialized('Failed to add inline renderer.');
206
207 1947
        if (!isset($this->inlineRenderersByClass[$inlineClass])) {
208 1947
            $this->inlineRenderersByClass[$inlineClass] = new PrioritizedList();
209
        }
210
211 1947
        $this->inlineRenderersByClass[$inlineClass]->add($renderer, $priority);
212 1947
        $this->injectEnvironmentAndConfigurationIfNeeded($renderer);
213
214 1947
        return $this;
215
    }
216
217
    /**
218
     * {@inheritdoc}
219
     */
220 1959
    public function getBlockParsers()
221
    {
222 1959
        $this->initializeExtensions();
223
224 1959
        return $this->blockParsers->getIterator();
225
    }
226
227
    /**
228
     * {@inheritdoc}
229
     */
230 1677
    public function getInlineParsersForCharacter($character)
231
    {
232 1677
        $this->initializeExtensions();
233
234 1677
        if (!isset($this->inlineParsersByCharacter[$character])) {
235 1551
            return [];
236
        }
237
238 1248
        return $this->inlineParsersByCharacter[$character]->getIterator();
239
    }
240
241
    /**
242
     * {@inheritdoc}
243
     */
244 1674
    public function getInlineProcessors()
245
    {
246 1674
        $this->initializeExtensions();
247
248 1674
        return $this->inlineProcessors->getIterator();
249
    }
250
251
    /**
252
     * {@inheritdoc}
253
     */
254 1950
    public function getDocumentProcessors()
255
    {
256 1950
        $this->initializeExtensions();
257
258 1950
        return $this->documentProcessors->getIterator();
259
    }
260
261
    /**
262
     * {@inheritdoc}
263
     */
264 1953
    public function getBlockRenderersForClass($blockClass)
265
    {
266 1953
        $this->initializeExtensions();
267
268 1953
        if (!isset($this->blockRenderersByClass[$blockClass])) {
269 6
            return [];
270
        }
271
272 1947
        return $this->blockRenderersByClass[$blockClass]->getIterator();
273
    }
274
275
    /**
276
     * {@inheritdoc}
277
     */
278 1674
    public function getInlineRenderersForClass($inlineClass)
279
    {
280 1674
        $this->initializeExtensions();
281
282 1674
        if (!isset($this->inlineRenderersByClass[$inlineClass])) {
283 9
            return [];
284
        }
285
286 1665
        return $this->inlineRenderersByClass[$inlineClass]->getIterator();
287
    }
288
289
    /**
290
     * Get all registered extensions
291
     *
292
     * @return ExtensionInterface[]
293
     */
294 12
    public function getExtensions()
295
    {
296 12
        return $this->extensions;
297
    }
298
299
    /**
300
     * Add a single extension
301
     *
302
     * @param ExtensionInterface $extension
303
     *
304
     * @return $this
305
     */
306 1956
    public function addExtension(ExtensionInterface $extension)
307
    {
308 1956
        $this->assertUninitialized('Failed to add extension.');
309
310 1953
        $this->extensions[] = $extension;
311
312 1953
        return $this;
313
    }
314
315 2025
    private function initializeExtensions()
316
    {
317
        // Only initialize them once
318 2025
        if ($this->extensionsInitialized) {
319 1944
            return;
320
        }
321
322
        // Ask all extensions to register their components
323 2025
        foreach ($this->extensions as $extension) {
324 1944
            $extension->register($this);
325
        }
326
327 2025
        $this->extensionsInitialized = true;
328
329
        // Lastly, let's build a regex which matches non-inline characters
330
        // This will enable a huge performance boost with inline parsing
331 2025
        $this->buildInlineParserCharacterRegex();
332 2025
    }
333
334 1983
    private function injectEnvironmentAndConfigurationIfNeeded($object)
335
    {
336 1983
        if ($object instanceof EnvironmentAwareInterface) {
337 1944
            $object->setEnvironment($this);
338
        }
339
340 1983
        if ($object instanceof ConfigurationAwareInterface) {
341 1944
            $object->setConfiguration($this->config);
342
        }
343 1983
    }
344
345
    /**
346
     * @return Environment
347
     */
348 1947
    public static function createCommonMarkEnvironment()
349
    {
350 1947
        $environment = new static();
351 1947
        $environment->addExtension(new CommonMarkCoreExtension());
352 1947
        $environment->mergeConfig([
353 1298
            'renderer' => [
354 649
                'block_separator' => "\n",
355
                'inner_separator' => "\n",
356
                'soft_break'      => "\n",
357
            ],
358
            'safe'               => false, // deprecated option
359 1947
            'html_input'         => self::HTML_INPUT_ALLOW,
360
            'allow_unsafe_links' => true,
361 1947
            'max_nesting_level'  => INF,
362
        ]);
363
364 1947
        return $environment;
365
    }
366
367
    /**
368
     * {@inheritdoc}
369
     */
370 1548
    public function getInlineParserCharacterRegex()
371
    {
372 1548
        return $this->inlineParserCharacterRegex;
373
    }
374
375 2025
    private function buildInlineParserCharacterRegex()
376
    {
377 2025
        $chars = array_keys($this->inlineParsersByCharacter);
378
379 2025
        if (empty($chars)) {
380
            // If no special inline characters exist then parse the whole line
381 75
            $this->inlineParserCharacterRegex = '/^.+$/u';
382
        } else {
383
            // Match any character which inline parsers are not interested in
384 1950
            $this->inlineParserCharacterRegex = '/^[^' . preg_quote(implode('', $chars), '/') . ']+/u';
385
        }
386 2025
    }
387
388
    /**
389
     * @param string $message
390
     *
391
     * @throws \RuntimeException
392
     */
393 2028
    private function assertUninitialized($message)
394
    {
395 2028
        if ($this->extensionsInitialized) {
396 27
            throw new \RuntimeException($message . ' Extensions have already been initialized.');
397
        }
398 2001
    }
399
}
400