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