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