1d4d8fb18SAndreas Gohr<?php 2d4d8fb18SAndreas Gohr 3d4d8fb18SAndreas Gohrnamespace dokuwiki\Parsing; 4d4d8fb18SAndreas Gohr 5*bcaec9f4SAndreas 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 */ 15d4d8fb18SAndreas Gohrclass Parser { 16d4d8fb18SAndreas 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 */ 24*bcaec9f4SAndreas 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 */ 34d4d8fb18SAndreas Gohr public function __construct(Doku_Handler $handler) { 35d4d8fb18SAndreas Gohr $this->handler = $handler; 36d4d8fb18SAndreas Gohr } 37d4d8fb18SAndreas Gohr 38d4d8fb18SAndreas Gohr /** 39d4d8fb18SAndreas Gohr * Adds the base mode and initialized the lexer 40d4d8fb18SAndreas Gohr * 41d4d8fb18SAndreas Gohr * @param Base $BaseMode 42d4d8fb18SAndreas Gohr */ 43d4d8fb18SAndreas Gohr protected function addBaseMode($BaseMode) { 44d4d8fb18SAndreas Gohr $this->modes['base'] = $BaseMode; 45d4d8fb18SAndreas Gohr if(!$this->lexer) { 46d4d8fb18SAndreas Gohr $this->lexer = new Lexer($this->handler, 'base', true); 47d4d8fb18SAndreas Gohr } 48d4d8fb18SAndreas Gohr $this->modes['base']->Lexer = $this->lexer; 49d4d8fb18SAndreas Gohr } 50d4d8fb18SAndreas Gohr 51d4d8fb18SAndreas Gohr /** 52d4d8fb18SAndreas Gohr * Add a new syntax element (mode) to the parser 53d4d8fb18SAndreas Gohr * 54d4d8fb18SAndreas Gohr * PHP preserves order of associative elements 55d4d8fb18SAndreas Gohr * Mode sequence is important 56d4d8fb18SAndreas Gohr * 57d4d8fb18SAndreas Gohr * @param string $name 58d4d8fb18SAndreas Gohr * @param ModeInterface $Mode 59d4d8fb18SAndreas Gohr */ 60d4d8fb18SAndreas Gohr public function addMode($name, ModeInterface $Mode) { 61d4d8fb18SAndreas Gohr if(!isset($this->modes['base'])) { 62d4d8fb18SAndreas Gohr $this->addBaseMode(new Base()); 63d4d8fb18SAndreas Gohr } 64d4d8fb18SAndreas Gohr $Mode->Lexer = $this->lexer; // FIXME should be done by setter 65d4d8fb18SAndreas Gohr $this->modes[$name] = $Mode; 66d4d8fb18SAndreas Gohr } 67d4d8fb18SAndreas Gohr 68d4d8fb18SAndreas Gohr /** 69d4d8fb18SAndreas Gohr * Connect all modes with each other 70d4d8fb18SAndreas Gohr * 71d4d8fb18SAndreas Gohr * This is the last step before actually parsing. 72d4d8fb18SAndreas Gohr */ 73d4d8fb18SAndreas Gohr protected function connectModes() { 74d4d8fb18SAndreas Gohr 75d4d8fb18SAndreas Gohr if($this->connected) { 76d4d8fb18SAndreas Gohr return; 77d4d8fb18SAndreas Gohr } 78d4d8fb18SAndreas Gohr 79d4d8fb18SAndreas Gohr foreach(array_keys($this->modes) as $mode) { 80d4d8fb18SAndreas Gohr // Base isn't connected to anything 81d4d8fb18SAndreas Gohr if($mode == 'base') { 82d4d8fb18SAndreas Gohr continue; 83d4d8fb18SAndreas Gohr } 84d4d8fb18SAndreas Gohr $this->modes[$mode]->preConnect(); 85d4d8fb18SAndreas Gohr 86d4d8fb18SAndreas Gohr foreach(array_keys($this->modes) as $cm) { 87d4d8fb18SAndreas Gohr 88d4d8fb18SAndreas Gohr if($this->modes[$cm]->accepts($mode)) { 89d4d8fb18SAndreas Gohr $this->modes[$mode]->connectTo($cm); 90d4d8fb18SAndreas Gohr } 91d4d8fb18SAndreas Gohr 92d4d8fb18SAndreas Gohr } 93d4d8fb18SAndreas Gohr 94d4d8fb18SAndreas Gohr $this->modes[$mode]->postConnect(); 95d4d8fb18SAndreas Gohr } 96d4d8fb18SAndreas Gohr 97d4d8fb18SAndreas Gohr $this->connected = true; 98d4d8fb18SAndreas Gohr } 99d4d8fb18SAndreas Gohr 100d4d8fb18SAndreas Gohr /** 101d4d8fb18SAndreas Gohr * Parses wiki syntax to instructions 102d4d8fb18SAndreas Gohr * 103d4d8fb18SAndreas Gohr * @param string $doc the wiki syntax text 104d4d8fb18SAndreas Gohr * @return array instructions 105d4d8fb18SAndreas Gohr */ 106d4d8fb18SAndreas Gohr public function parse($doc) { 107d4d8fb18SAndreas Gohr $this->connectModes(); 108d4d8fb18SAndreas Gohr // Normalize CRs and pad doc 109d4d8fb18SAndreas Gohr $doc = "\n" . str_replace("\r\n", "\n", $doc) . "\n"; 110d4d8fb18SAndreas Gohr $this->lexer->parse($doc); 1111b008e87SMichael Große 1121b008e87SMichael Große if (!method_exists($this->handler, 'finalize')) { 1131b008e87SMichael Große /** @deprecated 2019-10 we have a legacy handler from a plugin, assume legacy _finalize exists */ 1141b008e87SMichael Große 115*bcaec9f4SAndreas Gohr DebugHelper::dbgCustomDeprecationEvent( 1161b008e87SMichael Große 'finalize()', 1171b008e87SMichael Große get_class($this->handler) . '::_finalize()', 1181b008e87SMichael Große __METHOD__, 1191b008e87SMichael Große __FILE__, 1201b008e87SMichael Große __LINE__ 1211b008e87SMichael Große ); 1221b008e87SMichael Große $this->handler->_finalize(); 1231b008e87SMichael Große } else { 124d4d8fb18SAndreas Gohr $this->handler->finalize(); 1251b008e87SMichael Große } 126d4d8fb18SAndreas Gohr return $this->handler->calls; 127d4d8fb18SAndreas Gohr } 128d4d8fb18SAndreas Gohr 129d4d8fb18SAndreas Gohr} 130