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_SUBSTITION, 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 /** 65 * @return string The name used for emitted open/close handler instructions 66 * 67 * Defaults to the mode name. Override in subclasses where the emitted 68 * instruction should differ from the lexer mode name (e.g. Gfm modes 69 * that share instructions with a DW counterpart). 70 */ 71 protected function getInstructionName(): string 72 { 73 return $this->getModeName(); 74 } 75 76 /** @inheritdoc */ 77 public function postConnect() 78 { 79 $this->Lexer->addExitPattern( 80 $this->getExitPattern(), 81 $this->getModeName() 82 ); 83 } 84 85 /** @inheritdoc */ 86 public function handle($match, $state, $pos, Handler $handler) 87 { 88 $name = $this->getInstructionName(); 89 match ($state) { 90 DOKU_LEXER_ENTER => $handler->addCall($name . '_open', [], $pos), 91 DOKU_LEXER_EXIT => $handler->addCall($name . '_close', [], $pos), 92 DOKU_LEXER_UNMATCHED => $handler->addCall('cdata', [$match], $pos), 93 default => true, 94 }; 95 return true; 96 } 97} 98