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