1d4d8fb18SAndreas Gohr<?php 2d4d8fb18SAndreas Gohr 3d4d8fb18SAndreas Gohrnamespace dokuwiki\Parsing; 4d4d8fb18SAndreas Gohr 5bcaec9f4SAndreas Gohruse dokuwiki\Debug\DebugHelper; 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 */ 148c7c53b0SAndreas Gohrclass Parser 158c7c53b0SAndreas Gohr{ 16*71096e46SAndreas Gohr /** @var Handler */ 17d4d8fb18SAndreas Gohr protected $handler; 18d4d8fb18SAndreas Gohr 19d4d8fb18SAndreas Gohr /** @var Lexer $lexer */ 20d4d8fb18SAndreas Gohr protected $lexer; 21d4d8fb18SAndreas Gohr 22d4d8fb18SAndreas Gohr /** @var ModeInterface[] $modes */ 23bcaec9f4SAndreas Gohr protected $modes = []; 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 * 31*71096e46SAndreas Gohr * @param Handler $handler 32d4d8fb18SAndreas Gohr */ 33*71096e46SAndreas Gohr public function __construct(Handler $handler) 34d868eb89SAndreas Gohr { 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 */ 43d868eb89SAndreas Gohr protected function addBaseMode($BaseMode) 44d868eb89SAndreas Gohr { 45d4d8fb18SAndreas Gohr $this->modes['base'] = $BaseMode; 46d4d8fb18SAndreas Gohr if (!$this->lexer) { 47d4d8fb18SAndreas Gohr $this->lexer = new Lexer($this->handler, 'base', true); 48d4d8fb18SAndreas Gohr } 49d4d8fb18SAndreas Gohr $this->modes['base']->Lexer = $this->lexer; 50*71096e46SAndreas Gohr $this->handler->registerModeObject('base', $BaseMode); 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 */ 62d868eb89SAndreas Gohr public function addMode($name, ModeInterface $Mode) 63d868eb89SAndreas 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; 69*71096e46SAndreas Gohr $this->handler->registerModeObject($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 */ 77d868eb89SAndreas Gohr protected function connectModes() 78d868eb89SAndreas Gohr { 79d4d8fb18SAndreas Gohr 80d4d8fb18SAndreas Gohr if ($this->connected) { 81d4d8fb18SAndreas Gohr return; 82d4d8fb18SAndreas Gohr } 83d4d8fb18SAndreas Gohr 847958e698SAndreas Gohr // Run all preConnect() first so modes can register shared 857958e698SAndreas Gohr // metadata (e.g. line start markers) before any patterns are built 86d4d8fb18SAndreas Gohr foreach (array_keys($this->modes) as $mode) { 877958e698SAndreas Gohr if ($mode == 'base') continue; 88d4d8fb18SAndreas Gohr $this->modes[$mode]->preConnect(); 897958e698SAndreas Gohr } 907958e698SAndreas Gohr 917958e698SAndreas Gohr foreach (array_keys($this->modes) as $mode) { 927958e698SAndreas Gohr if ($mode == 'base') continue; 93d4d8fb18SAndreas Gohr 94d4d8fb18SAndreas Gohr foreach (array_keys($this->modes) as $cm) { 95d4d8fb18SAndreas Gohr if ($this->modes[$cm]->accepts($mode)) { 96d4d8fb18SAndreas Gohr $this->modes[$mode]->connectTo($cm); 97d4d8fb18SAndreas Gohr } 98d4d8fb18SAndreas Gohr } 99d4d8fb18SAndreas Gohr 100d4d8fb18SAndreas Gohr $this->modes[$mode]->postConnect(); 101d4d8fb18SAndreas Gohr } 102d4d8fb18SAndreas Gohr 103d4d8fb18SAndreas Gohr $this->connected = true; 104d4d8fb18SAndreas Gohr } 105d4d8fb18SAndreas Gohr 106d4d8fb18SAndreas Gohr /** 107d4d8fb18SAndreas Gohr * Parses wiki syntax to instructions 108d4d8fb18SAndreas Gohr * 109d4d8fb18SAndreas Gohr * @param string $doc the wiki syntax text 110d4d8fb18SAndreas Gohr * @return array instructions 111d4d8fb18SAndreas Gohr */ 112d868eb89SAndreas Gohr public function parse($doc) 113d868eb89SAndreas Gohr { 114d4d8fb18SAndreas Gohr $this->connectModes(); 115d4d8fb18SAndreas Gohr // Normalize CRs and pad doc 116d4d8fb18SAndreas Gohr $doc = "\n" . str_replace("\r\n", "\n", $doc) . "\n"; 117d4d8fb18SAndreas Gohr $this->lexer->parse($doc); 1181b008e87SMichael Große 1191b008e87SMichael Große if (!method_exists($this->handler, 'finalize')) { 1201b008e87SMichael Große /** @deprecated 2019-10 we have a legacy handler from a plugin, assume legacy _finalize exists */ 1211b008e87SMichael Große 122bcaec9f4SAndreas Gohr DebugHelper::dbgCustomDeprecationEvent( 1231b008e87SMichael Große 'finalize()', 124093fe67eSAndreas Gohr $this->handler::class . '::_finalize()', 1251b008e87SMichael Große __METHOD__, 1261b008e87SMichael Große __FILE__, 1271b008e87SMichael Große __LINE__ 1281b008e87SMichael Große ); 1291b008e87SMichael Große $this->handler->_finalize(); 1301b008e87SMichael Große } else { 131d4d8fb18SAndreas Gohr $this->handler->finalize(); 1321b008e87SMichael Große } 133d4d8fb18SAndreas Gohr return $this->handler->calls; 134d4d8fb18SAndreas Gohr } 135d4d8fb18SAndreas Gohr} 136