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