xref: /dokuwiki/inc/Parsing/Parser.php (revision 8c7c53b0321a3cd3116b8d3b2ad27863a38dece7)
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