xref: /dokuwiki/inc/Parsing/ParserMode/Media.php (revision fe58309edafb067d90f3f40ef3d416100d558a04)
1<?php
2
3namespace dokuwiki\Parsing\ParserMode;
4
5use dokuwiki\Parsing\Handler;
6use dokuwiki\Parsing\Helpers\Media as MediaHelper;
7
8class Media extends AbstractMode
9{
10    /** @inheritdoc */
11    public function getSort()
12    {
13        return 320;
14    }
15
16    /** @inheritdoc */
17    public function connectTo($mode)
18    {
19        // Body is possessive: it can only match up to the first `}}` (neither
20        // alternative accepts `}}`), so it never needs to backtrack to let the
21        // closing `}}` match. Without the possessive quantifier an unclosed
22        // `{{` followed by a large body drives the non-JIT PCRE engine to
23        // retain one backtracking frame per byte — an unbounded memory spike.
24        $this->Lexer->addSpecialPattern("\{\{(?:[^\}]|(?:\}[^\}]))++\}\}", $mode, 'media');
25    }
26
27    /** @inheritdoc */
28    public function handle($match, $state, $pos, Handler $handler)
29    {
30        $p = self::parseMedia($match);
31
32        $handler->addCall(
33            $p['type'],
34            [$p['src'], $p['title'], $p['align'], $p['width'], $p['height'], $p['cache'], $p['linking']],
35            $pos
36        );
37        return true;
38    }
39
40    /**
41     * Parse media syntax into its components
42     *
43     * @param string $match The full media syntax (e.g. {{image.png?200|title}})
44     * @return array Parsed media parameters (type, src, title, align, width, height, cache, linking)
45     */
46    public static function parseMedia($match)
47    {
48        // Strip the opening and closing markup
49        $link = preg_replace(['/^\{\{/', '/\}\}$/u'], '', $match);
50
51        // Split title from URL
52        $link = sexplode('|', $link, 2);
53
54        // Check alignment
55        $ralign = (bool)preg_match('/^ /', $link[0]);
56        $lalign = (bool)preg_match('/ $/', $link[0]);
57
58        // Logic = what's that ;)...
59        if ($lalign & $ralign) {
60            $align = 'center';
61        } elseif ($ralign) {
62            $align = 'right';
63        } elseif ($lalign) {
64            $align = 'left';
65        } else {
66            $align = null;
67        }
68
69        // The title...
70        if (!isset($link[1])) {
71            $link[1] = null;
72        }
73
74        //remove aligning spaces
75        $link[0] = trim($link[0]);
76
77        $p = MediaHelper::parseParameters($link[0]);
78
79        // Explicit param-derived alignment (?left/?right/?center) beats
80        // the whitespace-derived one — it's unambiguous and visible, and
81        // is the only form GfmMedia can express.
82        if ($p['align'] !== null) {
83            $align = $p['align'];
84        }
85
86        if (media_isexternal($p['src']) || link_isinterwiki($p['src'])) {
87            $call = 'externalmedia';
88        } else {
89            $call = 'internalmedia';
90        }
91
92        return [
93            'type' => $call,
94            'src' => $p['src'],
95            'title' => $link[1],
96            'align' => $align,
97            'width' => $p['width'],
98            'height' => $p['height'],
99            'cache' => $p['cache'],
100            'linking' => $p['linking']
101        ];
102    }
103}
104