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 93 if($this->modes[$cm]->accepts($mode)) { 94 $this->modes[$mode]->connectTo($cm); 95 } 96 97 } 98 99 $this->modes[$mode]->postConnect(); 100 } 101 102 $this->connected = true; 103 } 104 105 /** 106 * Parses wiki syntax to instructions 107 * 108 * @param string $doc the wiki syntax text 109 * @return array instructions 110 */ 111 public function parse($doc) 112 { 113 $this->connectModes(); 114 // Normalize CRs and pad doc 115 $doc = "\n" . str_replace("\r\n", "\n", $doc) . "\n"; 116 $this->lexer->parse($doc); 117 118 if (!method_exists($this->handler, 'finalize')) { 119 /** @deprecated 2019-10 we have a legacy handler from a plugin, assume legacy _finalize exists */ 120 121 DebugHelper::dbgCustomDeprecationEvent( 122 'finalize()', 123 get_class($this->handler) . '::_finalize()', 124 __METHOD__, 125 __FILE__, 126 __LINE__ 127 ); 128 $this->handler->_finalize(); 129 } else { 130 $this->handler->finalize(); 131 } 132 return $this->handler->calls; 133 } 134} 135