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