xref: /dokuwiki/inc/Parsing/ParserMode/AbstractFormatting.php (revision 56c730b56ef2746acf3b1a27c69bada1239535bd)
1<?php
2
3namespace dokuwiki\Parsing\ParserMode;
4
5use dokuwiki\Parsing\Handler;
6use dokuwiki\Parsing\ModeRegistry;
7
8/**
9 * Base class for inline formatting modes (bold, italic, underline, etc.)
10 *
11 * Each concrete subclass defines its entry/exit patterns, mode name, and sort order.
12 */
13abstract class AbstractFormatting extends AbstractMode
14{
15    /**
16     * Constructor. Sets up allowed modes for this formatting type.
17     *
18     * Formatting modes accept other formatting, substitutions, and disabled modes,
19     * but exclude themselves to prevent self-nesting (e.g. bold inside bold).
20     */
21    public function __construct()
22    {
23        $self = $this->getModeName();
24        $this->allowedModes = array_filter(
25            ModeRegistry::getInstance()->getModesForCategories([
26                ModeRegistry::CATEGORY_FORMATTING,
27                ModeRegistry::CATEGORY_SUBSTITUTION,
28                ModeRegistry::CATEGORY_DISABLED,
29            ]),
30            static fn($mode) => $mode !== $self
31        );
32    }
33
34    /** @inheritdoc */
35    public function connectTo($mode)
36    {
37        // Can't nest formatting in itself
38        if ($mode === $this->getModeName()) {
39            return;
40        }
41
42        $this->Lexer->addEntryPattern(
43            $this->getEntryPattern(),
44            $mode,
45            $this->getModeName()
46        );
47    }
48
49    /**
50     * @return string The regex pattern that starts this formatting
51     */
52    abstract protected function getEntryPattern(): string;
53
54    /**
55     * @return string The regex pattern that ends this formatting
56     */
57    abstract protected function getExitPattern(): string;
58
59    /**
60     * @return string The mode name used for lexer registration
61     */
62    abstract protected function getModeName(): string;
63
64    /** @inheritdoc */
65    public function postConnect()
66    {
67        $this->Lexer->addExitPattern(
68            $this->getExitPattern(),
69            $this->getModeName()
70        );
71    }
72
73    /** @inheritdoc */
74    public function handle($match, $state, $pos, Handler $handler)
75    {
76        $name = $this->getModeName();
77        match ($state) {
78            DOKU_LEXER_ENTER => $handler->addCall($name . '_open', [], $pos),
79            DOKU_LEXER_EXIT => $handler->addCall($name . '_close', [], $pos),
80            DOKU_LEXER_UNMATCHED => $handler->addCall('cdata', [$match], $pos),
81            default => true,
82        };
83        return true;
84    }
85}
86