1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @file BBCodeConverter.php |
5
|
|
|
* @brief This file contains the BBCodeConverter class. |
6
|
|
|
* @details |
7
|
|
|
* @author Filippo F. Fadda |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace Converter; |
11
|
|
|
|
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* @brief A rudimental converter that takes as input a BBCode formatted text and converts it to Markdown. |
15
|
|
|
*/ |
16
|
|
|
class BBCodeConverter extends Converter |
17
|
|
|
{ |
18
|
|
|
|
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* @brief Removes BBCode size. |
22
|
|
|
*/ |
23
|
|
|
protected function removeSize() |
24
|
|
|
{ |
25
|
|
|
|
26
|
|
|
$this->text = preg_replace_callback('%\[size=\d*\]([\W\D\w\s]*?)\[/size\]%iu', |
27
|
|
|
|
28
|
|
|
function ($matches) { |
29
|
|
|
return $matches[1]; |
30
|
|
|
}, |
31
|
|
|
|
32
|
|
|
$this->text |
33
|
|
|
); |
34
|
|
|
|
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @brief Removes BBCode center. |
40
|
|
|
*/ |
41
|
|
|
protected function removeCenter() |
42
|
|
|
{ |
43
|
|
|
|
44
|
|
|
$this->text = preg_replace_callback('%\[center\]([\W\D\w\s]*?)\[/center\]%iu', |
45
|
|
|
|
46
|
|
|
function ($matches) { |
47
|
|
|
return $matches[1]; |
48
|
|
|
}, |
49
|
|
|
|
50
|
|
|
$this->text |
51
|
|
|
); |
52
|
|
|
|
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @brief Removes BBCode center. |
58
|
|
|
*/ |
59
|
|
|
protected function removeLeft() |
60
|
|
|
{ |
61
|
|
|
|
62
|
|
|
$this->text = preg_replace_callback('%\[left\]([\W\D\w\s]*?)\[/left\]%iu', |
63
|
|
|
|
64
|
|
|
function ($matches) { |
65
|
|
|
return $matches[1]; |
66
|
|
|
}, |
67
|
|
|
|
68
|
|
|
$this->text |
69
|
|
|
); |
70
|
|
|
|
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @brief Removes BBCode color. |
75
|
|
|
*/ |
76
|
|
|
protected function removeColor() |
77
|
|
|
{ |
78
|
|
|
|
79
|
|
|
$this->text = preg_replace_callback('%\[color[=?][#?][\w]*\]([\W\D\w\s]*?)\[/color\]%iu', |
80
|
|
|
|
81
|
|
|
function ($matches) { |
82
|
|
|
return $matches[1]; |
83
|
|
|
}, |
84
|
|
|
|
85
|
|
|
$this->text |
86
|
|
|
); |
87
|
|
|
|
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* @brief Replaces BBCode bold. |
93
|
|
|
*/ |
94
|
|
View Code Duplication |
protected function replaceBold() |
|
|
|
|
95
|
|
|
{ |
96
|
|
|
|
97
|
|
|
$this->text = preg_replace_callback('%\[b\]([\W\D\w\s]*?)\[/b\]%iu', |
98
|
|
|
|
99
|
|
|
function ($matches) { |
100
|
|
|
return "**" . trim($matches[1], " ") . "**"; |
101
|
|
|
}, |
102
|
|
|
|
103
|
|
|
$this->text |
104
|
|
|
); |
105
|
|
|
|
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* @brief Replaces BBCode italic. |
111
|
|
|
*/ |
112
|
|
View Code Duplication |
protected function replaceItalic() |
|
|
|
|
113
|
|
|
{ |
114
|
|
|
|
115
|
|
|
$this->text = preg_replace_callback('%\[i\]([\W\D\w\s]*?)\[/i\]%iu', |
116
|
|
|
|
117
|
|
|
function ($matches) { |
118
|
|
|
return "*" . trim($matches[1], " ") . "*"; |
119
|
|
|
}, |
120
|
|
|
|
121
|
|
|
$this->text |
122
|
|
|
); |
123
|
|
|
|
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* @brief Replaces BBCode underline. Hoedown support underline. |
129
|
|
|
*/ |
130
|
|
View Code Duplication |
protected function replaceUnderline() |
|
|
|
|
131
|
|
|
{ |
132
|
|
|
|
133
|
|
|
$this->text = preg_replace_callback('%\[u\]([\W\D\w\s]*?)\[/u\]%iu', |
134
|
|
|
|
135
|
|
|
function ($matches) { |
136
|
|
|
return "_" . trim($matches[1], " ") . "_"; |
137
|
|
|
}, |
138
|
|
|
|
139
|
|
|
$this->text |
140
|
|
|
); |
141
|
|
|
|
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* @brief Replaces BBCode strikethrough. |
147
|
|
|
*/ |
148
|
|
View Code Duplication |
protected function replaceStrikethrough() |
|
|
|
|
149
|
|
|
{ |
150
|
|
|
|
151
|
|
|
$this->text = preg_replace_callback('%\[s\]([\W\D\w\s]*?)\[/s\]%iu', |
152
|
|
|
|
153
|
|
|
function ($matches) { |
154
|
|
|
return "~~" . trim($matches[1], " ") . "~~"; |
155
|
|
|
}, |
156
|
|
|
|
157
|
|
|
$this->text |
158
|
|
|
); |
159
|
|
|
|
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* @brief Replaces BBCode lists. |
165
|
|
|
*/ |
166
|
|
|
protected function replaceLists() |
167
|
|
|
{ |
168
|
|
|
|
169
|
|
|
$this->text = preg_replace_callback('%\[list(?P<type>=1)?\](?P<items>[\W\D\w\s]*?)\[/list\]%iu', |
170
|
|
|
|
171
|
|
|
function ($matches) { |
172
|
|
|
$buffer = ""; |
173
|
|
|
|
174
|
|
|
$list = preg_replace('/\s*$|^\s*/mu', '', $matches['items']); |
175
|
|
|
if ( is_null($list) ) |
176
|
|
|
throw new \RuntimeException(sprintf("Text identified by '%d' has malformed BBCode lists", $this->id)); |
177
|
|
|
|
178
|
|
|
$items = preg_split('/\[\*\]/u', $list); |
179
|
|
|
|
180
|
|
|
$counter = count($items); |
181
|
|
|
|
182
|
|
|
if ( isset($matches['type']) && $matches['type'] == '=1' ) { // ordered list |
183
|
|
|
// We start from 1 to discard the first string, in fact, it's empty. |
184
|
|
View Code Duplication |
for ( $i = 1; $i < $counter; $i++ ) |
|
|
|
|
185
|
|
|
if ( !empty($items[ $i ]) ) |
186
|
|
|
$buffer .= (string) ( $i ) . '. ' . trim($items[ $i ]) . PHP_EOL; |
187
|
|
|
} else { // unordered list |
188
|
|
|
// We start from 1 to discard the first string, in fact, it's empty. |
189
|
|
View Code Duplication |
for ( $i = 1; $i < $counter; $i++ ) |
|
|
|
|
190
|
|
|
if ( !empty($items[ $i ]) ) |
191
|
|
|
$buffer .= '- ' . trim($items[ $i ]) . PHP_EOL; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
// We need a like break above the list and another one below. |
195
|
|
|
if ( !empty($buffer) ) |
196
|
|
|
$buffer = PHP_EOL . $buffer . PHP_EOL; |
197
|
|
|
|
198
|
|
|
return $buffer; |
199
|
|
|
}, |
200
|
|
|
|
201
|
|
|
$this->text |
202
|
|
|
); |
203
|
|
|
|
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* @brief Replaces BBCode urls. |
209
|
|
|
*/ |
210
|
|
View Code Duplication |
protected function replaceUrls() |
|
|
|
|
211
|
|
|
{ |
212
|
|
|
|
213
|
|
|
$this->text = preg_replace_callback('%\[url\s*=\s*("(?:[^"]*")|\A[^\']*\Z|(?:[^\'">\]\s]+))\s*(?:[^]\s]*)\]([\W\D\w\s]*?)\[/url\]%iu', |
214
|
|
|
|
215
|
|
|
function ($matches) { |
216
|
|
|
if ( isset($matches[1]) && isset($matches[2]) ) |
217
|
|
|
return "[" . $matches[2] . "](" . $matches[1] . ")"; |
218
|
|
|
else |
219
|
|
|
throw new \RuntimeException(sprintf("Text identified by '%d' has malformed BBCode urls", $this->id)); |
220
|
|
|
}, |
221
|
|
|
|
222
|
|
|
$this->text |
223
|
|
|
); |
224
|
|
|
|
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* @brief Replaces BBCode urls without a description. |
230
|
|
|
*/ |
231
|
|
View Code Duplication |
protected function replaceUndescribedUrls() |
|
|
|
|
232
|
|
|
{ |
233
|
|
|
$this->text = preg_replace_callback('%\[url\]([\W\D\w\s]*?)\[/url\]%iu', |
234
|
|
|
|
235
|
|
|
function ($matches) { |
236
|
|
|
if ( isset($matches[1]) ) |
237
|
|
|
return "[" . $matches[1] . "](" . $matches[1] . ")"; |
238
|
|
|
else |
239
|
|
|
throw new \RuntimeException(sprintf("Text identified by '%d' has malformed BBCode urls", $this->id)); |
240
|
|
|
}, |
241
|
|
|
|
242
|
|
|
$this->text |
243
|
|
|
); |
244
|
|
|
|
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* @brief Replaces BBCode images. |
250
|
|
|
*/ |
251
|
|
|
protected function replaceImages() |
252
|
|
|
{ |
253
|
|
|
|
254
|
|
|
$this->text = preg_replace_callback('%\[img\s*\]\s*("(?:[^"]*")|\A[^\']*\Z|(?:[^\'">\]\s]+))\s*(?:[^]\s]*)\[/img\]%iu', |
255
|
|
|
|
256
|
|
|
function ($matches) { |
257
|
|
|
if ( isset($matches[1]) ) |
258
|
|
|
return PHP_EOL . "![]" . "(" . $matches[1] . ")" . PHP_EOL; |
259
|
|
|
else |
260
|
|
|
throw new \RuntimeException(sprintf("Text identified by '%d' have malformed BBCode images", $this->id)); |
261
|
|
|
}, |
262
|
|
|
|
263
|
|
|
$this->text |
264
|
|
|
); |
265
|
|
|
|
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* @brief Replaces BBCode quotes. |
271
|
|
|
* @details Thanks to Casimir et Hippolyte for helping me with this regex. |
272
|
|
|
*/ |
273
|
|
|
protected function replaceQuotes() |
274
|
|
|
{ |
275
|
|
|
// Removes the inner quotes, leaving just one level. |
276
|
|
|
$this->text = preg_replace('~\G(?<!^)(?>(\[quote\b[^]]*](?>[^[]++|\[(?!/?quote)|(?1))*\[/quote])|(?<!\[)(?>[^[]++|\[(?!/?quote))+\K)|\[quote\b[^]]*]\K~', '', $this->text); |
277
|
|
|
|
278
|
|
|
// Replaces all the remaining quotes with '> ' characters. |
279
|
|
|
$this->text = preg_replace_callback('%\[quote\b[^]]*\]((?>[^[]++|\[(?!/?quote))*)\[/quote\]%i', |
280
|
|
|
|
281
|
|
|
function ($matches) { |
282
|
|
|
$quote = preg_replace('/^\s*/mu', '', trim($matches[1])); |
283
|
|
|
return "> " . $quote . PHP_EOL . PHP_EOL; |
284
|
|
|
}, |
285
|
|
|
|
286
|
|
|
$this->text |
287
|
|
|
); |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* @brief Replaces BBCode snippets. |
293
|
|
|
*/ |
294
|
|
|
protected function replaceSnippets() |
295
|
|
|
{ |
296
|
|
|
|
297
|
|
|
$this->text = preg_replace_callback('%\[code\s*=?(?P<language>\w*)\](?P<snippet>[\W\D\w\s]*?)\[\/code\]%iu', |
298
|
|
|
|
299
|
|
|
function ($matches) { |
300
|
|
|
if ( isset($matches['snippet']) ) { |
301
|
|
|
$language = strtolower($matches['language']); |
302
|
|
|
|
303
|
|
|
if ( $language == 'html4strict' or $language == 'div' ) |
304
|
|
|
$language = 'html'; |
305
|
|
|
elseif ( $language == 'shell' or $language == 'dos' or $language == 'batch' ) |
306
|
|
|
$language = 'sh'; |
307
|
|
|
elseif ( $language == 'xul' or $language == 'wpf' ) |
308
|
|
|
$language = 'xml'; |
309
|
|
|
elseif ( $language == 'asm' ) |
310
|
|
|
$language = 'nasm'; |
311
|
|
|
elseif ( $language == 'vb' or $language == 'visualbasic' or $language == 'vba' ) |
312
|
|
|
$language = 'vb.net'; |
313
|
|
|
elseif ( $language == 'asp' ) |
314
|
|
|
$language = 'aspx-vb'; |
315
|
|
|
elseif ( $language == 'xaml' ) |
316
|
|
|
$language = 'xml'; |
317
|
|
|
elseif ( $language == 'cplusplus' ) |
318
|
|
|
$language = 'cpp'; |
319
|
|
|
elseif ( $language == 'txt' or $language == 'gettext' ) |
320
|
|
|
$language = 'text'; |
321
|
|
|
elseif ( $language == 'basic' ) |
322
|
|
|
$language = 'cbmbas'; |
323
|
|
|
elseif ( $language == 'lisp' ) |
324
|
|
|
$language = 'clojure'; |
325
|
|
|
elseif ( $language == 'aspnet' ) |
326
|
|
|
$language = 'aspx-vb'; |
327
|
|
|
|
328
|
|
|
return PHP_EOL . "```" . $language . PHP_EOL . trim($matches['snippet']) . PHP_EOL . "```" . PHP_EOL; |
329
|
|
|
} else |
330
|
|
|
throw new \RuntimeException(sprintf("Text identified by '%d' has malformed BBCode snippet.", $this->id)); |
331
|
|
|
}, |
332
|
|
|
|
333
|
|
|
$this->text |
334
|
|
|
); |
335
|
|
|
|
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* @brief Converts the provided BBCode text to an equivalent Markdown text. |
341
|
|
|
*/ |
342
|
|
|
public function toMarkdown() |
343
|
|
|
{ |
344
|
|
|
$this->removeCenter(); |
345
|
|
|
$this->removeLeft(); |
346
|
|
|
$this->removeColor(); |
347
|
|
|
$this->removeSize(); |
348
|
|
|
$this->replaceBold(); |
349
|
|
|
$this->replaceItalic(); |
350
|
|
|
$this->replaceUnderline(); |
351
|
|
|
$this->replaceStrikethrough(); |
352
|
|
|
$this->replaceLists(); |
353
|
|
|
$this->replaceUrls(); |
354
|
|
|
$this->replaceUndescribedUrls(); |
355
|
|
|
$this->replaceImages(); |
356
|
|
|
$this->replaceQuotes(); |
357
|
|
|
$this->replaceSnippets(); |
358
|
|
|
|
359
|
|
|
return trim($this->text); |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.