Completed
Branch feature/currentUserRefactoring (c13c1d)
by Schlaefer
09:08
created

Parser::_initParser()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 33
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 19
nc 5
nop 1
dl 0
loc 33
rs 9.0111
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Saito - The Threaded Web Forum
7
 *
8
 * @copyright Copyright (c) the Saito Project Developers
9
 * @link https://github.com/Schlaefer/Saito
10
 * @license http://opensource.org/licenses/MIT
11
 */
12
13
namespace Plugin\BbcodeParser\src\Lib;
14
15
use Cake\Core\Plugin;
16
use Cake\View\Helper;
17
use Plugin\BbcodeParser\src\Lib\jBBCode\Definitions;
18
use Plugin\BbcodeParser\src\Lib\jBBCode\Visitors;
19
use Plugin\BbcodeParser\src\Lib\Processors;
20
use Saito\Markup\MarkupSettings;
21
22
class Parser
23
{
24
    /**
25
     * @var \JBBCode\Parser
26
     */
27
    protected $_Parser;
28
29
    protected $_Preprocessors;
30
31
    protected $_Postprocessors;
32
33
    /**
34
     * @var boolean
35
     */
36
    private $areCombinedClassFilesLoaded = false;
37
38
    /**
39
     * @var array
40
     *
41
     * [
42
     *    <set title> => [
43
     *      <arbitrary definition title> => [ <definition> ]
44
     *    ]
45
     * ]
46
     *
47
     */
48
    protected $_tags = [
49
        'basic' => [
50
            // strong
51
            'b' => [
52
                'type' => 'replacement',
53
                'title' => 'b',
54
                'replacement' => '<strong>{param}</strong>'
55
            ],
56
            // code
57
            'codeWithAttributes' => [
58
                'type' => 'class',
59
                'title' => 'CodeWithAttributes'
60
            ],
61
            'codeWithoutAttributes' => [
62
                'type' => 'class',
63
                'title' => 'CodeWithoutAttributes'
64
            ],
65
            // edit marker
66
            'e' => [
67
                'type' => 'replacement',
68
                'title' => 'e',
69
                'replacement' => '<span class="richtext-editMark"></span>{param}'
70
            ],
71
            // float
72
            'float' => [
73
                'type' => 'replacement',
74
                'title' => 'float',
75
                'replacement' => '<div class="clearfix"></div><div class="richtext-float">{param}</div>'
76
            ],
77
            // email
78
            'email' => [
79
                'type' => 'class',
80
                'title' => 'email'
81
            ],
82
            'emailWithAttributes' => [
83
                'type' => 'class',
84
                'title' => 'emailWithAttributes'
85
            ],
86
            // hr
87
            'hr' => [
88
                'type' => 'replacement',
89
                'title' => 'hr',
90
                'replacement' => '<hr>{param}'
91
            ],
92
            '---' => [
93
                'type' => 'replacement',
94
                'title' => '---',
95
                'replacement' => '<hr>{param}'
96
            ],
97
            // emphasis
98
            'i' => [
99
                'type' => 'replacement',
100
                'title' => 'i',
101
                'replacement' => '<em>{param}</em>'
102
            ],
103
            // list
104
            'list' => [
105
                'type' => 'class',
106
                'title' => 'ulList'
107
            ],
108
            // spoiler
109
            'spoiler' => [
110
                'type' => 'class',
111
                'title' => 'spoiler'
112
            ],
113
            // strike
114
            's' => [
115
                'type' => 'replacement',
116
                'title' => 's',
117
                'replacement' => '<del>{param}</del>',
118
            ],
119
            'strike' => [
120
                'type' => 'replacement',
121
                'title' => 'strike',
122
                'replacement' => '<del>{param}</del>',
123
            ],
124
            // url
125
            'link' => [
126
                'type' => 'class',
127
                'title' => 'link'
128
            ],
129
            'linkWithAttributes' => [
130
                'type' => 'class',
131
                'title' => 'linkWithAttributes'
132
            ],
133
            'url' => [
134
                'type' => 'class',
135
                'title' => 'url'
136
            ],
137
            'urlWithAttributes' => [
138
                'type' => 'class',
139
                'title' => 'urlWithAttributes'
140
            ],
141
            // quotes
142
            'quote' => [
143
                'type' => 'replacement',
144
                'title' => 'quote',
145
                'replacement' => '<blockquote>{param}</blockquote>'
146
            ]
147
        ],
148
        'multimedia' => [
149
            'image' => [
150
                'type' => 'class',
151
                'title' => 'Image'
152
            ],
153
            'imageWithAttributes' => [
154
                'type' => 'class',
155
                'title' => 'ImageWithAttributes'
156
            ],
157
            'html5audio' => [
158
                'type' => 'class',
159
                'title' => 'Html5Audio'
160
            ],
161
            'html5audioWithAttributes' => [
162
                'type' => 'class',
163
                'title' => 'Html5AudioWithAttributes',
164
            ],
165
            'html5video' => [
166
                'type' => 'class',
167
                'title' => 'Html5Video'
168
            ],
169
            'html5videoWithAttributes' => [
170
                'type' => 'class',
171
                'title' => 'Html5VideoWithAttributes'
172
            ],
173
            'upload' => [
174
                'type' => 'class',
175
                'title' => 'Upload'
176
            ],
177
            'uploadWithAttributes' => [
178
                'type' => 'class',
179
                'title' => 'UploadWithAttributes'
180
            ],
181
            'fileWithAttributes' => [
182
                'type' => 'class',
183
                'title' => 'fileWithAttributes',
184
            ]
185
        ],
186
        'embed' => [
187
            'embed' => [
188
                'type' => 'class',
189
                'title' => 'Embed'
190
            ],
191
            'flash' => [
192
                'type' => 'class',
193
                'title' => 'Flash'
194
            ],
195
            'iframe' => [
196
                'type' => 'class',
197
                'title' => 'Iframe'
198
            ],
199
        ],
200
    ];
201
202
    /**
203
     * Initialized parsers
204
     *
205
     * @var array
206
     */
207
    protected $_initializedParsers = [];
208
209
    /**
210
     * @var MarkupSettings cache for app settings
211
     */
212
    protected $_cSettings;
213
214
    /**
215
     * @var Helper Helper usually the ParseHelper
216
     */
217
    protected $_Helper;
218
219
    /**
220
     * Constructor
221
     *
222
     * @param Helper $Helper helper
223
     * @param MarkupSettings $settings settings
224
     */
225
    public function __construct(Helper $Helper, MarkupSettings $settings)
226
    {
227
        $this->_Helper = $Helper;
228
        $this->_cSettings = $settings;
229
    }
230
231
    /**
232
     * Parses BBCode
233
     *
234
     * @param string $string string to parse
235
     * @param array $options options
236
     * - `return` string "html"|"text" result type
237
     * - `multimedia` bool true|false parse or ignore multimedia content
238
     * - `embed` bool true|false parse or ignore embed content
239
     *
240
     * @return mixed|string
241
     */
242
    public function parse($string, array $options = [])
243
    {
244
        $options += [
245
            'embed' => true,
246
            'multimedia' => true,
247
            'return' => 'html',
248
        ];
249
250
        $this->_initParser($options);
251
252
        $string = $this->_Preprocessors->process($string);
253
254
        $this->_Parser->parse($string);
255
256
        $this->_Parser->accept(
257
            new Visitors\JbbCodeNl2BrVisitor($this->_Helper, $this->_cSettings)
258
        );
259
        if ($this->_cSettings->get('autolink')) {
260
            $this->_Parser->accept(
261
                new Visitors\JbbCodeAutolinkVisitor($this->_Helper, $this->_cSettings)
262
            );
263
        }
264
        if ($this->_cSettings->get('smilies')) {
265
            $this->_Parser->accept(
266
                new Visitors\JbbCodeSmileyVisitor($this->_Helper, $this->_cSettings)
267
            );
268
        }
269
270
        switch ($options['return']) {
271
            case 'text':
272
                $html = $this->_Parser->getAsText();
273
                break;
274
            default:
275
                $html = $this->_Parser->getAsHtml();
276
        }
277
278
        $html = $this->_Postprocessors->process($html);
279
280
        return $html;
281
    }
282
283
    /**
284
     * Init parser
285
     *
286
     * @param array $options merged MarkupSettings and parse-run-option
287
     *
288
     * @return void
289
     * @throws \Exception
290
     */
291
    protected function _initParser($options)
292
    {
293
        // serializing complex objects kills PHP
294
        $serializable = array_filter(
295
            $options,
296
            function ($value) {
297
                return !is_object($value);
298
            }
299
        );
300
        $parserId = md5(serialize($serializable));
301
        if (isset($this->_initializedParsers[$parserId])) {
302
            $this->_Parser = $this->_initializedParsers[$parserId];
303
304
            return;
305
        }
306
307
        $this->_Parser = new \JBBCode\Parser();
308
        $this->_addDefinitionSet('basic', $this->_cSettings);
309
310
        if ($this->_cSettings->get('bbcode_img') && $options['multimedia']) {
311
            $this->_addDefinitionSet('multimedia', $this->_cSettings);
312
        }
313
314
        if ($this->_cSettings->get('bbcode_img') && $options['embed']) {
315
            $this->_addDefinitionSet('embed', $this->_cSettings);
316
        }
317
318
        $this->_Preprocessors = new Processors\BbcodeProcessorCollection();
319
        $this->_Preprocessors->add(new Processors\BbcodePreparePreprocessor($this->_cSettings));
320
        $this->_Postprocessors = new Processors\BbcodeProcessorCollection();
321
        $this->_Postprocessors->add(new Processors\BbcodeQuotePostprocessor($this->_cSettings));
322
323
        $this->_initializedParsers[$parserId] = $this->_Parser;
324
    }
325
326
    /**
327
     * Add definitin set
328
     *
329
     * @param string $set set
330
     * @param MarkupSettings $options options
331
     *
332
     * @return void
333
     * @throws \Exception
334
     */
335
    protected function _addDefinitionSet($set, MarkupSettings $options)
336
    {
337
        $this->loadCombinedClassFiles();
338
339
        foreach ($this->_tags[$set] as $definition) {
340
            $title = $definition['title'];
341
            switch ($definition['type']) {
342
                case 'replacement':
343
                    $builder = new \JBBCode\CodeDefinitionBuilder(
344
                        $title,
345
                        $definition['replacement']
346
                    );
347
                    $this->_Parser->addCodeDefinition($builder->build());
348
                    break;
349
                case 'class':
350
                    $class = '\Plugin\BbcodeParser\src\Lib\jBBCode\Definitions\\' . ucfirst($title);
351
                    $this->_Parser->addCodeDefinition(new $class($this->_Helper, $options));
352
                    break;
353
                default:
354
                    throw new \Exception();
355
            }
356
        }
357
    }
358
359
    /**
360
     * Class combined definition class files before first usage
361
     *
362
     * @return void
363
     */
364
    protected function loadCombinedClassFiles()
365
    {
366
        if ($this->areCombinedClassFilesLoaded) {
367
            return;
368
        }
369
370
        $folder = __DIR__ . '/jBBCode/Definitions/';
371
        require_once $folder . 'JbbCodeDefinitions.php';
372
        require_once $folder . 'JbbHtml5MediaCodeDefinition.php';
373
        require_once $folder . 'JbbCodeCodeDefinition.php';
374
375
        $this->areCombinedClassFilesLoaded = true;
376
    }
377
}
378