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 * Formatting modes accept other formatting, substitutions, and disabled modes. 17 * 18 * @inheritdoc 19 */ 20 protected function allowedCategories(): array 21 { 22 return [ 23 ModeRegistry::CATEGORY_FORMATTING, 24 ModeRegistry::CATEGORY_SUBSTITUTION, 25 ModeRegistry::CATEGORY_DISABLED, 26 ]; 27 } 28 29 /** 30 * Exclude self to prevent self-nesting (e.g. bold inside bold). 31 * 32 * @inheritdoc 33 */ 34 protected function filterAllowedModes(array $modes): array 35 { 36 $self = $this->getModeName(); 37 return array_values(array_filter($modes, static fn($mode) => $mode !== $self)); 38 } 39 40 /** @inheritdoc */ 41 public function connectTo($mode) 42 { 43 // Can't nest formatting in itself 44 if ($mode === $this->getModeName()) { 45 return; 46 } 47 48 $this->Lexer->addEntryPattern( 49 $this->getEntryPattern(), 50 $mode, 51 $this->getModeName() 52 ); 53 } 54 55 /** 56 * @return string The regex pattern that starts this formatting 57 */ 58 abstract protected function getEntryPattern(): string; 59 60 /** 61 * @return string The regex pattern that ends this formatting 62 */ 63 abstract protected function getExitPattern(): string; 64 65 /** 66 * @return string The mode name used for lexer registration 67 */ 68 abstract protected function getModeName(): string; 69 70 /** 71 * @return string The name used for emitted open/close handler instructions 72 * 73 * Defaults to the mode name. Override in subclasses where the emitted 74 * instruction should differ from the lexer mode name (e.g. Gfm modes 75 * that share instructions with a DW counterpart). 76 */ 77 protected function getInstructionName(): string 78 { 79 return $this->getModeName(); 80 } 81 82 /** @inheritdoc */ 83 public function postConnect() 84 { 85 $this->Lexer->addExitPattern( 86 $this->getExitPattern(), 87 $this->getModeName() 88 ); 89 } 90 91 /** @inheritdoc */ 92 public function handle($match, $state, $pos, Handler $handler) 93 { 94 $name = $this->getInstructionName(); 95 match ($state) { 96 DOKU_LEXER_ENTER => $handler->addCall($name . '_open', [], $pos), 97 DOKU_LEXER_EXIT => $handler->addCall($name . '_close', [], $pos), 98 DOKU_LEXER_UNMATCHED => $handler->addCall('cdata', [$match], $pos), 99 default => true, 100 }; 101 return true; 102 } 103} 104