Passed
Push — master ( 522b1b...17713d )
by MOHAMMAD
01:34
created

BBCodeConverter   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 324
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 131
dl 0
loc 324
rs 4.5599
c 0
b 0
f 0
wmc 58

15 Methods

Rating   Name   Duplication   Size   Complexity  
A addCleaner() 0 4 2
A __construct() 0 7 3
A replaceItalic() 0 9 1
B toMarkdown() 0 24 9
A replaceUnderline() 0 9 1
B replaceLists() 0 40 10
A removeColor() 0 9 1
A replaceStrikethrough() 0 9 1
A replaceBold() 0 9 1
A removeSize() 0 9 1
A replaceUrls() 0 13 3
A replaceImages() 0 13 2
D replaceSnippets() 0 41 21
A replaceQuotes() 0 18 1
A removeCenter() 0 9 1

How to fix   Complexity   

Complex Class

Complex classes like BBCodeConverter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BBCodeConverter, and based on these observations, apply Extract Interface, too.

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