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