1<?php 2 3namespace dokuwiki\Parsing; 4 5use dokuwiki\Debug\DebugHelper; 6use dokuwiki\Parsing\Lexer\Lexer; 7use dokuwiki\Parsing\ParserMode\Base; 8use dokuwiki\Parsing\ParserMode\ModeInterface; 9 10/** 11 * Sets up the Lexer with modes and points it to the Handler 12 * For an intro to the Lexer see: wiki:parser 13 */ 14class Parser 15{ 16 /** @var Handler */ 17 protected $handler; 18 19 /** @var Lexer $lexer */ 20 protected $lexer; 21 22 /** @var ModeInterface[] $modes */ 23 protected $modes = []; 24 25 /** @var bool mode connections may only be set up once */ 26 protected $connected = false; 27 28 /** 29 * dokuwiki\Parsing\Doku_Parser constructor. 30 * 31 * @param Handler $handler 32 */ 33 public function __construct(Handler $handler) 34 { 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 { 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 $this->handler->registerModeObject('base', $BaseMode); 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 $this->handler->registerModeObject($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 // Run all preConnect() first so modes can register shared 85 // metadata (e.g. line start markers) before any patterns are built 86 foreach (array_keys($this->modes) as $mode) { 87 if ($mode == 'base') continue; 88 $this->modes[$mode]->preConnect(); 89 } 90 91 foreach (array_keys($this->modes) as $mode) { 92 if ($mode == 'base') continue; 93 94 foreach (array_keys($this->modes) as $cm) { 95 if ($this->modes[$cm]->accepts($mode)) { 96 $this->modes[$mode]->connectTo($cm); 97 } 98 } 99 100 $this->modes[$mode]->postConnect(); 101 } 102 103 $this->connected = true; 104 } 105 106 /** 107 * Parses wiki syntax to instructions 108 * 109 * @param string $doc the wiki syntax text 110 * @return array instructions 111 */ 112 public function parse($doc) 113 { 114 $this->connectModes(); 115 // Normalize CRs and pad doc 116 $doc = "\n" . str_replace("\r\n", "\n", $doc) . "\n"; 117 $this->lexer->parse($doc); 118 119 if (!method_exists($this->handler, 'finalize')) { 120 /** @deprecated 2019-10 we have a legacy handler from a plugin, assume legacy _finalize exists */ 121 122 DebugHelper::dbgCustomDeprecationEvent( 123 'finalize()', 124 $this->handler::class . '::_finalize()', 125 __METHOD__, 126 __FILE__, 127 __LINE__ 128 ); 129 $this->handler->_finalize(); 130 } else { 131 $this->handler->finalize(); 132 } 133 return $this->handler->calls; 134 } 135} 136