Completed
Push — master ( 12e8ae...5696da )
by Michael
02:13
created

GwikiPage::renderLists()   F

Complexity

Conditions 18
Paths 1530

Size

Total Lines 67
Code Lines 44

Duplication

Lines 27
Ratio 40.3 %
Metric Value
dl 27
loc 67
rs 2.6879
cc 18
eloc 44
nc 1530
nop 1

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
 * gwikiPage.php - class to access wiki page data
4
 *
5
 * This file is part of gwiki - geekwright wiki
6
 */
7
8
// defined('XOOPS_ROOT_PATH') || exit('XOOPS root path not defined');
9
10
define('_WIKI_CAMELCASE_REGEX', '(([A-Z]{1,}[a-z\x80-\xff0-9\:]+){2,}\d*)');
11
define('_WIKI_KEYWORD_REGEX', '([A-Za-z\x80-\xff0-9.\:-]{1,})');
12
13
/**
14
 * gwikiPage.php - class to access wiki page data
15
 *
16
 *
17
 * @category  Class
18
 * @package   Gwiki
19
 * @author    Richard Griffith <[email protected]>
20
 * @copyright 2013-2015 geekwright, LLC. All rights reserved.
21
 * @license   gwiki/docs/license.txt  GNU General Public License (GPL)
22
 */
23
class GwikiPage
24
{
25
    //------------------------------------------------------------
26
    // Properties -  public, protected, and private
27
    //------------------------------------------------------------
28
29
    protected $currentid;
30
    protected $currentkeyword;
31
    public    $gwiki_id;
32
    public    $keyword;
33
    public    $display_keyword;
34
    public    $title;
35
    public    $body;
36
    public    $parent_page;
37
    public    $page_set_home;
38
    public    $page_set_order;
39
    public    $meta_description;
40
    public    $meta_keywords;
41
    public    $lastmodified;
42
    public    $uid;
43
    public    $admin_lock;
44
    public    $active;
45
    public    $search_body;
46
    public    $toc_cache;
47
    public    $show_in_index;
48
    public    $gwiki_version;
49
50
    public $page_id; // an integer id for the keyword
51
    public $wikiHomePage; // the home page
52
    public $currentprefix; // Prefix of current keyword, if any
53
    public $currentprefixid; // id of current Prefix
54
    public $currenttemplateid; // template for current Prefix (0=use default)
55
    public $attachments;
56
57
    public $renderedPage;
58
59
    private $numberOfRecentItems = 10;
60
    // $wikiLinkURL is a sprintf format string, with keyword as only arg. Better link establised in __construct()
61
    private $wikiLinkURL  = 'index.php?page=%s';
62
    public  $dateFormat;
63
    public  $defaultThumbSize;
64
    private $tocIdPrefix  = 'toc';
65
    private $tocAnchorFmt = '#%s';
66
    private $imageLib     = array();
67
    private $useCamelCase;
68
    private $autoNameFormat;
69
70
    private $module_id;
71
72
    private $wikiDir; // dirname of the gwiki module
73
    private $gwikiVersion = 1; // wiki syntax version for future backward compatibility
74
75
    private $highlightArg;
76
77
    private $noWikiQueue = array(); // hold no wiki content during rendering
78
    private $noWikiIndex = 0;
79
80
    private $tocQueue = array(); // track headers for toc
81
    private $tocIndex = 0;
82
83
    private $refQueue = array(); // track reference
84
    private $refIndex = 0;
85
    private $refShown = false;
86
87
    private $wikiPageLinks = array(); // link in current page
88
89
    // Out Of Bounds data - not cleared with resetPage
90
    private $pageIndexPrefix = ''; // current prefix for the pageindex
91
92
    //------------------------------------------------------------
93
    // Methods
94
    //------------------------------------------------------------
95
96
    /**
97
     * class constructor
98
     */
99
    public function __construct()
100
    {
101
        $this->resetPage();
102
        $dir           = basename(dirname(__DIR__));
103
        $this->wikiDir = $dir;
104
105
        $moduleHandler = xoops_getHandler('module');
106
        $module         = $moduleHandler->getByDirname($dir);
107
        $module_id      = $module->getVar('mid');
108
        $configHandler = xoops_getHandler('config');
109
        $moduleConfig   = $configHandler->getConfigsByCat(0, $module->getVar('mid'));
110
111
        $this->wikiLinkURL      = $moduleConfig['wikilink_template'];
112
        $this->wikiHomePage     = $moduleConfig['wiki_home_page'];
113
        $this->dateFormat       = $moduleConfig['date_format'];
114
        $this->imageLib         = explode(',', $moduleConfig['imagelib_pages']);
115
        $this->useCamelCase     = $moduleConfig['allow_camelcase'];
116
        $this->defaultThumbSize = $moduleConfig['default_thumb_size'];
117
        $this->autoNameFormat   = $moduleConfig['auto_name_format'];
118
        $this->module_id        = $module_id;
119
120
        if (!defined('_MI_GWIKI_WIKIHOME')) {
121
            $this->loadLanguage('modinfo', $dir);
122
        }
123
        if (!defined('_MD_GWIKI_PAGE_PERM_EDIT_ANY_NUM')) {
124
            $this->loadLanguage('main', $dir);
125
        }
126
    }
127
128
    /**
129
     * load language resources
130
     *
131
     * @param string $name     language file name (main, modinfo, etc.)
132
     * @param string $domain   domain/module
133
     * @param null   $language language
134
     *
135
     * @return void
136
     */
137 View Code Duplication
    private function loadLanguage($name, $domain = '', $language = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
138
    {
139
        global $xoopsConfig;
140
        if (!@include_once XOOPS_ROOT_PATH . "/modules/{$domain}/language/" . $xoopsConfig['language'] . "/{$name}.php") {
141
            include_once XOOPS_ROOT_PATH . "/modules/{$domain}/language/english/{$name}.php";
142
        }
143
    }
144
145
    /**
146
     * Reset all page properties
147
     *
148
     * @return void
149
     */
150
    protected function resetPage()
151
    {
152
        $this->gwiki_id         = null;
153
        $this->keyword          = '';
154
        $this->display_keyword  = '';
155
        $this->title            = '';
156
        $this->body             = '';
157
        $this->parent_page      = '';
158
        $this->page_set_home    = '';
159
        $this->page_set_order   = '';
160
        $this->meta_description = '';
161
        $this->meta_keywords    = '';
162
        $this->lastmodified     = 0;
163
        $this->uid              = 0;
164
        $this->admin_lock       = 0;
165
        $this->active           = 0;
166
        $this->search_body      = '';
167
        $this->toc_cache        = '';
168
        $this->show_in_index    = 1;
169
        $this->gwiki_version    = $this->gwikiVersion;
170
171
        $this->page_id           = 0;
172
        $this->created           = 0;
0 ignored issues
show
Bug introduced by
The property created does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
173
        $this->renderedPage      = '';
174
        $this->currentprefix     = '';
175
        $this->currentprefixid   = '';
176
        $this->currenttemplateid = 0;
177
        $this->attachments       = array();
178
        $this->tocQueue          = array();
179
        $this->tocIndex          = 0;
180
        $this->refQueue          = array();
181
        $this->refIndex          = 0;
182
        $this->refShown          = false;
183
        $this->wikiPageLinks     = array();
184
    }
185
186
    /**
187
     * escape a string to be "safe" for use in database
188
     *
189
     * @param string $value string to be escaped
190
     *
191
     * @return string
192
     */
193
    public function escapeForDB($value)
194
    {
195
        global $xoopsDB;
196
        return $value = $xoopsDB->escape($value);
0 ignored issues
show
Unused Code introduced by
$value is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
197
    }
198
199
    /**
200
     * set the count for recent item list
201
     *
202
     * @param int $count item count
203
     *
204
     * @return void
205
     */
206
    public function setRecentCount($count)
207
    {
208
        $count = (int)$count;
209
        if ($count > 1 && $count < 1000) {
210
            $this->numberOfRecentItems = $count;
211
        }
212
    }
213
214
    /**
215
     * Set the URL pattern for wiki links
216
     *
217
     * @param string $url sprintf pattern for URL. Will get page name as parameter.
218
     *
219
     * @return void
220
     */
221
    public function setWikiLinkURL($url)
222
    {
223
        $this->wikiLinkURL = $url;
224
    }
225
226
    /**
227
     * Get the URL pattern for wiki links
228
     *
229
     * @return string
230
     */
231
    public function getWikiLinkURL()
232
    {
233
        return $this->wikiLinkURL;
234
    }
235
236
    /**
237
     * git the wiki directory (dirname)
238
     *
239
     * @return string
240
     */
241
    public function getWikiDir()
242
    {
243
        return $this->wikiDir;
244
    }
245
246
    /**
247
     * get max upload size from ini
248
     *
249
     * @return int|string
250
     */
251
    public function getMaxUploadSize()
252
    {
253
        $val  = trim(ini_get('upload_max_filesize'));
254
        $last = strtolower($val[strlen($val) - 1]);
255
        switch ($last) {
256
            // The 'G' modifier is available since PHP 5.1.0
257
            case 'g':
258
                $val *= 1024;
259
            // no break
260
            case 'm':
261
                $val *= 1024;
262
            // no break
263
            case 'k':
264
                $val *= 1024;
265
            // no break
266
        }
267
268
        return $val;
269
    }
270
271
    /**
272
     * set format for TOC links
273
     *
274
     * @param string $prefix     prefix
275
     * @param string $linkformat anchor
276
     *
277
     * @return void
278
     */
279
    public function setTocFormat($prefix, $linkformat)
280
    {
281
        $this->tocIdPrefix  = $prefix;
282
        $this->tocAnchorFmt = $linkformat;
283
    }
284
285
    /**
286
     * Make sure that keyword obeys formatting rules or switch to illegal name
287
     *
288
     * @param mixed $keyword - wiki page name
289
     *
290
     * @return string
291
     */
292
    public function makeKeyword($keyword)
293
    {
294
        if (!preg_match('#^' . _WIKI_KEYWORD_REGEX . '$#', $keyword)) {
295
            $keyword = _MI_GWIKI_WIKI404;
296
        } else { // check for undefined prefix
297
            $prefix = $this->getPrefix($keyword);
298
            if ($prefix && !$prefix['defined']) {
299
                $keyword = _MI_GWIKI_WIKI404;
300
            }
301
        }
302
303
        return $keyword;
304
    }
305
306
    /**
307
     * add namespace prefix to a wiki word
308
     *
309
     * @param int    $nsid namespace (prefix) id
310
     * @param string $page keyword
311
     *
312
     * @return bool|string
313
     */
314
    public function makeKeywordFromPrefix($nsid, $page)
315
    {
316
        if ($nsid >= 0) {
317
            $pfx = $this->getPrefixFromId($nsid);
318
            if (empty($page)) {
319
                if ($pfx['prefix_auto_name']) {
320
                    $page = date($this->autoNameFormat);
321
                } else {
322
                    $page = $pfx['prefix_home'];
323
                }
324
            }
325
            $page = $pfx['prefix'] . ':' . $page;
326
        }
327
328
        return $page;
329
    }
330
331
    /**
332
     * Capture out of bounds data traveling with keyword. Such data is sent
333
     * in keyword(oob) construct. This function processes any oob data and
334
     * returns a clean keyword.
335
     * oob data is used this way to pass page specific data in any url
336
     *
337
     * Only call this if you will NOT be calling normalizeKeyword or the
338
     * OOB data will be lost.
339
     *
340
     * @param mixed $keyword - wiki page name possibily containing OOB data
341
     *
342
     * @return string - keyword with no OOB data
343
     */
344
    public function getOOBFromKeyword($keyword)
345
    {
346
        $oob = null;
347
        if (substr($keyword, -1) === ')') {
348
            $lparen = strpos($keyword, '(');
349
            if ($lparen !== false) {
350
                $inparen = substr($keyword, $lparen);
351
                $inparen = substr($inparen, 1, - 2);
352
                $keyword = substr($keyword, 0, $lparen);
353
                $oob     = $inparen;
354
            }
355
        }
356
        // currently this is the only use
357
        $this->pageIndexPrefix = strtolower($oob);
358
359
        return $keyword;
360
    }
361
362
    /**
363
     * If page exists, fix case of page name to that specified in database
364
     *
365
     * @param string $keyword - wiki page name
366
     *
367
     * @return string normalized keyword
368
     */
369
    public function normalizeKeyword($keyword)
370
    {
371
        global $xoopsDB;
372
373
        $keyword = $this->getOOBFromKeyword($keyword);
374
        $keyword = $this->escapeForDB($keyword);
375
        $sql     = 'SELECT keyword FROM ' . $xoopsDB->prefix('gwiki_pages') . " WHERE keyword='{$keyword}' AND active=1 ";
376
        $result  = $xoopsDB->query($sql);
377
        if ($content = $xoopsDB->fetchArray($result)) {
378
            $keyword = $content['keyword'];
379
        } else {
380
            $keyword = $this->makeKeyword($keyword);
381
        }
382
383
        return $keyword;
384
    }
385
386
    /**
387
     * Get the gwiki_id of the active page for the keyword
388
     *
389
     * @param mixed $keyword - wiki page name
390
     *
391
     * @return int - id of page
392
     */
393
    public function getCurrentId($keyword)
394
    {
395
        global $xoopsDB;
396
397
        $sql = 'SELECT gwiki_id FROM ' . $xoopsDB->prefix('gwiki_pages');
398
        $sql .= " WHERE keyword='{$keyword}' AND active = 1 ORDER BY gwiki_id DESC LIMIT 1";
399
        $result = $xoopsDB->query($sql);
400
        list($id) = $xoopsDB->fetchRow($result);
401
402
        return (int)$id;
403
    }
404
405
    /**
406
     * Add current page as a new revision
407
     *
408
     * @param bool $leave_inactive true to save page as inactive
409
     *
410
     * @return mixed
411
     */
412
    public function addRevision($leave_inactive = false)
413
    {
414
        global $xoopsDB;
415
416
        $page = $this->escapeForDB($this->keyword);
417
        if (empty($this->display_keyword)) {
418
            $this->display_keyword = $page;
419
        }
420
        $this->tocQueue      = array();
421
        $this->tocIndex      = 0;
422
        $this->refQueue      = array();
423
        $this->refIndex      = 0;
424
        $this->wikiPageLinks = array();
425
426
        // eliminate things we don't want in search page because they
427
        // are misleading and/or change outside of the page itself
428
        $search[]  = "#{(PageIndex|RecentChanges)([^\"<\n]+?)?}#si";
0 ignored issues
show
Coding Style Comprehensibility introduced by
$search was never initialized. Although not strictly required by PHP, it is generally a good practice to add $search = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
429
        $replace[] = '';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$replace was never initialized. Although not strictly required by PHP, it is generally a good practice to add $replace = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
430
        $search[]  = "#\{toc\}#i";
431
        $replace[] = '';
432
        $search[]  = "#\{pagesettoc\}#i";
433
        $replace[] = '';
434
        $tempbody  = preg_replace($search, $replace, $this->body) . "\n\n";
435
436
        $this->search_body   = strip_tags($this->renderPage($tempbody));
437
        $this->toc_cache     = serialize($this->tocQueue);
438
        $this->gwiki_version = $this->gwikiVersion; // new revisions always for current engine
439
440
        // if we are adding to a page set, auto increment the order if none specified
441
        if (!empty($this->page_set_home) && $this->page_set_order === '') {
442
            $this->page_set_order = $this->getNextPageSetOrder($this->page_set_home);
443
        }
444
445
        // this will usually fail (duplicate)
446
        $sql    = 'INSERT INTO ' . $xoopsDB->prefix('gwiki_pageids') . " (keyword, created) VALUES('{$page}', UNIX_TIMESTAMP())";
447
        $result = $xoopsDB->query($sql);
448
        if ($result) {
449
            $page_id       = $xoopsDB->getInsertId();
450
            $this->page_id = $page_id;
451
        }
452
        if ($leave_inactive) {
453
            // allow a save that is not activated (for conflict management, and maybe more)
454
            $this->active = 0;
455
            $sql          = 'INSERT INTO ' . $xoopsDB->prefix('gwiki_pages');
456
            $sql .= ' (keyword, display_keyword, title, body, parent_page, page_set_home, page_set_order';
457
            $sql .= ', meta_description, meta_keywords';
458
            $sql .= ', lastmodified, uid, admin_lock, active, search_body, toc_cache, show_in_index, gwiki_version)';
459
            $sql .= ' VALUES (';
460
            $sql .= '\'' . $page . '\' ,';
461
            $sql .= '\'' . $this->escapeForDB($this->display_keyword) . '\' ,';
462
            $sql .= '\'' . $this->escapeForDB($this->title) . '\' ,';
463
            $sql .= '\'' . $this->escapeForDB($this->body) . '\' ,';
464
            $sql .= '\'' . $this->escapeForDB($this->parent_page) . '\' ,';
465
            $sql .= '\'' . $this->escapeForDB($this->page_set_home) . '\' ,';
466
            $sql .= '\'' . $this->escapeForDB($this->page_set_order) . '\' ,';
467
            $sql .= '\'' . $this->escapeForDB($this->meta_description) . '\' ,';
468
            $sql .= '\'' . $this->escapeForDB($this->meta_keywords) . '\' ,';
469
            $sql .= 'UNIX_TIMESTAMP() ,';
470
            $sql .= '\'' . $this->escapeForDB($this->uid) . '\' ,';
471
            $sql .= '\'' . $this->escapeForDB($this->admin_lock) . '\' ,';
472
            $sql .= '\'' . $this->escapeForDB($this->active) . '\' ,';
473
            $sql .= '\'' . $this->escapeForDB($this->search_body) . '\' ,';
474
            $sql .= '\'' . $this->escapeForDB($this->toc_cache) . '\' ,';
475
            $sql .= '\'' . $this->escapeForDB($this->show_in_index) . '\' ,';
0 ignored issues
show
Documentation introduced by
$this->show_in_index is of type integer|boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
476
            $sql .= '\'' . $this->escapeForDB($this->gwiki_version) . '\' )';
477
            $result = $xoopsDB->query($sql);
478
            if ($result) {
479
                $result         = $xoopsDB->getInsertId();
480
                $this->gwiki_id = $result;
481
            }
482
        } else {
483
            $sql    = 'UPDATE ' . $xoopsDB->prefix('gwiki_pages') . " SET active = 0 WHERE keyword='{$page}' and active = 1 ";
484
            $result = $xoopsDB->query($sql);
485
            if ($result) {
486
                $previous_rows = $xoopsDB->getAffectedRows();
487
                $this->active  = 1;
488
                $sql           = 'INSERT INTO ' . $xoopsDB->prefix('gwiki_pages');
489
                $sql .= ' (keyword, display_keyword, title, body, parent_page, page_set_home, page_set_order';
490
                $sql .= ', meta_description, meta_keywords, lastmodified';
491
                $sql .= ', uid, admin_lock, active, search_body, toc_cache, show_in_index, gwiki_version)';
492
                $sql .= ' VALUES (';
493
                $sql .= '\'' . $page . '\' ,';
494
                $sql .= '\'' . $this->escapeForDB($this->display_keyword) . '\' ,';
495
                $sql .= '\'' . $this->escapeForDB($this->title) . '\' ,';
496
                $sql .= '\'' . $this->escapeForDB($this->body) . '\' ,';
497
                $sql .= '\'' . $this->escapeForDB($this->parent_page) . '\' ,';
498
                $sql .= '\'' . $this->escapeForDB($this->page_set_home) . '\' ,';
499
                $sql .= '\'' . $this->escapeForDB($this->page_set_order) . '\' ,';
500
                $sql .= '\'' . $this->escapeForDB($this->meta_description) . '\' ,';
501
                $sql .= '\'' . $this->escapeForDB($this->meta_keywords) . '\' ,';
502
                $sql .= 'UNIX_TIMESTAMP() ,';
503
                $sql .= '\'' . $this->escapeForDB($this->uid) . '\' ,';
504
                $sql .= '\'' . $this->escapeForDB($this->admin_lock) . '\' ,';
505
                $sql .= '\'' . $this->escapeForDB($this->active) . '\' ,';
506
                $sql .= '\'' . $this->escapeForDB($this->search_body) . '\' ,';
507
                $sql .= '\'' . $this->escapeForDB($this->toc_cache) . '\' ,';
508
                $sql .= '\'' . $this->escapeForDB($this->show_in_index) . '\' ,';
0 ignored issues
show
Documentation introduced by
$this->show_in_index is of type integer|boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
509
                $sql .= '\'' . $this->escapeForDB($this->gwiki_version) . '\' )';
510
                $result = $xoopsDB->query($sql);
511
                if ($result) {
512
                    $result         = $xoopsDB->getInsertId();
513
                    $this->gwiki_id = $result;
514
515
                    $this->updatePageLinks();
516
517
                    $notificationHandler = xoops_getHandler('notification');
518
                    $tags['PAGE_NAME']    = $page;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$tags was never initialized. Although not strictly required by PHP, it is generally a good practice to add $tags = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
519
                    $tags['PAGE_TITLE']   = $this->title;
520
                    if (empty($tags['PAGE_TITLE'])) {
521
                        $tags['PAGE_TITLE'] = $this->display_keyword;
522
                    }
523
                    if (empty($tags['PAGE_TITLE'])) {
524
                        $tags['PAGE_TITLE'] = $page;
525
                    }
526
                    $tags['PAGE_LINK'] = sprintf($this->wikiLinkURL, $page);
527
                    $tags['NAMESPACE'] = $this->currentprefix;
528
529
                    if ($previous_rows < 1) {
530
                        // only for new
531
                        $notificationHandler->triggerEvent('global', 0, 'new_page', $tags, array(), $this->module_id);
532
                        if ($this->currentprefixid) { // have namespace
533
                            $notificationHandler->triggerEvent('namespace', $this->currentprefixid, 'new_ns_page', $tags, array(), $this->module_id);
534
                        }
535
                    }
536
                    // for all cases (new is also an update)
537
                    $notificationHandler->triggerEvent('page', $this->page_id, 'page_watch', $tags, array(), $this->module_id);
538
                    $notificationHandler->triggerEvent('global', 0, 'upd_page', $tags, array(), $this->module_id);
539
                    if ($this->currentprefixid) { // have namespace
540
                        $notificationHandler->triggerEvent('namespace', $this->currentprefixid, 'upd_ns_page', $tags, array(), $this->module_id);
541
                    }
542
                }
543
            }
544
        }
545
546
        return $result;
547
    }
548
549
    /**
550
     * update gwiki_pagelinks table - expects $page to be current
551
     *
552
     * @param bool $render do a fresh page render before updating
553
     *
554
     * @return void
555
     */
556
    private function updatePageLinks($render = false)
557
    {
558
        global $xoopsDB;
559
560
        if ($render) {
561
            // eliminate things we don't want in search page because they
562
            // are misleading and/or change outside of the page itself
563
            $search[]  = "#{(PageIndex|RecentChanges)([^\"<\n]+?)?}#si";
0 ignored issues
show
Coding Style Comprehensibility introduced by
$search was never initialized. Although not strictly required by PHP, it is generally a good practice to add $search = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
564
            $replace[] = '';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$replace was never initialized. Although not strictly required by PHP, it is generally a good practice to add $replace = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
565
            $search[]  = "#\{toc\}#i";
566
            $replace[] = '';
567
            $search[]  = "#\{pagesettoc\}#i";
568
            $replace[] = '';
569
            $tempbody  = preg_replace($search, $replace, $this->body) . "\n\n";
570
571
            $this->renderPage($tempbody);
572
        }
573
        $page = $this->escapeForDB($this->keyword);
574
575
        $sql = 'DELETE FROM ' . $xoopsDB->prefix('gwiki_pagelinks');
576
        $sql .= ' WHERE from_keyword = \'' . $page . '\'';
577
        $result = $xoopsDB->query($sql);
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
578
579
        if (!empty($this->wikiPageLinks)) {
580
            $sql    = 'INSERT INTO ' . $xoopsDB->prefix('gwiki_pagelinks') . ' (from_keyword, to_keyword) VALUES ';
581
            $values = '';
582
            foreach ($this->wikiPageLinks as $i => $v) {
583
                if (!empty($values)) {
584
                    $values .= ', ';
585
                }
586
                $values .= '(\'' . $page . '\', \'' . $this->escapeForDB($i) . '\')';
587
            }
588
            $sql .= $values;
589
            $result = $xoopsDB->query($sql);
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
590
        }
591
592
        return;
593
    }
594
595
    /**
596
     * get the next higher unused page_set_order for a given page_set_home
597
     *
598
     * @param string $page_set_home keyword of page set home
599
     *
600
     * @return int
601
     */
602 View Code Duplication
    private function getNextPageSetOrder($page_set_home)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
603
    {
604
        global $xoopsDB;
605
606
        $page_set_order = 1;
607
608
        $keyword = $this->escapeForDB($page_set_home);
609
610
        $sql    = 'SELECT MAX(page_set_order) FROM ' . $xoopsDB->prefix('gwiki_pages') . " WHERE active = 1 and page_set_home = '{$keyword}' ";
611
        $result = $xoopsDB->query($sql);
612
        if ($result) {
613
            $myrow          = $xoopsDB->fetchRow($result);
614
            $page_set_order = $myrow[0] + 1;
615
        }
616
617
        return $page_set_order;
618
    }
619
620
    /**
621
     * Check if the current user may edit the current page
622
     * Since the class can be used outside the module where permissions are assigned, we have to work at this a bit
623
     *
624
     * @return boolean mayEdit
625
     */
626
    public function checkEdit()
627
    {
628
        global $xoopsUser, $xoopsDB;
629
630
        $mayEdit = false;
631
        $keyword = $this->keyword;
632
633
        $dir            = $this->wikiDir;
634
        $moduleHandler = xoops_getHandler('module');
635
        $module         = $moduleHandler->getByDirname($dir);
636
        $module_id      = $module->getVar('mid');
637
        // $configHandler = xoops_getHandler('config');
638
        // $moduleConfig   = $configHandler->getConfigsByCat(0, $module->getVar('mid'));
639
        $groups = XOOPS_GROUP_ANONYMOUS;
640
        if (is_object($xoopsUser)) {
641
            $groups = $xoopsUser->getGroups();
642
        }
643
644
        $gpermHandler = xoops_getHandler('groupperm');
645
646
        $edit_any   = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_EDIT_ANY_NUM, $groups, $module_id);
647
        $edit_pfx   = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_EDIT_PFX_NUM, $groups, $module_id);
648
        $create_any = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_CREATE_ANY_NUM, $groups, $module_id);
649
        $create_pfx = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_CREATE_PFX_NUM, $groups, $module_id);
650
651
        // check for namespace prefix
652
        $prefix = $this->getPrefix($keyword);
653
        if ($prefix) {
654
            if ($prefix['defined']) {
655 View Code Duplication
                if (is_array($groups)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
656
                    $groupwhere = ' IN (' . implode(', ', $groups) . ') ';
657
                } else {
658
                    $groupwhere = " = '" . $groups . "'";
659
                }
660
                $sql    = 'SELECT group_prefix_id FROM ' . $xoopsDB->prefix('gwiki_group_prefix') . ' WHERE prefix_id = \'' . $prefix['prefix_id'] . '\' AND group_id ' . $groupwhere;
661
                $result = $xoopsDB->query($sql);
662
                $rows   = $xoopsDB->getRowsNum($result);
663
                $xoopsDB->freeRecordSet($result);
664
                if ($rows) { // prefix is assigned to one or more of user's groups
665
                    if (($edit_pfx || $create_pfx) && $this->gwiki_id) {
666
                        $mayEdit = true;
667
                    }
668
                    if ($create_pfx && !$this->gwiki_id) {
669
                        $mayEdit = true;
670
                    }
671
                }
672
                if (($edit_any || $create_any) && $this->gwiki_id) {
673
                    $mayEdit = true;
674
                }
675
                if ($create_any && !$this->gwiki_id) {
676
                    $mayEdit = true;
677
                }
678
            } else { // allow edit, but no create if prefix is undefined
679
                if ($edit_any && $this->gwiki_id) {
680
                    $mayEdit = true;
681
                }
682
            }
683
        } else {
684
            if (($edit_any || $create_any) && $this->gwiki_id) {
685
                $mayEdit = true;
686
            }
687
            if ($create_any && !$this->gwiki_id) {
688
                $mayEdit = true;
689
            }
690
        }
691
692
        return $mayEdit;
693
    }
694
695
    /**
696
     * get user name based on id
697
     *
698
     * @param int $uid user id
699
     *
700
     * @return string
701
     */
702 View Code Duplication
    public function getUserName($uid)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
703
    {
704
        global $xoopsConfig;
705
706
        $uid = (int)$uid;
707
708
        if ($uid > 0) {
709
            $memberHandler = xoops_getHandler('member');
710
            $user           = $memberHandler->getUser($uid);
711
            if (is_object($user)) {
712
                return "<a href=\"" . XOOPS_URL . "/userinfo.php?uid=$uid\">" . htmlspecialchars($user->getVar('uname'), ENT_QUOTES) . '</a>';
713
            }
714
        }
715
716
        return $xoopsConfig['anonymous'];
717
    }
718
719
    /**
720
     * get array of prefixes user can edit
721
     *
722
     * @param boolean $createonly true to show only prefixes with create permission
723
     *
724
     * @return string[]|false
725
     */
726
    public function getUserNamespaces($createonly = false)
727
    {
728
        global $xoopsUser, $xoopsDB;
729
730
        $dir            = $this->wikiDir;
731
        $moduleHandler = xoops_getHandler('module');
732
        $module         = $moduleHandler->getByDirname($dir);
733
        $module_id      = $module->getVar('mid');
734
735
        $groups = XOOPS_GROUP_ANONYMOUS;
736
        if (is_object($xoopsUser)) {
737
            $groups = $xoopsUser->getGroups();
738
        }
739
740
        $gpermHandler = xoops_getHandler('groupperm');
741
742
        $edit_any   = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_EDIT_ANY_NUM, $groups, $module_id);
743
        $edit_pfx   = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_EDIT_PFX_NUM, $groups, $module_id);
744
        $create_any = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_CREATE_ANY_NUM, $groups, $module_id);
745
        $create_pfx = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_CREATE_PFX_NUM, $groups, $module_id);
746
747 View Code Duplication
        if (is_array($groups)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
748
            $groupwhere = ' IN (' . implode(', ', $groups) . ') ';
749
        } else {
750
            $groupwhere = " = '" . $groups . "'";
751
        }
752
753
        $sql = 'SELECT distinct p.prefix_id, prefix FROM ';
754
        $sql .= $xoopsDB->prefix('gwiki_prefix') . ' p, ';
755
        $sql .= $xoopsDB->prefix('gwiki_group_prefix') . ' g ';
756
        $sql .= ' WHERE group_id ' . $groupwhere;
757
        $sql .= ' AND p.prefix_id = g.prefix_id';
758
        $sql .= ' ORDER BY prefix ';
759
        $prefixes = array();
760
        $result   = $xoopsDB->query($sql);
761
        if ($create_any) {
762
            $prefixes[] = array('prefix_id' => -1, 'prefix' => ' ');
763
        }
764
        while ($myrow = $xoopsDB->fetchArray($result)) {
765
            $prefixes[] = $myrow;
766
        }
767
768
        // make sure we have some edit/create permission. We need full keyword to be certain, so let edit sort it out.
769
        $mayEdit = ($edit_any || $create_any || $edit_pfx || $create_pfx);
770
        if ($createonly) {
771
            $mayEdit = ($create_any || $create_pfx);
772
        }
773
        if ($mayEdit) {
774
            return $prefixes;
775
        }
776
777
        return false;
778
    }
779
780
    /**
781
     * Get keyword for an id
782
     *
783
     * @param int $page_id id
784
     *
785
     * @return string|null wiki keyword (page)
786
     */
787
    public function getKeywordById($page_id)
788
    {
789
        global $xoopsDB;
790
791
        $keyword = null;
792
793
        $sql    = 'SELECT keyword FROM ' . $xoopsDB->prefix('gwiki_pageids') . " WHERE page_id = '{$page_id}' ";
794
        $result = $xoopsDB->query($sql);
795
        if ($result) {
796
            $myrow   = $xoopsDB->fetchRow($result);
797
            $keyword = $myrow[0];
798
        }
799
800
        return $keyword;
801
    }
802
803
    /**
804
     * lookup id for a keyword
805
     *
806
     * @param string $keyword keyword
807
     *
808
     * @return int
809
     */
810 View Code Duplication
    public function getPageId($keyword)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
811
    {
812
        global $xoopsDB;
813
814
        $page_id = 0;
815
816
        $keyword = $this->escapeForDB($keyword);
817
818
        $sql    = 'SELECT page_id FROM ' . $xoopsDB->prefix('gwiki_pageids') . " WHERE keyword = '{$keyword}' ";
819
        $result = $xoopsDB->query($sql);
820
        if ($result) {
821
            $myrow   = $xoopsDB->fetchRow($result);
822
            $page_id = $myrow[0];
823
        }
824
825
        return $page_id;
826
    }
827
828
    /**
829
     * record a page hit for a keyword
830
     *
831
     * @param string $keyword keyword
832
     *
833
     * @return int count of rows updated
834
     */
835
    public function registerHit($keyword)
836
    {
837
        global $xoopsDB;
838
839
        $keyword = $this->escapeForDB($keyword);
840
841
        $sql    = 'UPDATE ' . $xoopsDB->prefix('gwiki_pageids') . " SET hit_count = hit_count + 1 WHERE keyword = '{$keyword}' ";
842
        $result = $xoopsDB->queryF($sql);
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
843
844
        // nothing to do if it fails
845
        return $xoopsDB->getAffectedRows();
846
    }
847
848
    /**
849
     * set a specific revison as active for a keyword
850
     *
851
     * @param string $keyword wiki keyword
852
     * @param int    $id      id of revision to activate
853
     *
854
     * @return mixed
855
     */
856
    public function setRevision($keyword, $id)
857
    {
858
        global $xoopsDB;
859
860
        $keyword = $this->escapeForDB($keyword);
861
        $id      = (int)$id;
862
863
        $page = $this->getPage($keyword, $id);
864
        if (!$page) {
865
            return false;
866
        }
867
868
        $sql    = 'UPDATE ' . $xoopsDB->prefix('gwiki_pages') . " SET active = 0 WHERE keyword='{$keyword}' and active = 1 ";
869
        $result = $xoopsDB->query($sql);
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
870
871
        $sql    = 'UPDATE ' . $xoopsDB->prefix('gwiki_pages') . " SET active = 1 WHERE keyword='{$keyword}' AND gwiki_id='{$id}'";
872
        $result = $xoopsDB->query($sql);
873
874
        $this->updatePageLinks(true);
875
876
        return $result;
877
    }
878
879
    /**
880
     * load a page with optional revision id
881
     *
882
     * @param string   $keyword keyword
883
     * @param int|null $id      optional page id
884
     *
885
     * @return bool
886
     */
887
    public function getPage($keyword, $id = null)
888
    {
889
        global $xoopsDB;
890
891
        $this->resetPage();
892
        $this->keyword = $keyword;
893
        $prefix        = $this->getPrefix($keyword);
894
        if ($prefix && $prefix['defined']) {
895
            $this->currentprefix     = $prefix['prefix'];
896
            $this->currentprefixid   = $prefix['prefix_id'];
897
            $this->currenttemplateid = $prefix['prefix_template_id'];
898
        }
899
900
        $keyword = $this->escapeForDB($keyword);
901
902
        $this->page_id = $this->getPageId($keyword);
903
904
        if (empty($id)) {
905
            $sql = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_pages') . ' natural left join ' . $xoopsDB->prefix('gwiki_pageids') . " WHERE keyword='{$keyword}' and active = 1 ";
906
        } else {
907
            $id  = (int)$id;
908
            $sql = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_pages') . ' natural left join ' . $xoopsDB->prefix('gwiki_pageids') . " WHERE keyword='{$keyword}' and gwiki_id = {$id} ";
909
        }
910
        $result = $xoopsDB->query($sql);
911
        $page   = false;
912
        $rows   = $xoopsDB->getRowsNum($result);
913
        if ($rows > 0) {
914
            $page = $xoopsDB->fetchArray($result);
915
916
            $this->gwiki_id        = $page['gwiki_id'];
917
            $this->keyword         = $page['keyword'];
918
            $this->display_keyword = $page['display_keyword'];
919
            $this->title           = $page['title'];
920
            $this->body            = $page['body'];
921
            $this->parent_page     = $page['parent_page'];
922
923
            $this->page_set_home  = $page['page_set_home'];
924
            $this->page_set_order = $page['page_set_order'];
925
926
            $this->meta_description = $page['meta_description'];
927
            $this->meta_keywords    = $page['meta_keywords'];
928
            $this->lastmodified     = $page['lastmodified'];
929
            $this->uid              = $page['uid'];
930
            $this->admin_lock       = $page['admin_lock'];
931
            $this->active           = $page['active'];
932
            $this->search_body      = $page['search_body'];
933
            $this->toc_cache        = $page['toc_cache'];
934
            $this->show_in_index    = $page['show_in_index'];
935
936
            $this->gwiki_version = $page['gwiki_version'];
937
            $this->page_id       = $page['page_id'];
938
            $this->created       = $page['created'];
939
940
            $page['author']       = $this->getUserName($page['uid']);
941
            $page['revisiontime'] = date($this->dateFormat, $page['lastmodified']);
942
            $page['createdtime']  = date($this->dateFormat, $page['created']);
943
            $page['createdmonth'] = date('M', $page['created']);
944
            $page['createdday']   = date('d', $page['created']);
945
            $page['createdyear']  = date('Y', $page['created']);
946
947
            $temp = $this->renderPageSetNav($keyword);
948
            if ($temp) {
949
                $page['pageset'] = $temp;
950
            }
951
        }
952
953
        return $page;
954
    }
955
956
    /**
957
     * Check for a prefix (namespace)
958
     *
959
     * @param mixed $keyword - wiki page name
960
     *
961
     * @return bool
962
     */
963
    public function getPrefix($keyword)
964
    {
965
        /*
966
         gwiki_prefix columns
967
          prefix_id
968
          prefix
969
          prefix_home
970
          prefix_template_id
971
          prefix_is_external
972
          prefix_external_url < sprintf template for page in external namespace
973
        */
974
        global $xoopsDB;
975
976
        $prefix  = false;
977
        $keyword = $this->escapeForDB($keyword);
978
979
        $pos = strpos($keyword, ':');
980
        // split namespace and page reference on first colon
981
        if ($pos !== false && $pos > 0) {
982
            $pre    = substr($keyword, 0, $pos);
983
            $page   = substr($keyword, $pos + 1);
984
            $q_pre  = $this->escapeForDB($pre);
985
            $sql    = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_prefix') . " WHERE prefix='{$q_pre}' ";
986
            $result = $xoopsDB->query($sql);
987
            $rows   = $xoopsDB->getRowsNum($result);
988
            if ($rows > 0) {
989
                $prefix = $xoopsDB->fetchArray($result);
990
                if ($page === '') {
991
                    $page = $prefix['prefix_home'];
992
                } // supply home page if empty
993
                $prefix['defined'] = true;
994
                // external namespace
995
                if ($prefix['prefix_is_external']) {
996
                    $prefix['actual_page'] = sprintf($prefix['prefix_external_url'], $page);
997
                } else { // local namespace
998
                    $prefix['actual_page'] = $prefix['prefix'] . ':' . $page;
999
                }
1000
            } else { // we have an undefined prefix
1001
                $prefix['defined'] = false;
1002
            }
1003
        }
1004
1005
        return $prefix;
1006
    }
1007
1008
    /**
1009
     * get prefix string for an id
1010
     *
1011
     * @param int $pid prefix id
1012
     *
1013
     * @return string namespace prefix, or empty string
1014
     */
1015
    public function getPrefixFromId($pid)
1016
    {
1017
        global $xoopsDB;
1018
1019
        $sql    = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_prefix') . ' WHERE prefix_id =' . $pid;
1020
        $result = $xoopsDB->query($sql);
1021
        while ($myrow = $xoopsDB->fetchArray($result)) {
1022
            return $myrow;
1023
        }
1024
1025
        return '';
1026
    }
1027
1028
    /**
1029
     * get template for the current page
1030
     *
1031
     * @return string template name
1032
     */
1033
    public function getTemplateName()
1034
    {
1035
        $template = 'gwiki_view.tpl';
1036
        if ($this->currenttemplateid) {
1037
            $template = $this->wikiDir . '_prefix_' . $this->currentprefixid . '.tpl';
1038
        }
1039
1040
        return $template;
1041
    }
1042
1043
    /**
1044
     * get attachment info associated with a page
1045
     *
1046
     * @param string $page keyword
1047
     *
1048
     * @return array
1049
     */
1050
    public function getAttachments($page)
1051
    {
1052
        global $xoopsDB;
1053
1054
        $this->attachments = array();
1055
        $q_keyword         = $this->escapeForDB($page);
1056
        $sql               = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_page_files') . " WHERE keyword='{$q_keyword}' ";
1057
        $result            = $xoopsDB->query($sql);
1058
        $rows              = $xoopsDB->getRowsNum($result);
0 ignored issues
show
Unused Code introduced by
$rows is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1059
        while ($row = $xoopsDB->fetchArray($result)) {
1060
            $row['iconlink']     = XOOPS_URL . '/modules/' . $this->wikiDir . '/assets/icons/48px/' . $row['file_icon'] . '.png';
1061
            $row['userlink']     = $this->getUserName($row['file_uid']);
1062
            $row['size']         = number_format($row['file_size']);
1063
            $row['date']         = date($this->dateFormat, $row['file_upload_date']);
1064
            $this->attachments[] = $row;
1065
        }
1066
1067
        return $this->attachments;
1068
    }
1069
1070
    /**
1071
     * Make a link from a wiki keyword
1072
     *
1073
     * @param mixed $keyword - wiki page name
1074
     * @param mixed $altkey  - alternate text for link. If empty, display_keyword will be used.
1075
     *
1076
     * @return string
1077
     */
1078
    public function wikiLink($keyword, $altkey = null)
1079
    {
1080
        global $xoopsDB;
1081
1082
        // HACK - get rid of spaces in page
1083
        // WikiCreole site is filled with page references such as [[Creole 1.0 Poll]] which resolve as
1084
        // hrefs like http://wikicreole.org/wiki/Creole1.0Poll
1085
        //
1086
        // will assume this is considered normal wikiish behavior, and try to emulate.
1087
        // Also seems to capitalize each portion, ie 'Ab and Cd' yields 'AbAndCd' - emulate this, too.
1088
        $org_keyword = $keyword;
1089
        if (strpos(trim($keyword), ' ')) {
1090
            $keys = explode(' ', $keyword);
1091
            foreach ($keys as $i => $k) {
1092
                $keys[$i] = ucfirst($k);
1093
            }
1094
            $keyword = implode('', $keys);
1095
        }
1096
        // $keyword=str_replace (' ', '', $keyword);
1097
1098
        // check for namespace prefix
1099
        $prefix = $this->getPrefix($keyword);
1100
        if ($prefix && $prefix['defined']) {
1101
            $link = $prefix['actual_page'];
1102
            // external namespace
1103
            if ($prefix['prefix_is_external']) {
1104
                $linktext = $org_keyword;
1105
                if ($altkey) {
1106
                    $linktext = $altkey;
1107
                }
1108
                $linktext = stripslashes($linktext);
1109
                $ret      = '<a href="' . $link . '" target="_blank" title="' . _MD_GWIKI_PAGE_EXT_LINK_TT . '">' . $linktext . '<span class="wikiextlink"> </span></a>';
1110
1111
                return $ret;
1112
            } else { // interal namespace
1113
                $keyword = $link; // we may have modified the keyword
1114
            }
1115
        }
1116
1117
        $sql    = 'SELECT keyword, display_keyword, title FROM ' . $xoopsDB->prefix('gwiki_pages') . " WHERE keyword='{$keyword}' and active = 1 ";
1118
        $result = $xoopsDB->query($sql);
1119
        $rows   = $xoopsDB->getRowsNum($result);
1120
        if ($rows) { // existing page
1121
            list($keyword, $display_keyword, $title) = $xoopsDB->fetchRow($result);
1122
            $display_keyword = htmlentities($display_keyword, ENT_QUOTES);
1123
            if (empty($display_keyword)) {
1124
                $display_keyword = $org_keyword;
1125
            }
1126
            $keyword = strtolower($keyword);
1127
            $newpage = '';
1128
        } else { // new page link
1129
            $display_keyword = $org_keyword;
1130
            $newpage         = '<span class="wikinewpage"> </span>';
1131
            $title           = sprintf(_MD_GWIKI_PAGE_CREATE_TT, $keyword);
1132
        }
1133
        if (!empty($altkey)) {
1134
            $display_keyword = $altkey;
1135
        }
1136
        $title           = htmlspecialchars($title);
1137
        $display_keyword = stripslashes($display_keyword);
1138
1139
        // track where this page links
1140
        if (isset($this->wikiPageLinks[$keyword])) {
1141
            $this->wikiPageLinks[$keyword] += 1;
1142
        } else {
1143
            $this->wikiPageLinks[$keyword] = 1;
1144
        }
1145
1146
        $url = sprintf($this->wikiLinkURL, $keyword);
1147
1148
        return sprintf('<a href="%s" title="%s">%s%s</a>', $url, $title, $display_keyword, $newpage);
1149
    }
1150
1151
    /**
1152
     * callback
1153
     *
1154
     * @param string[] $matches preg_replace_callback matches
1155
     *
1156
     * @return string
1157
     */
1158
    private function wikiCCLink($matches)
1159
    {
1160
        return $this->wikiLink($matches[1]);
1161
    }
1162
1163
    /**
1164
     * get tabbed page index
1165
     *
1166
     * @return array
1167
     */
1168
    private function getIndexTabs()
1169
    {
1170
        global $xoopsDB;
1171
1172
        $tabs = array();
1173
1174
        $sql = 'SELECT SUBSTRING(display_keyword,1,1) as letter, count(*) as count ';
1175
        $sql .= ' FROM ' . $xoopsDB->prefix('gwiki_pages');
1176
        $sql .= ' WHERE active=1 AND show_in_index=1 ';
1177
        $sql .= ' GROUP BY letter ';
1178
1179
        $result = $xoopsDB->query($sql);
1180
1181
        $currentset = false;
1182
        $rows       = $xoopsDB->getRowsNum($result);
1183
        if ($rows) {
1184
            while ($row = $xoopsDB->fetchArray($result)) {
1185
                $row['letter'] = strtolower($row['letter']);
1186
                if ($this->pageIndexPrefix === $row['letter']) {
1187
                    $row['current'] = true;
1188
                    $currentset     = true;
1189
                } else {
1190
                    $row['current'] = false;
1191
                }
1192
                $tabs[] = $row;
1193
            }
1194
        }
1195
        $xoopsDB->freeRecordSet($result);
1196
1197
        if (!$currentset) {
1198
            $this->pageIndexPrefix = $tabs[0]['letter'];
1199
            $tabs[0]['current']    = true;
1200
        }
1201
1202
        return $tabs;
1203
    }
1204
1205
    /**
1206
     * get a page index
1207
     *
1208
     * @param string|null $prefix if not null, limit index to a prefix
1209
     *
1210
     * @return string rendered index
1211
     *
1212
     */
1213
    private function pageIndex($prefix = null)
1214
    {
1215
        global $xoopsDB;
1216
        $simplelayout = false;
1217
        $tablayout    = false;
1218
1219
        $body = '';
1220
1221
        $pageselect = 'active=1 AND show_in_index=1 ';
1222
1223
        if (!empty($prefix)) {
1224
            $pageselect .= ' AND keyword LIKE "' . $prefix . '%" ';
1225
        } else {
1226
            $sql = 'SELECT count(*) as count  FROM ' . $xoopsDB->prefix('gwiki_pages');
1227
            $sql .= ' WHERE ' . $pageselect;
1228
            $result = $xoopsDB->query($sql);
1229
            $row    = $xoopsDB->fetchArray($result);
1230
            $cnt    = $row['count'];
1231
            $xoopsDB->freeRecordSet($result);
1232
            if ($cnt > 500) {
1233
                $tablayout    = true;
1234
                $simplelayout = true; // tablayout is already grouped by first character
1235
                $tabs         = $this->getIndexTabs();
1236
                $pageselect .= ' AND display_keyword LIKE "' . $this->pageIndexPrefix . '%" ';
1237
            }
1238
        }
1239
1240
        $sql = 'SELECT keyword, display_keyword, title';
1241
        $sql .= ' FROM ' . $xoopsDB->prefix('gwiki_pages');
1242
        $sql .= ' WHERE ' . $pageselect;
1243
        $sql .= ' ORDER BY display_keyword ';
1244
        //      $sql.=' ORDER BY active, show_in_index, display_keyword ';
1245
1246
        $result = $xoopsDB->query($sql);
1247
        $rowcnt = $xoopsDB->getRowsNum($result);
1248
1249
        if ($rowcnt < 50) {
1250
            $simplelayout = true;
1251
        } // skip the fancy by letter breakout if this is a small index
1252
1253
        if ($tablayout) {
1254
            $body .= '<div class="wikiindex"><div class="wikiindextabs"><ul>';
1255
1256
            foreach ($tabs as $tab) {
0 ignored issues
show
Bug introduced by
The variable $tabs does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1257
                $class = '';
1258
                if ($tab['current']) {
1259
                    $class = ' id="wikiindextabactive"';
1260
                }
1261
                $url    = sprintf($this->wikiLinkURL, strtolower($this->keyword . '(\\' . $tab['letter'] . ')'));
1262
                $letter = strtoupper($tab['letter']);
1263
                $body .= "\n<li{$class}><a href=\"{$url}\">{$letter}</a></li>";
1264
            }
1265
            $body .= '</ul></div><div class="wikiindexbody">';
1266
        }
1267
1268
        $lastletter = '';
1269
        if ($simplelayout) {
1270
            $body .= '<ul>';
1271
        }
1272
        while ($content = $xoopsDB->fetchArray($result)) {
1273
            $display_keyword = $content['display_keyword'];
1274
            if (empty($display_keyword)) {
1275
                $display_keyword = $content['keyword'];
1276
            }
1277
            if (!$simplelayout) {
1278
                if (function_exists('mb_substr')) {
1279
                    $testletter = mb_strtoupper(mb_substr($display_keyword, 0, 1, 'UTF-8'), 'UTF-8');
1280
                } else {
1281
                    $testletter = strtoupper(substr($display_keyword, 0, 1));
1282
                }
1283
                if ($lastletter === '') {
1284
                    $lastletter = $testletter;
1285
                    $body .= "<h3>{$lastletter}</h3><ul>";
1286
                }
1287
                if ($lastletter !== $testletter) {
1288
                    $lastletter = $testletter;
1289
                    $body .= "</ul><h3>{$lastletter}</h3><ul>";
1290
                }
1291
            }
1292
            $title           = htmlspecialchars($content['title']);
1293
            $display_keyword = htmlspecialchars($display_keyword);
1294
            $url             = sprintf($this->wikiLinkURL, strtolower($content['keyword']));
1295
            $link            = sprintf('<a href="%s" title="%s">%s%s</a>', $url, $title, $display_keyword, '');
1296
            $body .= '<li>' . $link . ' : ' . $title . '</li>';
1297
        }
1298
        $xoopsDB->freeRecordSet($result);
1299
        if ($tablayout) {
1300
            $body .= '</ul></div></div>';
1301
        } elseif ($body != '') {
1302
            $body .= '</ul>';
1303
        }
1304
1305
        return $body . "\n\n";
1306
    }
1307
1308
    /**
1309
     * get a recently modfied page index
1310
     *
1311
     * @param string|null $prefix if not null, limit index to a prefix
1312
     *
1313
     * @return string rendered index
1314
     *
1315
     */
1316
    private function recentIndex($prefix = null)
1317
    {
1318
        global $xoopsDB;
1319
1320
        // only show active pages
1321
        $pageselect = 'active=1  AND show_in_index=1 ';
1322
        if (!empty($prefix)) {
1323
            $pageselect .= ' AND keyword LIKE "' . $prefix . '%" ';
1324
        }
1325
1326
        $body = '';
1327
1328
        $sql = 'SELECT keyword, display_keyword, title, lastmodified';
1329
        $sql .= ', FROM_UNIXTIME(lastmodified) as fmtlastmodified, uid';
1330
        $sql .= ' FROM ' . $xoopsDB->prefix('gwiki_pages');
1331
        $sql .= ' WHERE ' . $pageselect;
1332
        $sql .= ' ORDER BY lastmodified DESC LIMIT ' . $this->numberOfRecentItems;
1333
1334
        $result = $xoopsDB->query($sql);
1335
1336
        $lastdate = '';
1337
        while ($content = $xoopsDB->fetchArray($result)) {
1338
            $testdate = substr($content['fmtlastmodified'], 0, 10);
1339
            if ($lastdate === '') {
1340
                $lastdate = $testdate;
1341
                $body .= "<h3>{$lastdate}</h3><ul>";
1342
            }
1343
            if ($lastdate !== $testdate) {
1344
                $lastdate = $testdate;
1345
                $body .= "</ul><h3>{$lastdate}</h3><ul>";
1346
            }
1347
1348
            $title           = htmlspecialchars($content['title']);
1349
            $display_keyword = htmlspecialchars($content['display_keyword']);
1350
            $url             = sprintf($this->wikiLinkURL, strtolower($content['keyword']));
1351
            $link            = sprintf('<a href="%s" title="%s">%s%s</a>', $url, $title, $display_keyword, '');
1352
            $body .= '<li>' . $link . ' : ' . $title . '</li>';
1353
        }
1354
        $xoopsDB->freeRecordSet($result);
1355
        if ($body !== '') {
1356
            $body .= '</ul>';
1357
        }
1358
1359
        return $body . "\n\n";
1360
    }
1361
1362
    /**
1363
     * callback render match specified index
1364
     *
1365
     * @param string[] $matches preg_replace_callback matches
1366
     *
1367
     * @return bool|string
1368
     */
1369
    private function renderIndex($matches)
1370
    {
1371
        $type  = $matches[1];
1372
        $parms = '';
1373
        if (isset($matches[2])) {
1374
            $parms = trim($matches[2]);
1375
        }
1376
        if (strcasecmp($type, 'RecentChanges') === 0) {
1377
            return $this->recentIndex($parms);
1378
        }
1379
        if (strcasecmp($type, 'PageIndex') === 0) {
1380
            return $this->pageIndex($parms);
1381
        }
1382
1383
        return false;
1384
    }
1385
1386
    /**
1387
     * highlight search terms
1388
     * adapted from: http://stack:overflow.com/questions/2591046/highlight-text-except-html-tags
1389
     *
1390
     * @param string[] $capture matches
1391
     *
1392
     * @return string
1393
     */
1394
    private function mon_rplc_callback($capture)
1395
    {
1396
        $haystack = $capture[1];
1397
        $p1       = stripos($haystack, $this->highlightArg['needle']);
1398
        $l1       = strlen($this->highlightArg['needle']);
1399
        $ret      = '';
1400
        while ($p1 !== false) {
1401
            $ret .= substr($haystack, 0, $p1) . $this->highlightArg['pre'] . substr($haystack, $p1, $l1) . $this->highlightArg['post'];
1402
            $haystack = substr($haystack, $p1 + $l1);
1403
            $p1       = stripos($haystack, $this->highlightArg['needle']);
1404
        }
1405
        $ret .= $haystack . $capture[2];
1406
1407
        return $ret;
1408
    }
1409
1410
    /**
1411
     * split string aware of html tags
1412
     *
1413
     * @param string $needle string to find
1414
     * @param string $pre    string to include before each match
1415
     * @param string $post   string to include after each match
1416
     * @param string $txt    text to search
1417
     *
1418
     * @return string
1419
     */
1420
    private function split_on_tag($needle, $pre, $post, $txt)
1421
    {
1422
        $this->highlightArg = compact('needle', 'pre', 'post');
1423
1424
        return preg_replace_callback('#((?:(?!<[/a-z]).)*)([^>]*>|$)#si', array($this, 'mon_rplc_callback'), $txt);
1425
    }
1426
1427
    /**
1428
     * highlight words in page
1429
     *
1430
     * @param string $words space separated words to match
1431
     *
1432
     * @return string rendered page with words highlighted
1433
     */
1434
    public function highlightWords($words)
1435
    {
1436
        $words = str_replace('  ', ' ', $words);
1437
        $words = explode(' ', $words);
1438
        $body  = $this->renderedPage;
1439
        foreach ($words as $word) {
1440
            $body = $this->split_on_tag($word, '<span class="wiki_search_term">', '</span>', $body);
1441
        }
1442
1443
        return $body;
1444
    }
1445
1446
    /**
1447
     * Hold content not to be processed for wiki markup, generate a unique tag to locate later
1448
     *
1449
     * @param string $type   type of nowiki invocation (block, wcinline or inline)
1450
     * @param string $source content to hold
1451
     *
1452
     * @return string generated tag for held content
1453
     */
1454
    private function noWikiHold($type, $source)
1455
    {
1456
        ++$this->noWikiIndex;
1457
        switch ($type) {
1458
            case 'block':
1459
                $this->noWikiQueue[$this->noWikiIndex] = "<pre>\n{$source}\n</pre>";
1460
                break;
1461
            case 'wcinline':
1462
                $this->noWikiQueue[$this->noWikiIndex] = '<span class="wikinoinline">' . $source . '</span>';
1463
                break;
1464
            case 'inline':
1465
            default:
1466
                $this->noWikiQueue[$this->noWikiIndex] = $source;
1467
                break;
1468
        }
1469
1470
        $ret = "{PdNlNw:{$this->noWikiIndex}}";
1471
1472
        return $ret;
1473
    }
1474
1475
    /**
1476
     * no wiki block callback
1477
     *
1478
     * @param string[] $matches preg_replace_callback matches
1479
     *
1480
     * @return string
1481
     */
1482
    private function noWikiHoldBlock($matches)
1483
    {
1484
        return $this->noWikiHold('block', $matches[1]);
1485
    }
1486
1487
    /**
1488
     * no wiki inline callback
1489
     *
1490
     * @param string[] $matches preg_replace_callback matches
1491
     *
1492
     * @return string
1493
     */
1494
    private function noWikiHoldInline($matches)
1495
    {
1496
        return $this->noWikiHold('inline', $matches[1]);
1497
    }
1498
1499
    /**
1500
     * no wiki inline (WikiCreole style) callback
1501
     *
1502
     * @param string[] $matches preg_replace_callback matches
1503
     *
1504
     * @return string
1505
     */
1506
    private function noWikiHoldWCInline($matches)
1507
    {
1508
        return $this->noWikiHold('wcinline', $matches[1]);
1509
    }
1510
1511
    /**
1512
     * no wiki for code block callback
1513
     *
1514
     * @param string[] $matches preg_replace_callback matches
1515
     *
1516
     * @return string
1517
     */
1518
    private function noWikiHoldCode($matches)
1519
    {
1520
        return $matches[1] . $this->noWikiHold('block', $matches[2]) . $matches[3];
1521
    }
1522
1523
    /**
1524
     * emit save nowiki content callback
1525
     *
1526
     * @param string[] $matches preg_replace_callback matches
1527
     *
1528
     * @return string
1529
     */
1530
    private function noWikiEmit($matches)
1531
    {
1532
        $index = $matches[1];
1533
1534
        return $this->noWikiQueue[$index];
1535
    }
1536
1537
    /**
1538
     * table support callback
1539
     *
1540
     * @param string[] $matches preg_replace_callback matches
1541
     *
1542
     * @return string
1543
     */
1544
    private function renderTables($matches)
1545
    {
1546
        $source = $matches[0];
1547
        $rowcnt = 0;
1548
        $table  = "<table class=\"wikitable\">\n";
1549
        $rows   = explode("\n", $source);
1550
        foreach ($rows as $i => $row) {
1551
            $row = trim($row);
1552
            if (!empty($row)) {
1553
                if ($row[0] === '|') {
1554
                    $row = substr($row, 1);
1555
                }
1556
                if (substr($row, -1) === '|') {
1557
                    $row = substr($row, 0, -1);
1558
                }
1559
                $cols = explode('|', $row);
1560
                $table .= '<tr' . (($rowcnt % 2) ? ' class="even"' : ' class="odd"') . '>';
1561
                ++$rowcnt;
1562
                foreach ($cols as $col) {
1563
                    if (empty($col)) {
1564
                        $table .= '<td>&nbsp;</td>';
1565 View Code Duplication
                    } elseif ($col[0] === '=') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1566
                        $table .= '<th>' . substr($col, 1) . '</th>';
1567
                    } elseif ($col[0] === '>') {
1568
                        $table .= '<td class="right">' . substr($col, 1) . '</td>';
1569 View Code Duplication
                    } elseif ($col[0] === '+') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1570
                        $table .= '<td class="center">' . substr($col, 1) . '</td>';
1571
                    } elseif (substr($col, 0, 4) === '&lt;') {
1572
                        $table .= '<td class="left">' . substr($col, 4) . '</td>';
1573
                    } elseif (preg_match('/^\s*[0-9.$+\-]+\s*$/', $col)) {
1574
                        $class = 'number';
1575
                        if ((float)preg_replace("/[^-0-9\.]/", '', $col) < 0) {
1576
                            $class = 'number negative';
1577
                        }
1578
                        $table .= '<td class="' . $class . '">' . trim($col) . '</td>';
1579
                    } else {
1580
                        $table .= '<td>' . $col . '</td>';
1581
                    }
1582
                }
1583
                $table .= "</tr>\n";
1584
            }
1585
        }
1586
        $table .= "</table>\n";
1587
1588
        return $table;
1589
    }
1590
1591
    /**
1592
     * link support callback
1593
     *
1594
     * @param string[] $matches preg_replace_callback matches
1595
     *
1596
     * @return string
1597
     */
1598
    private function renderLink($matches)
1599
    {
1600
        $source = trim($matches[1]);
1601
        $pos    = strpos($source, '|');
1602
1603
        if ($pos === false) { // no delimter - whole thing is the link
1604
            $link     = $source;
1605
            $linktext = '';
1606
            // handle the pathological case of a possesive of a person page.
1607
            // Creole test includes "[[Ward Cunningham's]]" which leads to a
1608
            // wiki page WardCunningham. Included in spirit of compatibility.
1609
            if (substr($link, -2) === "'s") {
1610
                $templink = substr($link, 0, - 3); // quote is slashed
1611
                // only if a wiki page
1612
                if (preg_match('/^([A-Za-z\x80-\xff0-9.:\- ]){2,}$/', $templink)) {
1613
                    $linktext = $link;
1614
                    $link     = $templink;
1615
                }
1616
            }
1617
        } else {
1618
            $link     = trim(substr($source, 0, $pos));
1619
            $linktext = trim(substr($source, $pos + 1));
1620
        }
1621
1622
        if (preg_match('/^([A-Za-z\x80-\xff0-9.:\- ]){2,}$/', $link)) {
1623
            //$link=str_replace (' ', '', $link);
1624
            if (empty($linktext)) {
1625
                $ret = $this->wikiLink($link);
1626
            } else {
1627
                $ret = $this->wikiLink($link, stripslashes($linktext));
1628
            }
1629
        } else {
1630
            $ext = true;
1631
            if (strncasecmp($link, XOOPS_URL, strlen(XOOPS_URL)) === 0) {
1632
                $ext = false;
1633
            } // matches our site
1634 View Code Duplication
            if (strcasecmp('siteurl:', substr($link, 0, 8)) === 0) { // explicit reference to our site
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1635
                $link = XOOPS_URL . substr($link, 8);
1636
                $ext  = false;
1637
            }
1638
            if (strpos($link, ':') === false) {
1639
                $ext = false;
1640
            } // no protocol, assume relative url
1641
            if ($linktext === '') {
1642
                $linktext = $link;
1643
            }
1644
            $linktext = stripslashes($linktext);
1645
            // $linktext=$this->noWikiHold('inline',stripslashes($linktext));
1646
            $ret = "<a href=\"{$link}\" title=\"{$linktext}\">{$linktext}</a>";
1647
            if ($ext) {
1648
                $ret = '<a href="' . $link . '" target="_blank" title="' . _MD_GWIKI_PAGE_EXT_LINK_TT . '">' . $linktext . '<span class="wikiextlink"> </span></a>';
1649
            }
1650
        }
1651
1652
        return $ret;
1653
    }
1654
1655
    /**
1656
     * header support callback
1657
     *
1658
     * @param string[] $matches preg_replace_callback matches
1659
     *
1660
     * @return string
1661
     */
1662
    private function renderHeader($matches)
1663
    {
1664
        $source                          = $matches[3];
1665
        $level                           = $matches[2];
1666
        $level                           = strlen($level) + 1;
1667
        $this->tocQueue[$this->tocIndex] = array('level' => $level, 'name' => $source);
1668
        $toc                             = "\n<h" . $level . ' id="' . $this->tocIdPrefix . $this->tocIndex . '" >' . $source . '</h' . $level . ">\n";
1669
        ++$this->tocIndex;
1670
1671
        return $toc;
1672
    }
1673
1674
    /**
1675
     * indent support callback
1676
     *
1677
     * @param string[] $matches preg_replace_callback matches
1678
     *
1679
     * @return string
1680
     */
1681
    private function renderIndent($matches)
1682
    {
1683
        $source = $matches[2];
1684
        $level  = $matches[1];
1685
        $level  = strlen($level);
1686
        $ret    = "\n<div class=\"wikiindent{$level}\">\n{$source}\n</div>";
1687
1688
        return $ret;
1689
    }
1690
1691
    /**
1692
     * table of contents support callback
1693
     *
1694
     * @param string[] $matches preg_replace_callback matches
1695
     *
1696
     * @return string
1697
     */
1698
    private function renderToc($matches)
0 ignored issues
show
Unused Code introduced by
The parameter $matches is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1699
    {
1700
        $tocq = $this->tocQueue;
1701
        $toc  = '';
1702
        foreach ($tocq as $i => $v) {
1703
            $toc .= '<li class="wikitoclevel' . $v['level'] . '"><a href="' . sprintf($this->tocAnchorFmt, $this->tocIdPrefix . $i) . '">' . strip_tags($v['name']) . '</a></li>';
1704
        }
1705
        if (!empty($toc)) {
1706
            $toc = '<div class="wikitoc"><div class="wikitocheader">' . _MD_GWIKI_TOC . '</div><ul class="wikitoclist">' . $toc . '</ul></div>';
1707
        }
1708
1709
        return $toc;
1710
    }
1711
1712
    /**
1713
     * fetch table of contents for a page
1714
     *
1715
     * @param string $page keyword
1716
     *
1717
     * @return array|bool
1718
     */
1719
    public function fetchPageSetToc(&$page)
1720
    {
1721
        global $xoopsDB;
1722
        $toc = false;
1723
1724
        $q_page = $this->escapeForDB($page);
1725
1726
        $sql = 'SELECT gwiki_id, keyword, display_keyword, page_set_home, page_set_order, toc_cache ';
1727
        $sql .= ' FROM ' . $xoopsDB->prefix('gwiki_pages');
1728
        $sql .= " WHERE active=1 and keyword='{$q_page}' ";
1729
1730
        $result = $xoopsDB->query($sql);
1731
1732
        $rows = $xoopsDB->getRowsNum($result);
1733
        if ($rows) {
1734
            $row = $xoopsDB->fetchArray($result);
1735
            if (!empty($row['page_set_home'])) {
1736
                $page   = $row['page_set_home']; // this is passed back up to caller!
1737
                $q_page = $this->escapeForDB($row['page_set_home']);
1738
                $xoopsDB->freeRecordSet($result);
1739
                $sql = 'SELECT gwiki_id, keyword, display_keyword, page_set_home, page_set_order, toc_cache ';
1740
                $sql .= ' FROM ' . $xoopsDB->prefix('gwiki_pages');
1741
                $sql .= " WHERE active=1 and page_set_home='{$q_page}' ";
1742
                $sql .= ' ORDER BY page_set_order, keyword ';
1743
1744
                $result = $xoopsDB->query($sql);
1745
                while ($row = $xoopsDB->fetchArray($result)) {
1746
                    $row['display_keyword'] = strip_tags($row['display_keyword']);
1747
                    if (!empty($row['toc_cache'])) {
1748
                        $tmp = unserialize($row['toc_cache']);
1749
                        foreach ($tmp as $i => $v) {
1750
                            $tmp[$i]['name'] = strip_tags($tmp[$i]['name']);
1751
                        }
1752
                        $row['toc'] = $tmp;
1753
1754
                        $toc[] = $row;
1755
                    }
1756
                }
1757
            }
1758
        }
1759
        $xoopsDB->freeRecordSet($result);
1760
1761
        return $toc;
1762
    }
1763
1764
    /**
1765
     * page set toc support callback
1766
     *
1767
     * @param string[] $matches preg_replace_callback matches
1768
     *
1769
     * @return string
1770
     */
1771
    public function renderPageSetTocWrapper($matches)
0 ignored issues
show
Unused Code introduced by
The parameter $matches is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1772
    {
1773
        return $this->renderPageSetToc($this->keyword, 6);
1774
    }
1775
1776
    /**
1777
     * render a table of contents
1778
     *
1779
     * @param string  $page     keyword
1780
     * @param integer $level    level limit
1781
     * @param string  $tocclass base class for toc. Current level will be appended
1782
     *
1783
     * @return bool|string
1784
     */
1785
    public function renderPageSetToc(&$page, $level, $tocclass = 'wikitocpage')
1786
    {
1787
        $toc = $this->fetchPageSetToc($page);
1788
        if (!$toc) {
1789
            return false;
1790
        }
1791
        $tocout = '';
1792
        foreach ($toc as $ti => $tv) {
0 ignored issues
show
Bug introduced by
The expression $toc of type boolean is not traversable.
Loading history...
1793
            //$link=sprintf($this->getWikiLinkURL(),$tv['keyword']);
1794
            foreach ($tv['toc'] as $i => $v) {
1795
                if ((int)$v['level'] <= $level) {
1796
                    $tocout .= '<li class="wikitoclevel' . $v['level'] . '"><a href="' . sprintf($this->getWikiLinkURL(), $tv['keyword'] . sprintf($this->tocAnchorFmt, $this->tocIdPrefix . $i)) . '">' . $v['name'] . '</a></li>';
1797
                }
1798
            }
1799
        }
1800
        if (!empty($tocout)) {
1801
            $tocout = '<div class="' . $tocclass . '"><ul class="wikitoclist">' . $tocout . '</ul></div>';
1802
        }
1803
1804
        return $tocout;
1805
    }
1806
1807
    /**
1808
     * render navigation for a page set
1809
     * @param $page
1810
     *
1811
     * @return mixed
1812
     */
1813
    public function renderPageSetNav($page)
1814
    {
1815
        $sethome = $page;
1816
        $toc     = $this->fetchPageSetToc($sethome); // this will set home
1817
        if (!$toc) {
1818
            return false;
1819
        }
1820
        $home    = -1;
1821
        $current = -1;
1822
        $prev    = -1;
1823
        $next    = -1;
1824
        foreach ($toc as $i => $v) {
0 ignored issues
show
Bug introduced by
The expression $toc of type boolean is not traversable.
Loading history...
1825
            if (strcasecmp($toc[$i]['keyword'], $page) === 0) {
1826
                $current = $i;
1827
            }
1828
            if (strcasecmp($toc[$i]['keyword'], $sethome) === 0) {
1829
                $home = $i;
1830
            }
1831
        }
1832
1833
        if ($current > -1) {
1834
            $prev = $current - 1;
1835
            $next = $current + 1;
1836
        }
1837
1838
        $first = 0;
1839
        $last  = count($toc) - 1;
1840
1841
        // should these wrap instead?
1842
        if ($next > $last) {
1843
            $next = $last;
1844
        }
1845
        if ($prev < 0) {
1846
            $prev = 0;
1847
        }
1848
        if ($home < 0) {
1849
            $home = 0;
1850
        }
1851
1852
        $pageset['first'] = array(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$pageset was never initialized. Although not strictly required by PHP, it is generally a good practice to add $pageset = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1853
            'link' => sprintf($this->getWikiLinkURL(), $toc[$first]['keyword']),
1854
            'text' => htmlentities($toc[$first]['display_keyword'], ENT_QUOTES),
1855
            'desc' => _MD_GWIKI_PAGENAV_FIRST
1856
        );
1857
1858
        $pageset['prev'] = array(
1859
            'link' => sprintf($this->getWikiLinkURL(), $toc[$prev]['keyword']),
1860
            'text' => htmlentities($toc[$prev]['display_keyword'], ENT_QUOTES),
1861
            'desc' => _MD_GWIKI_PAGENAV_PREV
1862
        );
1863
1864
        $pageset['home'] = array(
1865
            'link' => sprintf($this->getWikiLinkURL(), $toc[$home]['keyword']),
1866
            'text' => htmlentities($toc[$home]['display_keyword'], ENT_QUOTES),
1867
            'desc' => _MD_GWIKI_PAGENAV_TOP
1868
        );
1869
1870
        $pageset['next'] = array(
1871
            'link' => sprintf($this->getWikiLinkURL(), $toc[$next]['keyword']),
1872
            'text' => htmlentities($toc[$next]['display_keyword'], ENT_QUOTES),
1873
            'desc' => _MD_GWIKI_PAGENAV_NEXT
1874
        );
1875
1876
        $pageset['last'] = array(
1877
            'link' => sprintf($this->getWikiLinkURL(), $toc[$last]['keyword']),
1878
            'text' => htmlentities($toc[$last]['display_keyword'], ENT_QUOTES),
1879
            'desc' => _MD_GWIKI_PAGENAV_LAST
1880
        );
1881
1882 View Code Duplication
        if (strcasecmp($toc[$first]['keyword'], $page) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1883
            $pageset['first']['link'] = 'javascript:void(0)';
1884
        }
1885 View Code Duplication
        if (strcasecmp($toc[$prev]['keyword'], $page) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1886
            $pageset['prev']['link'] = 'javascript:void(0)';
1887
        }
1888 View Code Duplication
        if (strcasecmp($toc[$home]['keyword'], $page) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1889
            $pageset['home']['link'] = 'javascript:void(0)';
1890
        }
1891 View Code Duplication
        if (strcasecmp($toc[$next]['keyword'], $page) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1892
            $pageset['next']['link'] = 'javascript:void(0)';
1893
        }
1894 View Code Duplication
        if (strcasecmp($toc[$last]['keyword'], $page) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1895
            $pageset['last']['link'] = 'javascript:void(0)';
1896
        }
1897
1898
        return $pageset;
1899
    }
1900
1901
    /**
1902
     * get image libraries for a page
1903
     *
1904
     * @param string $keyword keyword of page
1905
     *
1906
     * @return string[]
1907
     */
1908
    public function getImageLib($keyword)
1909
    {
1910
        $lib = $this->imageLib;
1911
        array_unshift($lib, $keyword);
1912
1913
        return array_unique($lib);
1914
    }
1915
1916
    /**
1917
     * get an image relative to specified page
1918
     *
1919
     * return array includes:
1920
     *   image_id
1921
     *   keyword
1922
     *   image_name
1923
     *   image_alt_text
1924
     *   image_file
1925
     *
1926
     * @param string $keyword keyword of page
1927
     * @param string $name    image name
1928
     *
1929
     * @return array|bool image data or false if invalid or not found
1930
     */
1931
    public function getPageImage($keyword, $name)
1932
    {
1933
        global $xoopsDB;
1934
1935
        if (strncasecmp($name, 'http://', 7) === 0 || strncasecmp($name, 'https://', 8) === 0) {
1936
            return false;
1937
        }
1938
        $lib = $this->imageLib;
1939
        array_unshift($lib, $keyword);
1940
        foreach ($lib as $page) {
1941
            $sql = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_page_images') . ' WHERE keyword=\'' . $this->escapeForDB($page) . '\' ';
1942
            $sql .= ' AND image_name=\'' . $this->escapeForDB($name) . '\' ';
1943
            $result = $xoopsDB->query($sql);
1944
            if ($image = $xoopsDB->fetchArray($result)) {
1945
                return $image;
1946
            }
1947
        }
1948
1949
        return false;
1950
    }
1951
1952
    /**
1953
     * render image support callback
1954
     *
1955
     * @param string[] $matches preg_replace_callback matches
1956
     *
1957
     * @return string
1958
     */
1959
    private function renderImage($matches)
1960
    {
1961
        $source = trim($matches[1]);
1962
        $pos    = strpos($source, '|');
1963
        //if($pos===false) $pos=strpos($source,' ');
1964
        if ($pos === false) { // no delimter - whole thing is the image url
1965
            $link  = $source;
1966
            $parms = array();
1967
        } else {
1968
            $link  = trim(substr($source, 0, $pos));
1969
            $parms = explode('|', trim(substr($source, $pos + 1)));
1970
            foreach ($parms as $i => $parm) {
1971
                $parms[$i] = trim($parm);
1972
            }
1973
        }
1974 View Code Duplication
        if (strcasecmp('siteurl:', substr($link, 0, 8)) === 0) { // explicit reference to our site
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
1975
            $link = XOOPS_URL . substr($link, 8);
1976
        }
1977
        $showthumb     = false;
1978
        $showthumblink = false;
1979
        if (strcasecmp('thumb:', substr($link, 0, 6)) === 0) { // explicit request for thumbnail, links to full image
1980
            $revertlink    = $link;
1981
            $link          = substr($link, 6);
1982
            $showthumblink = true;
1983
        }
1984
        $alttext = empty($parms[0]) ? '' : $parms[0];
1985
        $align   = empty($parms[1]) ? '' : $parms[1];
0 ignored issues
show
Unused Code introduced by
$align is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1986
        $maxpx   = empty($parms[2]) ? '' : (int)$parms[2];
1987
1988
        // align must be left, right, center or empty
1989
        $align = '';
1990
        if (strcasecmp($align, 'left') === 0) {
1991
            $align = 'left';
1992
        } elseif (strcasecmp($align, 'right') === 0) {
1993
            $align = 'right';
1994
        } elseif (strcasecmp($align, 'center') === 0) {
1995
            $align = 'center';           
1996
        }
1997
1998
        $alignparm = '';
1999
        if ($align === 'left' || $align === 'right' || $align === 'center') {
2000
            $alignparm = ', ' . $align;
2001
        }
2002
2003
        // look up link in page_images table, if found use that, otherwise just pass on link as is
2004
        $image = $this->getPageImage($this->keyword, $link);
2005
        if ($image) {
2006
            // image array includes:
2007
            //   image_id
2008
            //   keyword
2009
            //   image_name
2010
            //   image_alt_text
2011
            //   image_file
2012
            //   use_to_represent
2013
            $link = XOOPS_URL . '/uploads/' . $this->wikiDir . '/' . $image['image_file'];
2014
            if (empty($alttext)) {
2015
                $alttext = $image['image_alt_text'];
2016
            }
2017
        } else {
2018
            // thumbs don't apply, so put everything back the way it was
2019
            if ($showthumblink) {
2020
                $link          = $revertlink;
0 ignored issues
show
Bug introduced by
The variable $revertlink does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2021
                $showthumblink = false;
2022
            }
2023
        }
2024
2025
        $alt = '';
2026
        //      $alttext=htmlspecialchars($alttext);
2027
        if (!empty($alttext)) {
2028
            $alt = " alt=\"{$alttext}\"  title=\"{$alttext}\" ";
2029
        }
2030
2031
        $maxpxstyle = '';
2032
        if (!empty($maxpx)) {
2033
            $maxpxstyle = " style=\"max-width:{$maxpx}px; max-height:{$maxpx}px; width:auto; height:auto;\" ";
2034
            $showthumb  = true; // trigger automatic thumbnail use
2035
        }
2036
2037
        if ($showthumb) {
2038
            $thumbsize = $this->defaultThumbSize;
2039
            if (!empty($maxpx)) {
2040
                $thumbsize = $maxpx;
2041
            }
2042
            $link = XOOPS_URL . '/modules/' . $this->wikiDir . '/getthumb.php?page=' . $image['keyword'] . '&name=' . urlencode($image['image_name']) . '&size=' . $thumbsize;
2043
        }
2044
2045
        if ($showthumblink) {
2046
            $ret       = '';
2047
            $thumbsize = $this->defaultThumbSize;
2048
            if (!empty($maxpx)) {
2049
                $thumbsize = $maxpx;
2050
            }
2051
            $thumb = XOOPS_URL . '/modules/' . $this->wikiDir . '/getthumb.php?page=' . $image['keyword'] . '&name=' . urlencode($image['image_name']) . '&size=' . $thumbsize;
2052
            $img   = XOOPS_URL . '/uploads/' . $this->wikiDir . '/' . $image['image_file'];
2053
            $ret .= '<a href="' . $img . '" ' . $alt . '><img src="' . $thumb . '"' . $alt . $maxpxstyle . '/></a>';
2054
        } else {
2055
            $ret = "<img class=\"wikiimage{$alignparm}\" src=\"{$link}\" {$alt}{$maxpxstyle} />";
2056
        }
2057
2058
        if ($align === 'center') {
2059
            $ret = '<div style="margin: 0 auto; text-align: center;">' . $ret . '</div>';
2060
        }
2061
2062
        return $ret;
2063
    }
2064
2065
    /**
2066
     * gallery support callback
2067
     *
2068
     * @param string[] $matches preg_replace_callback matches
2069
     *
2070
     * @return string
2071
     */
2072
    private function renderGallery($matches)
2073
    {
2074
        global $xoopsDB;
2075
2076
        $source = '';
2077
        if (isset($matches[1])) {
2078
            $source = $matches[1];
2079
        }
2080
        $maxpx = (int)trim($source);
2081
        if ($maxpx < 10) {
2082
            $maxpx = $this->defaultThumbSize;
2083
        }
2084
        $page = $this->keyword;
2085
2086
        $sql    = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_page_images') . ' WHERE keyword = \'' . $page . '\' ' . ' ORDER BY image_name ';
2087
        $result = $xoopsDB->query($sql);
2088
2089
        $dir  = $this->wikiDir;
2090
        $body = '<div class="wikigallery"><ul class="wikigalleryimg">';
2091
2092
        for ($i = 0, $iMax = $xoopsDB->getRowsNum($result); $i < $iMax; ++$i) {
2093
            $image = $xoopsDB->fetchArray($result);
2094
            $img   = XOOPS_URL . '/uploads/' . $dir . '/' . $image['image_file'];
2095
            $thumb = XOOPS_URL . '/modules/' . $dir . '/getthumb.php?page=' . $image['keyword'] . '&name=' . urlencode($image['image_name']) . '&size=' . $maxpx;
2096
            $alt   = htmlentities($image['image_alt_text'], ENT_QUOTES);
2097
            $name  = htmlentities($image['image_name'], ENT_QUOTES);
2098
            if (empty($alt)) {
2099
                $alt = $name;
2100
            }
2101
            $body .= '<li><a href="' . $img . '" title="' . $name . '"><img src="' . $thumb . '" alt="' . $alt . '" title="' . $alt . '" /></a></li>' . "\n";
2102
        }
2103
2104
        $body .= '</ul><br style="clear:both;" /></div>';
2105
2106
        return $body;
2107
    }
2108
2109
    /**
2110
     * list support callback
2111
     *
2112
     * @param string[] $matches preg_replace_callback matches
2113
     *
2114
     * @return string
2115
     */
2116
    private function renderLists($matches)
2117
    {
2118
        $lines = explode("\n", $matches[0]);
2119
        $last  = '';
2120
        foreach ($lines as $i => $line) {
2121
            $line = ltrim($line);
2122
            if (!empty($line)) {
2123
                $list    = '';
2124
                $p       = strpos($line, ' ');
2125
                $current = substr($line, 0, $p);
2126
                $x       = 0;
2127
                while (!empty($last[$x]) && !empty($current[$x]) && $last[$x] === $current[$x]) {
2128
                    ++$x;
2129
                }
2130
                // $x is where the last and current list prefixes differ
2131
                // close anything from $x to end in last
2132
                $close = strrev(substr($last, $x));
2133
                $y     = 0;
2134 View Code Duplication
                while (!empty($close[$y])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
2135
                    if ($close[$y] === '*') {
2136
                        $list .= '</li></ul>';
2137
                    } //.($x>0?'</li>':'');
2138
                    if ($close[$y] === '#') {
2139
                        $list .= '</li></ol>';
2140
                    } //.($x>0?'</li>':'');
2141
                    ++$y;
2142
                }
2143
                // open anything from $x to end in
2144
                $open = substr($current, $x);
2145
                $y    = 0;
2146 View Code Duplication
                while (!empty($open[$y])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
2147
                    if ($open[$y] === '*') {
2148
                        $list .= '<ul class="wikiulist">';
2149
                    }
2150
                    if ($open[$y] === '#') {
2151
                        $list .= '<ol class="wikiolist">';
2152
                    }
2153
                    ++$y;
2154
                }
2155
                $endli     = ($last === $current) ? '</li>' : '';
2156
                $last      = $current;
2157
                $lines[$i] = $list . $endli . "\n<li> " . substr($line, $p + 1);
2158
            }
2159
        }
2160
2161
        // put list back together
2162
        $list = "\n";
2163
        foreach ($lines as $line) {
2164
            if (!empty($line)) {
2165
                $list .= $line;
2166
            }
2167
        }
2168
        // close anything left open
2169
        $close = strrev($last);
2170
        $y     = 0;
2171 View Code Duplication
        while (!empty($close[$y])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
2172
            if ($close[$y] === '*') {
2173
                $list .= "</li></ul>\n";
2174
            }
2175
            if ($close[$y] === '#') {
2176
                $list .= "</li></ol>\n";
2177
            }
2178
            ++$y;
2179
        }
2180
2181
        return $list;
2182
    }
2183
2184
    /**
2185
     * reference support callback
2186
     *
2187
     * @param string[] $matches preg_replace_callback matches
2188
     *
2189
     * @return string
2190
     */
2191
    private function renderRef($matches)
2192
    {
2193
        $refinfo      = $matches[1];
2194
        $source       = $matches[2];
2195
        $first_ref    = false;
2196
        $refs         = explode('|', trim($refinfo) . '|||');
2197
        $rq['id']     = $refs[0];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$rq was never initialized. Although not strictly required by PHP, it is generally a good practice to add $rq = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
2198
        $rq['first']  = $refs[1];
2199
        $rq['repeat'] = $refs[2];
2200
        $rq['source'] = $source;
2201
        $refid        = (-1);
2202
        if (!empty($rq['id'])) {
2203
            foreach ($this->refQueue as $i => $v) {
2204
                if ($v['id'] === $rq['id']) {
2205
                    $refid = $i;
2206
                }
2207
            }
2208
        }
2209
        if ($refid === (-1)) {
2210
            $refid                           = $this->refIndex;
2211
            $first_ref                       = true;
2212
            $this->refQueue[$this->refIndex] = $rq;
2213
            ++$this->refIndex;
2214
        }
2215
        $paren_ref = false;
2216
        if (!empty($this->refQueue[$refid]['first'])) {
2217
            $paren_ref = true;
2218
        }
2219
        if ($paren_ref) {
2220
            $ref_text = $this->refQueue[$refid]['first'];
2221
            if (!$first_ref) {
2222
                if (!empty($this->refQueue[$refid]['repeat'])) {
2223
                    $ref_text = $this->refQueue[$refid]['repeat'];
2224
                }
2225
            }
2226
            $r = '<span class="wikiparenref"><a href="#ref' . $refid . '">(' . $ref_text . ')</a></span>';
2227
        } else {
2228
            $r = '<span class="wikinumref"><a href="#ref' . $refid . '">' . ($refid + 1) . '</a></span>';
2229
        }
2230
2231
        return $r;
2232
    }
2233
2234
    /**
2235
     * reference list support callback
2236
     *
2237
     * @param string[] $matches preg_replace_callback matches
2238
     *
2239
     * @return string
2240
     */
2241
    private function renderRefList($matches)
0 ignored issues
show
Unused Code introduced by
The parameter $matches is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2242
    {
2243
        $this->refShown = true;
2244
        $r              = '<div class="wikicitelist">';
2245
2246
        foreach ($this->refQueue as $i => $v) {
2247
            $refid = $i;
2248
            if (empty($v['first'])) {
2249
                $r .= '<div class="wikicitenumitem" id="ref' . $refid . '"><span class="wikicitenum">' . ($refid + 1) . '. </span>' . $v['source'] . "</div>\n";
2250
            } else {
2251
                $r .= '<div class="wikiciteparenitem" id="ref' . $refid . '">' . $v['source'] . "</div>\n";
2252
            }
2253
        }
2254
2255
        $r .= '</div>';
2256
2257
        return $r;
2258
    }
2259
2260
    /**
2261
     * box support callback
2262
     *
2263
     * @param string[] $matches preg_replace_callback matches
2264
     *
2265
     * @return string
2266
     */
2267
    private function renderBox($matches)
2268
    {
2269
        $type  = $matches[1];
2270
        $title = $matches[2];
2271
        $body  = $matches[3];
2272
        // make sure we have a valid type
2273
        $type = strtolower($type);
2274
        if (!($type === 'code' || $type === 'info' || $type === 'note' || $type === 'tip' || $type === 'warn' || $type === 'folded')) {
2275
            $type = 'info';
2276
        }
2277
2278
        // $title may include options ( title | align ) :
2279
        //   align: adds additonal class 'left' or 'right' to box so css can alter float, size, etc.
2280
        $title    = trim($title);
2281
        $eclass   = '';
2282
        $ejs      = '';
2283
        $etooltip = '';
2284
        $pos      = strpos($title, '|');
2285
        if ($pos !== false) { // if no delimiter - whole thing is the title
2286
            $parms = explode('|', $title);
2287
            $title = $parms[0];
2288
            if (!empty($parms[1]) && ($parms[1] === 'left' || $parms[1] === 'right')) {
2289
                $eclass = ' ' . $parms[1];
2290
            }
2291
        }
2292
        if ($type === 'folded') {
2293
            $foldclass   = 'wikifolded' . $eclass;
2294
            $unfoldclass = 'wikiunfolded' . $eclass;
2295
            $ejs         = ' onclick="var c=this.className; if(c==\'' . $foldclass . '\') this.className=\'' . $unfoldclass . '\'; else this.className=\'' . $foldclass . '\';"';
2296
            $etooltip    = '<span>' . _MD_GWIKI_FOLDED_TT . '</span>';
2297
        }
2298
2299
        $ret = '<div class="wiki' . $type . $eclass . '"' . $ejs . '><div class="wiki' . $type . 'icon"></div><div class="wiki' . $type . 'title">' . $title . $etooltip . '</div><div class="wiki' . $type . 'inner">' . $body . '<br clear="all" /></div></div>' . "\n\n";
2300
2301
        return $ret;
2302
    }
2303
2304
    /**
2305
     * Convert entities
2306
     *
2307
     * @param string $body wiki text to process
2308
     *
2309
     * @return string
2310
     */
2311
    private function convertEntities($body)
2312
    {
2313
        // convert some entites
2314
        $sym   = array();
2315
        $ent   = array();
2316
        $sym[] = '{cent}';
2317
        $ent[] = '&cent;';
2318
        $sym[] = '{pound}';
2319
        $ent[] = '&pound;';
2320
        $sym[] = '{yen}';
2321
        $ent[] = '&yen;';
2322
        $sym[] = '{euro}';
2323
        $ent[] = '&euro;';
2324
        $sym[] = '{c}';
2325
        $ent[] = '&copy;';
2326
        $sym[] = '(c)';
2327
        $ent[] = '&copy;';
2328
        $sym[] = '{r}';
2329
        $ent[] = '&reg;';
2330
        $sym[] = '(r)';
2331
        $ent[] = '&reg;';
2332
        $sym[] = '{tm}';
2333
        $ent[] = '&trade;';
2334
        $sym[] = '(tm)';
2335
        $ent[] = '&trade;';
2336
        $sym[] = '{sm}';
2337
        // very poor font support for unicode code point for service mark, fake with markup
2338
        $ent[] = '<span style="font-size: 50%; vertical-align: super;">SM</span>';
2339
        $sym[] = '{nbsp}';
2340
        $ent[] = '&nbsp;';
2341
2342
        $body = str_ireplace($sym, $ent, $body);
2343
2344
        return $body;
2345
    }
2346
2347
    /**
2348
     * Render a teaser section. If page includes a {more} tag, teaser will be text that preceeds it.
2349
     * Otherwise try to break semantically at about 400 characters.
2350
     *
2351
     * @param string|null $body  text to process, defaults to current page body
2352
     * @param string|null $title title to use
2353
     *
2354
     * @return string
2355
     */
2356
    public function renderTeaser($body = null, $title = null)
2357
    {
2358
        // chop body at more tag if it is set
2359
        $splitsize = 400; // arbitrary size to use when no {more} tag
2360
        if (empty($body)) {
2361
            $body = $this->body;
2362
        }
2363
        $pos = stripos($body, '{more}');
2364
        if ($pos === false && strlen($body) > $splitsize) {
2365
            $search  = "#\r\n?#";
2366
            $replace = "\n";
2367
            $body    = preg_replace($search, $replace, $body);
2368
            $pos     = stripos($body, "\n\n", $splitsize); // hopefully the end of a paragraph
2369
        }
2370
        if ($pos !== false) {
2371
            $body = substr($body, 0, $pos);
2372
            $url  = sprintf($this->wikiLinkURL, $this->keyword);
2373
        }
2374
2375
        $body = str_ireplace('{toc}', '', $body);
2376
        $body = $this->renderPage($body, $title);
2377
        if ($pos !== false) {
2378
            $body .= '<a href="' . $url . '#more"><span class="wikimore">' . _MD_GWIKI_MORE . '</span></a>';
0 ignored issues
show
Bug introduced by
The variable $url does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2379
        }
2380
2381
        return $body;
2382
    }
2383
2384
    /**
2385
     * block quote support callback
2386
     *
2387
     * @param string[] $matches preg_replace_callback matches
2388
     *
2389
     * @return string
2390
     */
2391
    private function renderBlockquote($matches)
2392
    {
2393
        $src = str_replace("\n", ' ', preg_replace('#^> #m', '', $matches[0]));
2394
2395
        return '<blockquote class=\"wikiquote\">' . $src . "</blockquote>\n";
2396
    }
2397
2398
    /**
2399
     * preformatted support callback
2400
     *
2401
     * @param string[] $matches preg_replace_callback matches
2402
     *
2403
     * @return string
2404
     */
2405
    private function renderPreformat($matches)
2406
    {
2407
        $src = preg_replace('#^. #m', '', $matches[0]);
2408
2409
        return '<pre>' . $src . "</pre>\n";
2410
    }
2411
2412
    /**
2413
     * Render a page
2414
     *
2415
     * @param string|null $body  text to process, defaults to current page body
2416
     * @param string|null $title title to use, defaults to current page title
2417
     *
2418
     * @return string
2419
     */
2420
    public function renderPage($body = null, $title = null)
2421
    {
2422
        if (empty($body)) {
2423
            $body = $this->body;
2424
        }
2425
        $this->renderedPage = '';
2426
        $this->noWikiQueue  = array();
2427
        $this->noWikiIndex  = 0;
2428
        $this->refShown     = false;
2429
2430
        if (empty($title)) {
2431
            $title = $this->title;
2432
        }
2433
        // do first because title should always be #toc0 - set in template
2434
        $this->renderHeader(array('', '', '', $title));
2435
2436
        $body .= "\n";
2437
2438
        // eliminate double line endings
2439
        $search  = "#\r\n?#";
2440
        $replace = "\n";
2441
        $body    = preg_replace($search, $replace, $body);
2442
2443
        // neuter html tags
2444
        $search  = '#<#';
2445
        $replace = '&lt;';
2446
        $body    = preg_replace($search, $replace, $body);
2447
2448
        // neuter single quotes
2449
        $search  = "#'#";
2450
        $replace = "\\'";
2451
        $body    = preg_replace($search, $replace, $body);
2452
2453
        // nowiki - tilde escape
2454
        $search  = "#~([^ \t\r\n\v\f])#U";
2455
        $replace = array($this, 'noWikiHoldInline');
2456
        $body    = preg_replace_callback($search, $replace, $body);
2457
2458
        // nowiki content gwiki style
2459
        $search  = '#{nowiki}(.*){endnowiki}#Umsi';
2460
        $replace = array($this, 'noWikiHoldInline');
2461
        $body    = preg_replace_callback($search, $replace, $body);
2462
2463
        // nowiki content block creole style (a nowiki that forces a style, how odd.)
2464
        $search  = "#^{{{\n(.*)^}}}\n#Umsi";
2465
        $replace = array($this, 'noWikiHoldBlock');
2466
        $body    = preg_replace_callback($search, $replace, $body);
2467
2468
        // nowiki content inline creole style
2469
        $search  = '#{{{(.*)}}}#U';
2470
        $replace = array($this, 'noWikiHoldWCInline');
2471
        $body    = preg_replace_callback($search, $replace, $body);
2472
2473
        // automatically nowiki content of code box - {code title}xxx{endcode}
2474
        $search  = "#({code [^\"<\n]+?})(.*?)({endcode})#si";
2475
        $replace = array($this, 'noWikiHoldCode');
2476
        $body    = preg_replace_callback($search, $replace, $body);
2477
2478
        // center ++ xxx
2479
        $search  = "#^(\+{2})(.*)(?=\n\n|\Z)#Usm";
2480
        $replace = "<div style=\"text-align: center;\" class=\"wikicenter\">\n\\2\n</div>\n";
2481
        $body    = preg_replace($search, $replace, $body);
2482
2483
        // : indent up to 5 levels
2484
        $search  = "#^(\:{1,5})\s(.*)(?=\n\n|\Z)#Usm";
2485
        $replace = array($this, 'renderIndent');
2486
        $body    = preg_replace_callback($search, $replace, $body);
2487
2488
        // lists
2489
        $search  = "#^( *[*\#]{1,} (.*)\n)+#m";
2490
        $replace = array($this, 'renderLists');
2491
        $body    = preg_replace_callback($search, $replace, $body);
2492
2493
        // bold **xxx**
2494
        $search  = "#\*{2}(.*?)(\*{2}|(?=\n\n))#s";
2495
        $replace = "<strong class=\"wikistrong\">\\1</strong>";
2496
        $body    = preg_replace($search, $replace, $body);
2497
2498
        // italic //xxx//
2499
        $search  = "#(?<![:])/{2}(.*?[^:])(/{2}|(?=\n\n))#s";
2500
        $replace = "<em class=\"wikiem\">\\1</em>";
2501
        $body    = preg_replace($search, $replace, $body);
2502
2503
        // horizontal rule ---- (not an empty strikethru; creole says 4 or more so this needs to go first)
2504
        $search  = "#^-{4,}$#m";
2505
        $replace = "\n<hr  class=\"wikihr\"/>\n";
2506
        $body    = preg_replace($search, $replace, $body);
2507
2508
        // strikethru --xxx-- (this does NOT cross lines, as '--' is a common typographic convention
2509
        $search  = "#-{2}([^\s]{1}.*?)(-{2})#";
2510
        $replace = "<del class=\"wikidel\">\\1</del>";
2511
        $body    = preg_replace($search, $replace, $body);
2512
2513
        // underline __xxx__
2514
        $search  = "#(?<=\s)_{2}(.*?)(_{2}|(?=\n\n))#s";
2515
        $replace = "<span class=\"wikiu\">\\1</span>";
2516
        $body    = preg_replace($search, $replace, $body);
2517
2518
        // superscript ^^xxx^^
2519
        $search  = "#\^{2}(.*?)(\^{2}|(?=\n\n))#s";
2520
        $replace = "<sup class=\"wikisup\">\\1</sup>";
2521
        $body    = preg_replace($search, $replace, $body);
2522
2523
        // subscript ,,xxx,,
2524
        $search  = "#,{2}(.*?)(,{2}|(?=\n\n))#s";
2525
        $replace = "<sub class=\"wikisub\">\\1</sub>";
2526
        $body    = preg_replace($search, $replace, $body);
2527
2528
        // monospace ##xxx##
2529
        $search  = "#\#{2}(.*?)(\#{2}|(?=\n\n))#s";
2530
        $replace = "<span class=\"wikitt\">\\1</span>";
2531
        $body    = preg_replace($search, $replace, $body);
2532
2533
        // color !!color:xxx!!
2534
        $search  = "#!{2}(\#{0,1}[0-9A-Za-z]*):(.*?)(!{2}|(?=\n\n))#s";
2535
        $replace = "<span style=\"color:\\1;\">\\2</span>";
2536
        $body    = preg_replace($search, $replace, $body);
2537
2538
        // color !!color,background:xxx!!
2539
        $search  = "#!{2}(\#{0,1}[0-9A-Za-z]*),(\#{0,1}[0-9A-Za-z]*):(.*?)(!{2}|(?=\n\n))#s";
2540
        $replace = "<span style=\"color:\\1; background-color:\\2;\">\\3</span>";
2541
        $body    = preg_replace($search, $replace, $body);
2542
2543
        // forced line break creole style \\, just a bare break tag
2544
        $search  = "#(\\\{2})#i";
2545
        $replace = '<br />';
2546
        $body    = preg_replace($search, $replace, $body);
2547
2548
        // forced line break blog [[br]] or gwiki {break} styles, themed - by default clear all
2549
        $search  = "#(\[\[BR\]\]|{break})#i";
2550
        $replace = '<br class="wikibreak" />';
2551
        $body    = preg_replace($search, $replace, $body);
2552
2553
        // image {{image url|alt text|align|max width in pixels}}
2554
        $search  = "#\{{2}(.*)\}{2}#Um";
2555
        $replace = array($this, 'renderImage');
2556
        $body    = preg_replace_callback($search, $replace, $body);
2557
2558
        // info box {info title}xxx{endinfo}
2559
        $search  = "#{(info) ([^\"<\n]+?)?}(.*?){endinfo}#si";
2560
        $replace = array($this, 'renderBox');
2561
        $body    = preg_replace_callback($search, $replace, $body);
2562
2563
        // note box {note title}xxx{endnote}
2564
        $search  = "#{(note) ([^\"<\n]+?)?}(.*?){endnote}#si";
2565
        $replace = array($this, 'renderBox');
2566
        $body    = preg_replace_callback($search, $replace, $body);
2567
2568
        // tip box {tip title}xxx{endtip}
2569
        $search  = "#{(tip) ([^\"<\n]+?)?}(.*?){endtip}#si";
2570
        $replace = array($this, 'renderBox');
2571
        $body    = preg_replace_callback($search, $replace, $body);
2572
2573
        // warning box {warning title}xxx{endwarning}
2574
        $search  = "#{(warn)ing ([^\"<\n]+?)?}(.*?){endwarning}#si";
2575
        $replace = array($this, 'renderBox');
2576
        $body    = preg_replace_callback($search, $replace, $body);
2577
2578
        // code (preformatted) box {code title}xxx{endcode}
2579
        $search  = "#{(code) ([^\"<\n]+?)?}(.*?){endcode}#si";
2580
        $replace = array($this, 'renderBox');
2581
        $body    = preg_replace_callback($search, $replace, $body);
2582
2583
        // folded box {folded title}xxx{endfolded}
2584
        $search  = "#{(folded) ([^\"<\n]+?)?}(.*?){endfolded}#si";
2585
        $replace = array($this, 'renderBox');
2586
        $body    = preg_replace_callback($search, $replace, $body);
2587
2588
        // urls - smells like a link
2589
        $search  = "#(?<=\s)((http|https|ftp|ftps)://.{2,}\..*)(?=[,.?!:;]{0,1}\s)#Ui";
2590
        $replace = array($this, 'renderLink');
2591
        $body    = preg_replace_callback($search, $replace, $body);
2592
2593
        // link [[link|linktext]]
2594
        $search  = "#\[{2}(.*)\]{2}#Um";
2595
        $replace = array($this, 'renderLink');
2596
        $body    = preg_replace_callback($search, $replace, $body);
2597
2598
        // email [email protected]
2599
        $search  = "#(?<=\s)([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4})(?=\s)#i";
2600
        $replace = '<a href="mailto:\\1">\\1</a>';
2601
        $body    = preg_replace($search, $replace, $body);
2602
2603
        // CamelCase wiki link "#^([A-Z][a-z\:]+){2,}\d*$#"
2604
        // Could be between whitespace on either end or between > on start and/or < on end
2605
        if ($this->useCamelCase) {
2606
            $search  = "#(?<=\s|>)" . _WIKI_CAMELCASE_REGEX . "(?=\s|</l|</t)#";
2607
            $replace = array($this, 'wikiCCLink');
2608
            $body    = preg_replace_callback($search, $replace, $body);
2609
        }
2610
2611
        // =====headings up to 5 levels
2612
        $search  = "#(^\s{0,})(={1,5})([^=].*[^=])(={0,5})\s*$#Um";
2613
        $replace = array($this, 'renderHeader');
2614
        $body    = preg_replace_callback($search, $replace, $body);
2615
2616
        // blockquote > xxx
2617
        $search  = "#^(> .*\n)+#m";
2618
        $replace = array($this, 'renderBlockquote');
2619
        $body    = preg_replace_callback($search, $replace, $body);
2620
2621
        // preformated  .xxx
2622
        $search  = "#^(\. .*\n)+#m";
2623
        $replace = array($this, 'renderPreformat');
2624
        $body    = preg_replace_callback($search, $replace, $body);
2625
2626
        // reference {ref id|first-ref}source{endref}
2627
        $search  = "#{ref( [^\"<\n]+?)?}(.*?){endref}#si";
2628
        $replace = array($this, 'renderRef');
2629
        $body    = preg_replace_callback($search, $replace, $body);
2630
2631
        // forced line break blog [[br]] or gwiki {break} styles, themed - by default clear all
2632
        $search  = '#({reflist})#i';
2633
        $replace = array($this, 'renderRefList');
2634
        $body    = preg_replace_callback($search, $replace, $body);
2635
2636
        // index or change list {pageindex prefix}
2637
        $search  = "#{(PageIndex|RecentChanges)([^\"<\n]+?)?}#si";
2638
        $replace = array($this, 'renderIndex');
2639
        $body    = preg_replace_callback($search, $replace, $body);
2640
2641
        // table of contents
2642
        $search  = "#\{toc\}#i";
2643
        $replace = array($this, 'renderToc');
2644
        $body    = preg_replace_callback($search, $replace, $body);
2645
2646
        // page set table of contents
2647
        $search  = "#\{pagesettoc\}#i";
2648
        $replace = array($this, 'renderPageSetTocWrapper');
2649
        $body    = preg_replace_callback($search, $replace, $body);
2650
2651
        // image gallery {gallery size}
2652
        $search  = "#{gallery([^\"<\n]+?)?}#si";
2653
        $replace = array($this, 'renderGallery');
2654
        $body    = preg_replace_callback($search, $replace, $body);
2655
2656
        // more anchor - indicates end of teaser/summary
2657
        $search  = "#\{more\}#i";
2658
        $replace = '<span id="more"></span>';
2659
        $body    = preg_replace($search, $replace, $body);
2660
2661
        // tables
2662
        $search  = "#^( *\|((.*)\|){1,}\s*\n)+#m";
2663
        $replace = array($this, 'renderTables');
2664
        $body    = preg_replace_callback($search, $replace, $body);
2665
2666
        // paragraph on 2 consecutive newlines
2667
        $search  = "#\n{2}#";
2668
        $replace = "\n<p>";
2669
        $body    = preg_replace($search, $replace, $body);
2670
2671
        // restore cached nowiki content, all styles
2672
        // (if you need to use {PdNlNw:#} in your page, put it in a nowiki tag)
2673
        $search  = '#{PdNlNw:([0-9]{1,})}#';
2674
        $replace = array($this, 'noWikiEmit');
2675
        $body    = preg_replace_callback($search, $replace, $body);
2676
2677
        if ($this->refShown === false && $this->refIndex > 0) {
2678
            $body .= $this->renderRefList(null);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2679
        }
2680
        $body = stripslashes($this->convertEntities($body));
2681
2682
        $this->renderedPage = $body;
2683
2684
        return $this->renderedPage;
2685
    }
2686
}
2687