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