1<?php 2 3namespace dokuwiki\Parsing; 4 5use dokuwiki\Debug\DebugHelper; 6use Doku_Handler; 7use dokuwiki\Parsing\Lexer\Lexer; 8use dokuwiki\Parsing\ParserMode\Base; 9use dokuwiki\Parsing\ParserMode\ModeInterface; 10 11/** 12 * Sets up the Lexer with modes and points it to the Handler 13 * For an intro to the Lexer see: wiki:parser 14 */ 15class Parser 16{ 17 /** @var Doku_Handler */ 18 protected $handler; 19 20 /** @var Lexer $lexer */ 21 protected $lexer; 22 23 /** @var ModeInterface[] $modes */ 24 protected $modes = []; 25 26 /** @var bool mode connections may only be set up once */ 27 protected $connected = false; 28 29 /** 30 * dokuwiki\Parsing\Doku_Parser constructor. 31 * 32 * @param Doku_Handler $handler 33 */ 34 public function __construct(Doku_Handler $handler) 35 { 36 $this->handler = $handler; 37 } 38 39 /** 40 * Adds the base mode and initialized the lexer 41 * 42 * @param Base $BaseMode 43 */ 44 protected function addBaseMode($BaseMode) 45 { 46 $this->modes['base'] = $BaseMode; 47 if (!$this->lexer) { 48 $this->lexer = new Lexer($this->handler, 'base', true); 49 } 50 $this->modes['base']->Lexer = $this->lexer; 51 } 52 53 /** 54 * Add a new syntax element (mode) to the parser 55 * 56 * PHP preserves order of associative elements 57 * Mode sequence is important 58 * 59 * @param string $name 60 * @param ModeInterface $Mode 61 */ 62 public function addMode($name, ModeInterface $Mode) 63 { 64 if (!isset($this->modes['base'])) { 65 $this->addBaseMode(new Base()); 66 } 67 $Mode->Lexer = $this->lexer; // FIXME should be done by setter 68 $this->modes[$name] = $Mode; 69 } 70 71 /** 72 * Connect all modes with each other 73 * 74 * This is the last step before actually parsing. 75 */ 76 protected function connectModes() 77 { 78 79 if ($this->connected) { 80 return; 81 } 82 83 // Run all preConnect() first so modes can register shared 84 // metadata (e.g. line start markers) before any patterns are built 85 foreach (array_keys($this->modes) as $mode) { 86 if ($mode == 'base') continue; 87 $this->modes[$mode]->preConnect(); 88 } 89 90 foreach (array_keys($this->modes) as $mode) { 91 if ($mode == 'base') continue; 92 93 foreach (array_keys($this->modes) as $cm) { 94 if ($this->modes[$cm]->accepts($mode)) { 95 $this->modes[$mode]->connectTo($cm); 96 } 97 } 98 99 $this->modes[$mode]->postConnect(); 100 } 101 102 $this->connected = true; 103 } 104 105 /** 106 * Parses wiki syntax to instructions 107 * 108 * @param string $doc the wiki syntax text 109 * @return array instructions 110 */ 111 public function parse($doc) 112 { 113 $this->connectModes(); 114 // Normalize CRs and pad doc 115 $doc = "\n" . str_replace("\r\n", "\n", $doc) . "\n"; 116 $this->lexer->parse($doc); 117 118 if (!method_exists($this->handler, 'finalize')) { 119 /** @deprecated 2019-10 we have a legacy handler from a plugin, assume legacy _finalize exists */ 120 121 DebugHelper::dbgCustomDeprecationEvent( 122 'finalize()', 123 $this->handler::class . '::_finalize()', 124 __METHOD__, 125 __FILE__, 126 __LINE__ 127 ); 128 $this->handler->_finalize(); 129 } else { 130 $this->handler->finalize(); 131 } 132 return $this->handler->calls; 133 } 134} 135