xref: /dokuwiki/inc/Parsing/Parser.php (revision 9172eccff11a70df61d7aad697f84bbd815147ce)
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     * Accessor for the Handler instance.
40     *
41     * @return Handler
42     */
43    public function getHandler()
44    {
45        return $this->handler;
46    }
47
48    /**
49     * Adds the base mode and initialized the lexer
50     *
51     * @param Base $BaseMode
52     */
53    protected function addBaseMode($BaseMode)
54    {
55        $this->modes['base'] = $BaseMode;
56        if (!$this->lexer) {
57            $this->lexer = new Lexer($this->handler, 'base', true);
58        }
59        $this->modes['base']->Lexer = $this->lexer;
60        $this->handler->registerModeObject('base', $BaseMode);
61    }
62
63    /**
64     * Add a new syntax element (mode) to the parser
65     *
66     * PHP preserves order of associative elements
67     * Mode sequence is important
68     *
69     * @param string $name
70     * @param ModeInterface $Mode
71     */
72    public function addMode($name, ModeInterface $Mode)
73    {
74        if (!isset($this->modes['base'])) {
75            $this->addBaseMode(new Base());
76        }
77        $Mode->Lexer = $this->lexer; // FIXME should be done by setter
78        $this->modes[$name] = $Mode;
79        $this->handler->registerModeObject($name, $Mode);
80    }
81
82    /**
83     * Connect all modes with each other
84     *
85     * This is the last step before actually parsing.
86     */
87    protected function connectModes()
88    {
89
90        if ($this->connected) {
91            return;
92        }
93
94        // Run all preConnect() first so modes can register shared
95        // metadata (e.g. line start markers) before any patterns are built
96        foreach (array_keys($this->modes) as $mode) {
97            if ($mode == 'base') continue;
98            $this->modes[$mode]->preConnect();
99        }
100
101        foreach (array_keys($this->modes) as $mode) {
102            if ($mode == 'base') continue;
103
104            foreach (array_keys($this->modes) as $cm) {
105                if ($this->modes[$cm]->accepts($mode)) {
106                    $this->modes[$mode]->connectTo($cm);
107                }
108            }
109
110            $this->modes[$mode]->postConnect();
111        }
112
113        $this->connected = true;
114    }
115
116    /**
117     * Parses wiki syntax to instructions
118     *
119     * @param string $doc the wiki syntax text
120     * @return array instructions
121     */
122    public function parse($doc)
123    {
124        $this->connectModes();
125        // Normalize CRs and pad doc
126        $doc = "\n" . str_replace("\r\n", "\n", $doc) . "\n";
127        $this->lexer->parse($doc);
128
129        if (!method_exists($this->handler, 'finalize')) {
130            /** @deprecated 2019-10 we have a legacy handler from a plugin, assume legacy _finalize exists */
131
132            DebugHelper::dbgCustomDeprecationEvent(
133                'finalize()',
134                $this->handler::class . '::_finalize()',
135                __METHOD__,
136                __FILE__,
137                __LINE__
138            );
139            $this->handler->_finalize();
140        } else {
141            $this->handler->finalize();
142        }
143        return $this->handler->calls;
144    }
145}
146