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