xref: /dokuwiki/inc/Parsing/Parser.php (revision 71096e46fcbfaeaa808667aba794e77fe2780169)
1d4d8fb18SAndreas Gohr<?php
2d4d8fb18SAndreas Gohr
3d4d8fb18SAndreas Gohrnamespace dokuwiki\Parsing;
4d4d8fb18SAndreas Gohr
5bcaec9f4SAndreas Gohruse dokuwiki\Debug\DebugHelper;
6d4d8fb18SAndreas Gohruse dokuwiki\Parsing\Lexer\Lexer;
7d4d8fb18SAndreas Gohruse dokuwiki\Parsing\ParserMode\Base;
8d4d8fb18SAndreas Gohruse dokuwiki\Parsing\ParserMode\ModeInterface;
9d4d8fb18SAndreas Gohr
10d4d8fb18SAndreas Gohr/**
11d4d8fb18SAndreas Gohr * Sets up the Lexer with modes and points it to the Handler
12d4d8fb18SAndreas Gohr * For an intro to the Lexer see: wiki:parser
13d4d8fb18SAndreas Gohr */
148c7c53b0SAndreas Gohrclass Parser
158c7c53b0SAndreas Gohr{
16*71096e46SAndreas Gohr    /** @var Handler */
17d4d8fb18SAndreas Gohr    protected $handler;
18d4d8fb18SAndreas Gohr
19d4d8fb18SAndreas Gohr    /** @var Lexer $lexer */
20d4d8fb18SAndreas Gohr    protected $lexer;
21d4d8fb18SAndreas Gohr
22d4d8fb18SAndreas Gohr    /** @var ModeInterface[] $modes */
23bcaec9f4SAndreas Gohr    protected $modes = [];
24d4d8fb18SAndreas Gohr
25d4d8fb18SAndreas Gohr    /** @var bool mode connections may only be set up once */
26d4d8fb18SAndreas Gohr    protected $connected = false;
27d4d8fb18SAndreas Gohr
28d4d8fb18SAndreas Gohr    /**
29d4d8fb18SAndreas Gohr     * dokuwiki\Parsing\Doku_Parser constructor.
30d4d8fb18SAndreas Gohr     *
31*71096e46SAndreas Gohr     * @param Handler $handler
32d4d8fb18SAndreas Gohr     */
33*71096e46SAndreas Gohr    public function __construct(Handler $handler)
34d868eb89SAndreas Gohr    {
35d4d8fb18SAndreas Gohr        $this->handler = $handler;
36d4d8fb18SAndreas Gohr    }
37d4d8fb18SAndreas Gohr
38d4d8fb18SAndreas Gohr    /**
39d4d8fb18SAndreas Gohr     * Adds the base mode and initialized the lexer
40d4d8fb18SAndreas Gohr     *
41d4d8fb18SAndreas Gohr     * @param Base $BaseMode
42d4d8fb18SAndreas Gohr     */
43d868eb89SAndreas Gohr    protected function addBaseMode($BaseMode)
44d868eb89SAndreas Gohr    {
45d4d8fb18SAndreas Gohr        $this->modes['base'] = $BaseMode;
46d4d8fb18SAndreas Gohr        if (!$this->lexer) {
47d4d8fb18SAndreas Gohr            $this->lexer = new Lexer($this->handler, 'base', true);
48d4d8fb18SAndreas Gohr        }
49d4d8fb18SAndreas Gohr        $this->modes['base']->Lexer = $this->lexer;
50*71096e46SAndreas Gohr        $this->handler->registerModeObject('base', $BaseMode);
51d4d8fb18SAndreas Gohr    }
52d4d8fb18SAndreas Gohr
53d4d8fb18SAndreas Gohr    /**
54d4d8fb18SAndreas Gohr     * Add a new syntax element (mode) to the parser
55d4d8fb18SAndreas Gohr     *
56d4d8fb18SAndreas Gohr     * PHP preserves order of associative elements
57d4d8fb18SAndreas Gohr     * Mode sequence is important
58d4d8fb18SAndreas Gohr     *
59d4d8fb18SAndreas Gohr     * @param string $name
60d4d8fb18SAndreas Gohr     * @param ModeInterface $Mode
61d4d8fb18SAndreas Gohr     */
62d868eb89SAndreas Gohr    public function addMode($name, ModeInterface $Mode)
63d868eb89SAndreas Gohr    {
64d4d8fb18SAndreas Gohr        if (!isset($this->modes['base'])) {
65d4d8fb18SAndreas Gohr            $this->addBaseMode(new Base());
66d4d8fb18SAndreas Gohr        }
67d4d8fb18SAndreas Gohr        $Mode->Lexer = $this->lexer; // FIXME should be done by setter
68d4d8fb18SAndreas Gohr        $this->modes[$name] = $Mode;
69*71096e46SAndreas Gohr        $this->handler->registerModeObject($name, $Mode);
70d4d8fb18SAndreas Gohr    }
71d4d8fb18SAndreas Gohr
72d4d8fb18SAndreas Gohr    /**
73d4d8fb18SAndreas Gohr     * Connect all modes with each other
74d4d8fb18SAndreas Gohr     *
75d4d8fb18SAndreas Gohr     * This is the last step before actually parsing.
76d4d8fb18SAndreas Gohr     */
77d868eb89SAndreas Gohr    protected function connectModes()
78d868eb89SAndreas Gohr    {
79d4d8fb18SAndreas Gohr
80d4d8fb18SAndreas Gohr        if ($this->connected) {
81d4d8fb18SAndreas Gohr            return;
82d4d8fb18SAndreas Gohr        }
83d4d8fb18SAndreas Gohr
847958e698SAndreas Gohr        // Run all preConnect() first so modes can register shared
857958e698SAndreas Gohr        // metadata (e.g. line start markers) before any patterns are built
86d4d8fb18SAndreas Gohr        foreach (array_keys($this->modes) as $mode) {
877958e698SAndreas Gohr            if ($mode == 'base') continue;
88d4d8fb18SAndreas Gohr            $this->modes[$mode]->preConnect();
897958e698SAndreas Gohr        }
907958e698SAndreas Gohr
917958e698SAndreas Gohr        foreach (array_keys($this->modes) as $mode) {
927958e698SAndreas Gohr            if ($mode == 'base') continue;
93d4d8fb18SAndreas Gohr
94d4d8fb18SAndreas Gohr            foreach (array_keys($this->modes) as $cm) {
95d4d8fb18SAndreas Gohr                if ($this->modes[$cm]->accepts($mode)) {
96d4d8fb18SAndreas Gohr                    $this->modes[$mode]->connectTo($cm);
97d4d8fb18SAndreas Gohr                }
98d4d8fb18SAndreas Gohr            }
99d4d8fb18SAndreas Gohr
100d4d8fb18SAndreas Gohr            $this->modes[$mode]->postConnect();
101d4d8fb18SAndreas Gohr        }
102d4d8fb18SAndreas Gohr
103d4d8fb18SAndreas Gohr        $this->connected = true;
104d4d8fb18SAndreas Gohr    }
105d4d8fb18SAndreas Gohr
106d4d8fb18SAndreas Gohr    /**
107d4d8fb18SAndreas Gohr     * Parses wiki syntax to instructions
108d4d8fb18SAndreas Gohr     *
109d4d8fb18SAndreas Gohr     * @param string $doc the wiki syntax text
110d4d8fb18SAndreas Gohr     * @return array instructions
111d4d8fb18SAndreas Gohr     */
112d868eb89SAndreas Gohr    public function parse($doc)
113d868eb89SAndreas Gohr    {
114d4d8fb18SAndreas Gohr        $this->connectModes();
115d4d8fb18SAndreas Gohr        // Normalize CRs and pad doc
116d4d8fb18SAndreas Gohr        $doc = "\n" . str_replace("\r\n", "\n", $doc) . "\n";
117d4d8fb18SAndreas Gohr        $this->lexer->parse($doc);
1181b008e87SMichael Große
1191b008e87SMichael Große        if (!method_exists($this->handler, 'finalize')) {
1201b008e87SMichael Große            /** @deprecated 2019-10 we have a legacy handler from a plugin, assume legacy _finalize exists */
1211b008e87SMichael Große
122bcaec9f4SAndreas Gohr            DebugHelper::dbgCustomDeprecationEvent(
1231b008e87SMichael Große                'finalize()',
124093fe67eSAndreas Gohr                $this->handler::class . '::_finalize()',
1251b008e87SMichael Große                __METHOD__,
1261b008e87SMichael Große                __FILE__,
1271b008e87SMichael Große                __LINE__
1281b008e87SMichael Große            );
1291b008e87SMichael Große            $this->handler->_finalize();
1301b008e87SMichael Große        } else {
131d4d8fb18SAndreas Gohr            $this->handler->finalize();
1321b008e87SMichael Große        }
133d4d8fb18SAndreas Gohr        return $this->handler->calls;
134d4d8fb18SAndreas Gohr    }
135d4d8fb18SAndreas Gohr}
136