bbcode.php ➔ bbcode2xml()   F
last analyzed

Complexity

Conditions 18
Paths 1472

Size

Total Lines 280
Code Lines 171

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 18
eloc 171
nc 1472
nop 3
dl 0
loc 280
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
//------------------------------------------------------------------------------
4
//
5
//  eTraxis - Records tracking web-based system
6
//  Copyright (C) 2007-2011  Artem Rodygin
7
//
8
//  This program is free software: you can redistribute it and/or modify
9
//  it under the terms of the GNU General Public License as published by
10
//  the Free Software Foundation, either version 3 of the License, or
11
//  (at your option) any later version.
12
//
13
//  This program is distributed in the hope that it will be useful,
14
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
//  GNU General Public License for more details.
17
//
18
//  You should have received a copy of the GNU General Public License
19
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
//
21
//------------------------------------------------------------------------------
22
23
/**
24
 * BBCode
25
 *
26
 * This module is responsible for BBCode processing.
27
 *
28
 * @package Engine
29
 * @subpackage BBCode
30
 */
31
32
/**#@+
33
 * Dependency.
34
 */
35
require_once('../engine/debug.php');
36
/**#@-*/
37
38
//------------------------------------------------------------------------------
39
//  Definitions.
40
//------------------------------------------------------------------------------
41
42
/**
43
 * BBCode processing mode.
44
 *
45
 * No BBCode processing, all tags are hidden.
46
 */
47
define('BBCODE_OFF', 0);
48
49
/**
50
 * BBCode processing mode.
51
 *
52
 * <ul>
53
 * <li>"[b]", "[i]", "[u]", "[s]", "[sub]", "[sup]", "[color] are hidden</li>
54
 * <li>"[search]" is processed</li>
55
 * <li>all others are displayed as is</li>
56
 * </ul>
57
 */
58
define('BBCODE_SEARCH_ONLY', 1);
59
60
/**
61
 * BBCode processing mode.
62
 *
63
 * <ul>
64
 * <li>"[b]", "[i]", "[u]", "[s]", "[sub]", "[sup]", "[color]", "[search]" are processed</li>
65
 * <li>others are displayed as is</li>
66
 * </ul>
67
 */
68
define('BBCODE_MINIMUM', 2);
69
70
/**
71
 * BBCode processing mode.
72
 *
73
 * All available tags are processed.
74
 */
75
define('BBCODE_ALL', 3);
76
/**#@-*/
77
78
//------------------------------------------------------------------------------
79
//  Functions.
80
//------------------------------------------------------------------------------
81
82
/**
83
 * Transform BBCode tags into XML ones.
84
 *
85
 * @param string $bbcode Block of text, which could contain BBCode.
86
 * @param int $mode Mode of BBCode processing:
87
 * <ul>
88
 * <li>{@link BBCODE_OFF} - no BBCode processing, all tags are hidden</li>
89
 * <li>{@link BBCODE_SEARCH_ONLY} - "[search]" is processed, "[b]", "[i]", "[u]", "[s]", "[sub]", "[sup]", "[color]" are hidden, all others are displayed as is</li>
90
 * <li>{@link BBCODE_MINIMUM} - "[b]", "[i]", "[u]", "[s]", "[sub]", "[sup]", "[color]", "[search]" are processed, others are displayed as is</li>
91
 * <li>{@link BBCODE_ALL} - all available tags are processed</li>
92
 * </ul>
93
 * @param string $search Text to be searched.
94
 * @return string Resulted text with processed BBCode.
95
 */
96
function bbcode2xml ($bbcode, $mode = BBCODE_ALL, $search = NULL)
97
{
98
    debug_write_log(DEBUG_TRACE, '[bbcode2xml]');
99
    debug_write_log(DEBUG_DUMP,  '[bbcode2xml] $mode = ' . $mode);
100
101
    // PCRE for opening BBCode tags.
102
    $bbcode_open_tags = array
103
    (
104
        '!(\[b\])!isu',
105
        '!(\[i\])!isu',
106
        '!(\[u\])!isu',
107
        '!(\[s\])!isu',
108
        '!(\[sub\])!isu',
109
        '!(\[sup\])!isu',
110
        '!(\[color\=(.*?)\])!isu',
111
        '!(\[size\=(.*?)\])!isu',
112
        '!(\[font\=(.*?)\])!isu',
113
        '!(\[align\=(left|center|right)\])!isu',
114
        '!(\[h1\])!isu',
115
        '!(\[h2\])!isu',
116
        '!(\[h3\])!isu',
117
        '!(\[h4\])!isu',
118
        '!(\[h5\])!isu',
119
        '!(\[h6\])!isu',
120
        '!(\[list\])!isu',
121
        '!(\[ulist\])!isu',
122
        '!(\[li\])!isu',
123
        '!(\[url\])!isu',
124
        '!(\[url\=(.*?)\])!isu',
125
        '!(\[mail\])!isu',
126
        '!(\[mail\=(.*?)\])!isu',
127
        '!(\[code\])!isu',
128
        '!(\[quote\])!isu',
129
        '!(\[search\])!isu',
130
    );
131
132
    // PCRE for closing BBCode tags.
133
    $bbcode_close_tags = array
134
    (
135
        '!(\[/b\])!isu',
136
        '!(\[/i\])!isu',
137
        '!(\[/u\])!isu',
138
        '!(\[/s\])!isu',
139
        '!(\[/sub\])!isu',
140
        '!(\[/sup\])!isu',
141
        '!(\[/color\])!isu',
142
        '!(\[/size\])!isu',
143
        '!(\[/font\])!isu',
144
        '!(\[/align\])!isu',
145
        '!(\[/h1\])!isu',
146
        '!(\[/h2\])!isu',
147
        '!(\[/h3\])!isu',
148
        '!(\[/h4\])!isu',
149
        '!(\[/h5\])!isu',
150
        '!(\[/h6\])!isu',
151
        '!(\[/list\])!isu',
152
        '!(\[/ulist\])!isu',
153
        '!(\[/li\])!isu',
154
        '!(\[/url\])!isu',
155
        '!(\[/url\])!isu',
156
        '!(\[/mail\])!isu',
157
        '!(\[/mail\])!isu',
158
        '!(\[/code\])!isu',
159
        '!(\[/quote\])!isu',
160
        '!(\[/search\])!isu',
161
    );
162
163
    // XML alternatives for opening BBCode tsgs.
164
    $xml_open_tags = array
165
    (
166
        /* [b]      */ '<bbcode_b>',
167
        /* [i]      */ '<bbcode_i>',
168
        /* [u]      */ '<bbcode_u>',
169
        /* [s]      */ '<bbcode_s>',
170
        /* [sub]    */ '<bbcode_sub>',
171
        /* [sup]    */ '<bbcode_sup>',
172
        /* [color]  */ '<bbcode_color value="$2">',
173
        /* [size]   */ '<bbcode_size value="$2">',
174
        /* [font]   */ '<bbcode_font value="$2">',
175
        /* [align]  */ '<bbcode_align value="$2">',
176
        /* [h1]     */ '<bbcode_h1>',
177
        /* [h2]     */ '<bbcode_h2>',
178
        /* [h3]     */ '<bbcode_h3>',
179
        /* [h4]     */ '<bbcode_h4>',
180
        /* [h5]     */ '<bbcode_h5>',
181
        /* [h6]     */ '<bbcode_h6>',
182
        /* [list]   */ '<bbcode_list>',
183
        /* [ulist]  */ '<bbcode_ulist>',
184
        /* [li]     */ '<bbcode_li>',
185
        /* [url]    */ '<bbcode_url>',
186
        /* [url]    */ '<bbcode_url value="$2">',
187
        /* [mail]   */ '<bbcode_mail>',
188
        /* [mail]   */ '<bbcode_mail value="$2">',
189
        /* [code]   */ '<bbcode_code>',
190
        /* [quote]  */ '<bbcode_quote>',
191
        /* [search] */ '<bbcode_search>',
192
    );
193
194
    // XML alternatives for closing BBCode tsgs.
195
    $xml_close_tags = array
196
    (
197
        /* [/b]      */ '</bbcode_b>',
198
        /* [/i]      */ '</bbcode_i>',
199
        /* [/u]      */ '</bbcode_u>',
200
        /* [/s]      */ '</bbcode_s>',
201
        /* [/sub]    */ '</bbcode_sub>',
202
        /* [/sup]    */ '</bbcode_sup>',
203
        /* [/color]  */ '</bbcode_color>',
204
        /* [/size]   */ '</bbcode_size>',
205
        /* [/font]   */ '</bbcode_font>',
206
        /* [/align]  */ '</bbcode_align>',
207
        /* [/h1]     */ '</bbcode_h1>',
208
        /* [/h2]     */ '</bbcode_h2>',
209
        /* [/h3]     */ '</bbcode_h3>',
210
        /* [/h4]     */ '</bbcode_h4>',
211
        /* [/h5]     */ '</bbcode_h5>',
212
        /* [/h6]     */ '</bbcode_h6>',
213
        /* [/list]   */ '</bbcode_list>',
214
        /* [/ulist]  */ '</bbcode_ulist>',
215
        /* [/li]     */ '</bbcode_li>',
216
        /* [/url]    */ '</bbcode_url>',
217
        /* [/url]    */ '</bbcode_url>',
218
        /* [/mail]   */ '</bbcode_mail>',
219
        /* [/mail]   */ '</bbcode_mail>',
220
        /* [/code]   */ '</bbcode_code>',
221
        /* [/quote]  */ '</bbcode_quote>',
222
        /* [/search] */ '</bbcode_search>',
223
    );
224
225
    // If search mode is on, strip the delimiter and special PCRE characters.
226
    if (!is_null($search))
227
    {
228
        $search = preg_quote($search, '!');
229
    }
230
231
    // Put zero byte before and after each BBCode tag, as a tag border.
232
    $bbcode = preg_replace($bbcode_open_tags,  "\0\$1\0", $bbcode);
233
    $bbcode = preg_replace($bbcode_close_tags, "\0\$1\0", $bbcode);
234
235
    // Split BBCode text into array via zero byte border, so each tag is a separated array item and
236
    // each text between tags is the same.
237
    $text = explode("\0", $bbcode);
238
239
    // Stack for found opening BBCode tags.
240
    $stack = array();
241
242
    // Evaluate each piece of BBCode text.
243
    foreach ($text as $i => $str)
244
    {
245
        // Flag to determine whether the piece is BBCode tag.
246
        $is_tag = FALSE;
247
248
        // Check whether the piece is opening BBCode tag.
249
        // If so, push it to the stack.
250
        foreach ($bbcode_open_tags as $j => $tag)
251
        {
252
            if (($is_tag = preg_match($tag, $str)))
253
            {
254
                array_push($stack, $bbcode_close_tags[$j]);
255
                break;
256
            }
257
        }
258
259
        // If still not is tag, then it's definitely not an *opening* BBCode tag.
260
        // Check whether the piece is closing BBCode tag.
261
        if (!$is_tag)
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_tag of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
262
        {
263
            foreach ($bbcode_close_tags as $j => $tag)
264
            {
265
                if (($is_tag = preg_match($tag, $str)))
266
                {
267
                    $is_closed = FALSE;
268
269
                    // Close all previous tags, remained unclosed.
270
                    while (count($stack) > 0 && !$is_closed)
271
                    {
272
                        $k = array_pop($stack);
273
274
                        if ($k == $tag)
275
                        {
276
                            $is_closed = TRUE;
277
                        }
278
                        else
279
                        {
280
                            // Add missing closing tag.
281
                            $close_tag = preg_replace('!(\!\(\\\\\[/(.*)\\\]\)\!isu)!isu', '[/$2]', $k);
282
                            $text[$i] = $close_tag . $text[$i];
283
                        }
284
                    }
285
286
                    // If still not closed, then corresponding opening tag was missed.
287
                    if (!$is_closed)
288
                    {
289
                        // Remove current tag.
290
                        $text[$i] = ustrcut($text[$i], ustrlen($text[$i]) - ustrlen($str), FALSE);
291
                    }
292
293
                    break;
294
                }
295
            }
296
        }
297
298
        // If still not is tag, then it's definitely user's text between two BBCode tags.
299
        if (!$is_tag)
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_tag of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
300
        {
301
            // If search mode is on, add "[search]" tags for all corresponding matches.
302
            if (!is_null($search))
303
            {
304
                $text[$i] = preg_replace("!({$search})!isu", '[search]$1[/search]', $text[$i]);
305
            }
306
        }
307
    }
308
309
    // Close all tags, remained unclosed.
310
    while (count($stack) > 0)
311
    {
312
        $k = array_pop($stack);
313
314
        // Add missing closing tag.
315
        $close_tag = preg_replace('!(\!\(\\\\\[/(.*)\\\]\)\!isu)!isu', '[/$2]', $k);
316
        array_push($text, $close_tag);
317
    }
318
319
    // Merge the array into solid block of text.
320
    $bbcode = implode(NULL, $text);
321
322
    // Remove extra newline characters before and after some tags.
323
    $no_extra_newlines_before = array('[/code]', '[/quote]', '[/list]', '[/ulist]', '[li]', '[/li]');
324
    $no_extra_newlines_after  = array('[code]',  '[quote]',  '[list]',  '[ulist]',  '[li]', '[/li]');
325
326
    foreach ($no_extra_newlines_before as $tag)
327
    {
328
        $bbcode = ustr_replace('%br;' . $tag, $tag, $bbcode);
329
    }
330
331
    foreach ($no_extra_newlines_after as $tag)
332
    {
333
        $bbcode = ustr_replace($tag . '%br;', $tag, $bbcode);
334
    }
335
336
    // Encode existing HTML special characters.
337
    $bbcode = htmlspecialchars($bbcode, ENT_COMPAT, 'UTF-8');
338
339
    // Convert BBCode tags into XML ones.
340
    $bbcode = preg_replace($bbcode_open_tags,  $xml_open_tags,  $bbcode);
341
    $bbcode = preg_replace($bbcode_close_tags, $xml_close_tags, $bbcode);
342
343
    // XSLTs for different BBCode modes.
344
    $bbcode_xsl = array
345
    (
346
        BBCODE_OFF         => 'bbcode_off.xsl',
347
        BBCODE_SEARCH_ONLY => 'bbcode_search.xsl',
348
        BBCODE_MINIMUM     => 'bbcode_minimum.xsl',
349
        BBCODE_ALL         => 'bbcode_all.xsl',
350
    );
351
352
    // Transform resulted XML into DOM document.
353
    $page = new DOMDocument();
354
    $xslt = new XSLTProcessor();
355
356
    $page->load(LOCALROOT . '/engine/' . $bbcode_xsl[$mode]);
357
    $xslt->importStyleSheet($page);
358
    $page->loadXML("<bbcode>{$bbcode}</bbcode>");
359
360
    $dom = $xslt->transformToDoc($page);
361
362
    // Remove XML headers from DOM document.
363
    $root   = $dom->getElementsByTagName('bbcode')->item(0);
364
    $bbcode = NULL;
365
366
    for ($i = 0; $i < $root->childNodes->length; $i++)
367
    {
368
        $bbcode .= $dom->saveXML($root->childNodes->item($i));
369
    }
370
371
    // Decode back all existing HTML special characters, encoded before.
372
    $bbcode = htmlspecialchars_decode($bbcode, ENT_COMPAT);
373
374
    return $bbcode;
375
}
376
377
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
378