xref: /dokuwiki/inc/Parsing/Parser.php (revision d4f83172d9533c4d84f450fe22ef630816b21d75)
1d4d8fb18SAndreas Gohr<?php
2d4d8fb18SAndreas Gohr
3d4d8fb18SAndreas Gohrnamespace dokuwiki\Parsing;
4d4d8fb18SAndreas Gohr
5bcaec9f4SAndreas Gohruse dokuwiki\Debug\DebugHelper;
6d4d8fb18SAndreas Gohruse Doku_Handler;
7d4d8fb18SAndreas Gohruse dokuwiki\Parsing\Lexer\Lexer;
8d4d8fb18SAndreas Gohruse dokuwiki\Parsing\ParserMode\Base;
9d4d8fb18SAndreas Gohruse dokuwiki\Parsing\ParserMode\ModeInterface;
10d4d8fb18SAndreas Gohr
11d4d8fb18SAndreas Gohr/**
12d4d8fb18SAndreas Gohr * Sets up the Lexer with modes and points it to the Handler
13d4d8fb18SAndreas Gohr * For an intro to the Lexer see: wiki:parser
14d4d8fb18SAndreas Gohr */
158c7c53b0SAndreas Gohrclass Parser
168c7c53b0SAndreas Gohr{
17d4d8fb18SAndreas Gohr    /** @var Doku_Handler */
18d4d8fb18SAndreas Gohr    protected $handler;
19d4d8fb18SAndreas Gohr
20d4d8fb18SAndreas Gohr    /** @var Lexer $lexer */
21d4d8fb18SAndreas Gohr    protected $lexer;
22d4d8fb18SAndreas Gohr
23d4d8fb18SAndreas Gohr    /** @var ModeInterface[] $modes */
24bcaec9f4SAndreas Gohr    protected $modes = [];
25d4d8fb18SAndreas Gohr
26d4d8fb18SAndreas Gohr    /** @var bool mode connections may only be set up once */
27d4d8fb18SAndreas Gohr    protected $connected = false;
28d4d8fb18SAndreas Gohr
29d4d8fb18SAndreas Gohr    /**
30d4d8fb18SAndreas Gohr     * dokuwiki\Parsing\Doku_Parser constructor.
31d4d8fb18SAndreas Gohr     *
32d4d8fb18SAndreas Gohr     * @param Doku_Handler $handler
33d4d8fb18SAndreas Gohr     */
34*d868eb89SAndreas Gohr    public function __construct(Doku_Handler $handler)
35*d868eb89SAndreas Gohr    {
36d4d8fb18SAndreas Gohr        $this->handler = $handler;
37d4d8fb18SAndreas Gohr    }
38d4d8fb18SAndreas Gohr
39d4d8fb18SAndreas Gohr    /**
40d4d8fb18SAndreas Gohr     * Adds the base mode and initialized the lexer
41d4d8fb18SAndreas Gohr     *
42d4d8fb18SAndreas Gohr     * @param Base $BaseMode
43d4d8fb18SAndreas Gohr     */
44*d868eb89SAndreas Gohr    protected function addBaseMode($BaseMode)
45*d868eb89SAndreas Gohr    {
46d4d8fb18SAndreas Gohr        $this->modes['base'] = $BaseMode;
47d4d8fb18SAndreas Gohr        if (!$this->lexer) {
48d4d8fb18SAndreas Gohr            $this->lexer = new Lexer($this->handler, 'base', true);
49d4d8fb18SAndreas Gohr        }
50d4d8fb18SAndreas Gohr        $this->modes['base']->Lexer = $this->lexer;
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     */
62*d868eb89SAndreas Gohr    public function addMode($name, ModeInterface $Mode)
63*d868eb89SAndreas 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;
69d4d8fb18SAndreas Gohr    }
70d4d8fb18SAndreas Gohr
71d4d8fb18SAndreas Gohr    /**
72d4d8fb18SAndreas Gohr     * Connect all modes with each other
73d4d8fb18SAndreas Gohr     *
74d4d8fb18SAndreas Gohr     * This is the last step before actually parsing.
75d4d8fb18SAndreas Gohr     */
76*d868eb89SAndreas Gohr    protected function connectModes()
77*d868eb89SAndreas Gohr    {
78d4d8fb18SAndreas Gohr
79d4d8fb18SAndreas Gohr        if ($this->connected) {
80d4d8fb18SAndreas Gohr            return;
81d4d8fb18SAndreas Gohr        }
82d4d8fb18SAndreas Gohr
83d4d8fb18SAndreas Gohr        foreach (array_keys($this->modes) as $mode) {
84d4d8fb18SAndreas Gohr            // Base isn't connected to anything
85d4d8fb18SAndreas Gohr            if ($mode == 'base') {
86d4d8fb18SAndreas Gohr                continue;
87d4d8fb18SAndreas Gohr            }
88d4d8fb18SAndreas Gohr            $this->modes[$mode]->preConnect();
89d4d8fb18SAndreas Gohr
90d4d8fb18SAndreas Gohr            foreach (array_keys($this->modes) as $cm) {
91d4d8fb18SAndreas Gohr                if ($this->modes[$cm]->accepts($mode)) {
92d4d8fb18SAndreas Gohr                    $this->modes[$mode]->connectTo($cm);
93d4d8fb18SAndreas Gohr                }
94d4d8fb18SAndreas Gohr            }
95d4d8fb18SAndreas Gohr
96d4d8fb18SAndreas Gohr            $this->modes[$mode]->postConnect();
97d4d8fb18SAndreas Gohr        }
98d4d8fb18SAndreas Gohr
99d4d8fb18SAndreas Gohr        $this->connected = true;
100d4d8fb18SAndreas Gohr    }
101d4d8fb18SAndreas Gohr
102d4d8fb18SAndreas Gohr    /**
103d4d8fb18SAndreas Gohr     * Parses wiki syntax to instructions
104d4d8fb18SAndreas Gohr     *
105d4d8fb18SAndreas Gohr     * @param string $doc the wiki syntax text
106d4d8fb18SAndreas Gohr     * @return array instructions
107d4d8fb18SAndreas Gohr     */
108*d868eb89SAndreas Gohr    public function parse($doc)
109*d868eb89SAndreas Gohr    {
110d4d8fb18SAndreas Gohr        $this->connectModes();
111d4d8fb18SAndreas Gohr        // Normalize CRs and pad doc
112d4d8fb18SAndreas Gohr        $doc = "\n" . str_replace("\r\n", "\n", $doc) . "\n";
113d4d8fb18SAndreas Gohr        $this->lexer->parse($doc);
1141b008e87SMichael Große
1151b008e87SMichael Große        if (!method_exists($this->handler, 'finalize')) {
1161b008e87SMichael Große            /** @deprecated 2019-10 we have a legacy handler from a plugin, assume legacy _finalize exists */
1171b008e87SMichael Große
118bcaec9f4SAndreas Gohr            DebugHelper::dbgCustomDeprecationEvent(
1191b008e87SMichael Große                'finalize()',
1201b008e87SMichael Große                get_class($this->handler) . '::_finalize()',
1211b008e87SMichael Große                __METHOD__,
1221b008e87SMichael Große                __FILE__,
1231b008e87SMichael Große                __LINE__
1241b008e87SMichael Große            );
1251b008e87SMichael Große            $this->handler->_finalize();
1261b008e87SMichael Große        } else {
127d4d8fb18SAndreas Gohr            $this->handler->finalize();
1281b008e87SMichael Große        }
129d4d8fb18SAndreas Gohr        return $this->handler->calls;
130d4d8fb18SAndreas Gohr    }
131d4d8fb18SAndreas Gohr}
132