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