Passed
Push — master ( 34ea2d...e24904 )
by Caen
03:11 queued 12s
created

ShortcodeProcessor::preprocess()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Hyde\Framework\Modules\Markdown;
4
5
use Hyde\Framework\Contracts\MarkdownPreProcessorContract;
6
use Hyde\Framework\Contracts\MarkdownShortcodeContract;
7
use Hyde\Framework\Modules\Markdown\Shortcodes\AbstractColoredBlockquote;
8
9
/**
10
 * Handle shortcode processing for Markdown conversions.
11
 *
12
 * The shortcode system has a few limitations, as it is meant to be simple
13
 * by design so that it is easy to understand how the code works, and
14
 * what each shortcode does. Shortcodes are expanded on a per-line basis,
15
 * and do not support multi-line input. Shortcodes are expected to be
16
 * the very first thing on a line. The signature is a static string
17
 * that is used to identify the shortcode. The built-in shortcodes
18
 * do not use regex, as that would make them harder to read.
19
 *
20
 * @see \Hyde\Framework\Testing\Feature\Services\Markdown\ShortcodeProcessorTest
21
 * @phpstan-consistent-constructor
22
 */
23
class ShortcodeProcessor implements MarkdownPreProcessorContract
24
{
25
    /**
26
     * The input Markdown document body.
27
     */
28
    protected string $input;
29
30
    /**
31
     * The processed Markdown document body.
32
     */
33
    protected string $output;
34
35
    /**
36
     * The activated shortcode instances.
37
     */
38
    public array $shortcodes;
39
40
    public function __construct(string $input)
41
    {
42
        $this->input = $input;
43
44
        $this->discoverShortcodes();
45
    }
46
47
    public function processInput(): static
48
    {
49
        $this->output = implode("\n", array_map(function ($line) {
50
            return $this->expandShortcode($line);
51
        }, explode("\n", $this->input)));
52
53
        return $this;
54
    }
55
56
    public function getOutput(): string
57
    {
58
        return $this->output;
59
    }
60
61
    public function run(): string
62
    {
63
        return $this->processInput()->getOutput();
64
    }
65
66
    public static function preprocess(string $input): string
67
    {
68
        return (new static($input))->run();
69
    }
70
71
    protected function discoverShortcodes(): void
72
    {
73
        $this->addShortcodesFromArray(array_merge(
74
            AbstractColoredBlockquote::get(),
75
        ));
76
    }
77
78
    public function addShortcodesFromArray(array $shortcodes): static
79
    {
80
        foreach ($shortcodes as $shortcode) {
81
            $this->addShortcode($shortcode);
82
        }
83
84
        return $this;
85
    }
86
87
    public function addShortcode(MarkdownShortcodeContract $shortcode): static
88
    {
89
        $this->shortcodes[$shortcode::signature()] = $shortcode;
90
91
        return $this;
92
    }
93
94
    protected function expandShortcode(string $line): string
95
    {
96
        return array_key_exists($signature = $this->discoverSignature($line), $this->shortcodes)
97
            ? $this->shortcodes[$signature]::resolve($line)
98
            : $line;
99
    }
100
101
    protected function discoverSignature(string $line): string
102
    {
103
        return str_contains($line, ' ') ? substr($line, 0, strpos($line, ' ')) : $line;
104
    }
105
}
106