Issues (207)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

class/GwikiPage.php (44 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
        $moduleHelper = Xmf\Module\Helper::getHelper($dir);
106
107
        $this->wikiLinkURL      = $moduleHelper->getConfig('wikilink_template');
108
        $this->wikiHomePage     = $moduleHelper->getConfig('wiki_home_page');
109
        $this->dateFormat       = $moduleHelper->getConfig('date_format');
110
        $this->imageLib         = explode(',', $moduleHelper->getConfig('imagelib_pages'));
111
        $this->useCamelCase     = $moduleHelper->getConfig('allow_camelcase');
112
        $this->defaultThumbSize = $moduleHelper->getConfig('default_thumb_size');
113
        $this->autoNameFormat   = $moduleHelper->getConfig('auto_name_format');
114
        $this->module_id        = $moduleHelper->getModule()->getVar('mid');
115
116
        if (!defined('_MI_GWIKI_WIKIHOME')) {
117
            $this->loadLanguage('modinfo', $dir);
118
        }
119
        if (!defined('_MD_GWIKI_PAGE_PERM_EDIT_ANY_NUM')) {
120
            $this->loadLanguage('main', $dir);
121
        }
122
    }
123
124
    /**
125
     * load language resources
126
     *
127
     * @param string $name     language file name (main, modinfo, etc.)
128
     * @param string $domain   domain/module
129
     * @param null   $language language
130
     *
131
     * @return void
132
     */
133 View Code Duplication
    private function loadLanguage($name, $domain = '', $language = null)
0 ignored issues
show
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...
134
    {
135
        global $xoopsConfig;
136
        if (!@include_once XOOPS_ROOT_PATH . "/modules/{$domain}/language/" . $xoopsConfig['language'] . "/{$name}.php") {
137
            include_once XOOPS_ROOT_PATH . "/modules/{$domain}/language/english/{$name}.php";
138
        }
139
    }
140
141
    /**
142
     * Reset all page properties
143
     *
144
     * @return void
145
     */
146
    protected function resetPage()
147
    {
148
        $this->gwiki_id         = null;
149
        $this->keyword          = '';
150
        $this->display_keyword  = '';
151
        $this->title            = '';
152
        $this->body             = '';
153
        $this->parent_page      = '';
154
        $this->page_set_home    = '';
155
        $this->page_set_order   = '';
156
        $this->meta_description = '';
157
        $this->meta_keywords    = '';
158
        $this->lastmodified     = 0;
159
        $this->uid              = 0;
160
        $this->admin_lock       = 0;
161
        $this->active           = 0;
162
        $this->search_body      = '';
163
        $this->toc_cache        = '';
164
        $this->show_in_index    = 1;
165
        $this->gwiki_version    = $this->gwikiVersion;
166
167
        $this->page_id           = 0;
168
        $this->created           = 0;
0 ignored issues
show
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...
169
        $this->renderedPage      = '';
170
        $this->currentprefix     = '';
171
        $this->currentprefixid   = '';
172
        $this->currenttemplateid = 0;
173
        $this->attachments       = array();
174
        $this->tocQueue          = array();
175
        $this->tocIndex          = 0;
176
        $this->refQueue          = array();
177
        $this->refIndex          = 0;
178
        $this->refShown          = false;
179
        $this->wikiPageLinks     = array();
180
    }
181
182
    /**
183
     * escape a string to be "safe" for use in database
184
     *
185
     * @param string $value string to be escaped
186
     *
187
     * @return string
188
     */
189
    public function escapeForDB($value)
190
    {
191
        global $xoopsDB;
192
193
        return $value = $xoopsDB->escape($value);
0 ignored issues
show
$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...
194
    }
195
196
    /**
197
     * set the count for recent item list
198
     *
199
     * @param int $count item count
200
     *
201
     * @return void
202
     */
203
    public function setRecentCount($count)
204
    {
205
        $count = (int)$count;
206
        if ($count > 1 && $count < 1000) {
207
            $this->numberOfRecentItems = $count;
208
        }
209
    }
210
211
    /**
212
     * Set the URL pattern for wiki links
213
     *
214
     * @param string $url sprintf pattern for URL. Will get page name as parameter.
215
     *
216
     * @return void
217
     */
218
    public function setWikiLinkURL($url)
219
    {
220
        $this->wikiLinkURL = $url;
221
    }
222
223
    /**
224
     * Get the URL pattern for wiki links
225
     *
226
     * @return string
227
     */
228
    public function getWikiLinkURL()
229
    {
230
        return $this->wikiLinkURL;
231
    }
232
233
    /**
234
     * git the wiki directory (dirname)
235
     *
236
     * @return string
237
     */
238
    public function getWikiDir()
239
    {
240
        return $this->wikiDir;
241
    }
242
243
    /**
244
     * get max upload size from ini
245
     *
246
     * @return int|string
247
     */
248
    public function getMaxUploadSize()
249
    {
250
        $val  = trim(ini_get('upload_max_filesize'));
251
        $last = strtolower($val[strlen($val) - 1]);
252
        switch ($last) {
253
            // The 'G' modifier is available since PHP 5.1.0
254
            case 'g':
255
                $val *= 1024;
256
            // no break
257
            case 'm':
258
                $val *= 1024;
259
            // no break
260
            case 'k':
261
                $val *= 1024;
262
            // no break
263
        }
264
265
        return $val;
266
    }
267
268
    /**
269
     * set format for TOC links
270
     *
271
     * @param string $prefix     prefix
272
     * @param string $linkformat anchor
273
     *
274
     * @return void
275
     */
276
    public function setTocFormat($prefix, $linkformat)
277
    {
278
        $this->tocIdPrefix  = $prefix;
279
        $this->tocAnchorFmt = $linkformat;
280
    }
281
282
    /**
283
     * Make sure that keyword obeys formatting rules or switch to illegal name
284
     *
285
     * @param mixed $keyword - wiki page name
286
     *
287
     * @return string
288
     */
289
    public function makeKeyword($keyword)
290
    {
291
        if (!preg_match('#^' . _WIKI_KEYWORD_REGEX . '$#', $keyword)) {
292
            $keyword = _MI_GWIKI_WIKI404;
293
        } else { // check for undefined prefix
294
            $prefix = $this->getPrefix($keyword);
295
            if ($prefix && !$prefix['defined']) {
296
                $keyword = _MI_GWIKI_WIKI404;
297
            }
298
        }
299
300
        return $keyword;
301
    }
302
303
    /**
304
     * add namespace prefix to a wiki word
305
     *
306
     * @param int    $nsid namespace (prefix) id
307
     * @param string $page keyword
308
     *
309
     * @return bool|string
310
     */
311
    public function makeKeywordFromPrefix($nsid, $page)
312
    {
313
        if ($nsid >= 0) {
314
            $pfx = $this->getPrefixFromId($nsid);
315
            if (empty($page)) {
316
                if ($pfx['prefix_auto_name']) {
317
                    $page = date($this->autoNameFormat);
318
                } else {
319
                    $page = $pfx['prefix_home'];
320
                }
321
            }
322
            $page = $pfx['prefix'] . ':' . $page;
323
        }
324
325
        return $page;
326
    }
327
328
    /**
329
     * Capture out of bounds data traveling with keyword. Such data is sent
330
     * in keyword(oob) construct. This function processes any oob data and
331
     * returns a clean keyword.
332
     * oob data is used this way to pass page specific data in any url
333
     *
334
     * Only call this if you will NOT be calling normalizeKeyword or the
335
     * OOB data will be lost.
336
     *
337
     * @param mixed $keyword - wiki page name possibily containing OOB data
338
     *
339
     * @return string - keyword with no OOB data
340
     */
341
    public function getOOBFromKeyword($keyword)
342
    {
343
        $oob = null;
344
        if (substr($keyword, -1) === ')') {
345
            $lparen = strpos($keyword, '(');
346
            if ($lparen !== false) {
347
                $inparen = substr($keyword, $lparen);
348
                $inparen = substr($inparen, 1, -2);
349
                $keyword = substr($keyword, 0, $lparen);
350
                $oob     = $inparen;
351
            }
352
        }
353
        // currently this is the only use
354
        $this->pageIndexPrefix = strtolower($oob);
355
356
        return $keyword;
357
    }
358
359
    /**
360
     * If page exists, fix case of page name to that specified in database
361
     *
362
     * @param string $keyword - wiki page name
363
     *
364
     * @return string normalized keyword
365
     */
366
    public function normalizeKeyword($keyword)
367
    {
368
        global $xoopsDB;
369
370
        $keyword = $this->getOOBFromKeyword($keyword);
371
        $keyword = $this->escapeForDB($keyword);
372
        $sql     = 'SELECT keyword FROM ' . $xoopsDB->prefix('gwiki_pages') . " WHERE keyword='{$keyword}' AND active=1 ";
373
        $result  = $xoopsDB->query($sql);
374
        if ($content = $xoopsDB->fetchArray($result)) {
375
            $keyword = $content['keyword'];
376
        } else {
377
            $keyword = $this->makeKeyword($keyword);
378
        }
379
380
        return $keyword;
381
    }
382
383
    /**
384
     * Get the gwiki_id of the active page for the keyword
385
     *
386
     * @param mixed $keyword - wiki page name
387
     *
388
     * @return int - id of page
389
     */
390
    public function getCurrentId($keyword)
391
    {
392
        global $xoopsDB;
393
394
        $sql = 'SELECT gwiki_id FROM ' . $xoopsDB->prefix('gwiki_pages');
395
        $sql .= " WHERE keyword='{$keyword}' AND active = 1 ORDER BY gwiki_id DESC LIMIT 1";
396
        $result = $xoopsDB->query($sql);
397
        list($id) = $xoopsDB->fetchRow($result);
398
399
        return (int)$id;
400
    }
401
402
    /**
403
     * Add current page as a new revision
404
     *
405
     * @param bool $leave_inactive true to save page as inactive
406
     *
407
     * @return mixed
408
     */
409
    public function addRevision($leave_inactive = false)
410
    {
411
        global $xoopsDB;
412
413
        $page = $this->escapeForDB($this->keyword);
414
        if (empty($this->display_keyword)) {
415
            $this->display_keyword = $page;
416
        }
417
        $this->tocQueue      = array();
418
        $this->tocIndex      = 0;
419
        $this->refQueue      = array();
420
        $this->refIndex      = 0;
421
        $this->wikiPageLinks = array();
422
423
        // eliminate things we don't want in search page because they
424
        // are misleading and/or change outside of the page itself
425
        $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...
426
        $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...
427
        $search[]  = "#\{toc\}#i";
428
        $replace[] = '';
429
        $search[]  = "#\{pagesettoc\}#i";
430
        $replace[] = '';
431
        $tempbody  = preg_replace($search, $replace, $this->body) . "\n\n";
432
433
        $this->search_body   = strip_tags($this->renderPage($tempbody));
434
        $this->toc_cache     = serialize($this->tocQueue);
435
        $this->gwiki_version = $this->gwikiVersion; // new revisions always for current engine
436
437
        // if we are adding to a page set, auto increment the order if none specified
438
        if (!empty($this->page_set_home) && $this->page_set_order === '') {
439
            $this->page_set_order = $this->getNextPageSetOrder($this->page_set_home);
440
        }
441
442
        // this will usually fail (duplicate)
443
        $sql    = 'INSERT INTO ' . $xoopsDB->prefix('gwiki_pageids') . " (keyword, created) VALUES('{$page}', UNIX_TIMESTAMP())";
444
        $result = $xoopsDB->query($sql);
445
        if ($result) {
446
            $page_id       = $xoopsDB->getInsertId();
447
            $this->page_id = $page_id;
448
        }
449
        if ($leave_inactive) {
450
            // allow a save that is not activated (for conflict management, and maybe more)
451
            $this->active = 0;
452
            $sql          = 'INSERT INTO ' . $xoopsDB->prefix('gwiki_pages');
453
            $sql .= ' (keyword, display_keyword, title, body, parent_page, page_set_home, page_set_order';
454
            $sql .= ', meta_description, meta_keywords';
455
            $sql .= ', lastmodified, uid, admin_lock, active, search_body, toc_cache, show_in_index, gwiki_version)';
456
            $sql .= ' VALUES (';
457
            $sql .= '\'' . $page . '\' ,';
458
            $sql .= '\'' . $this->escapeForDB($this->display_keyword) . '\' ,';
459
            $sql .= '\'' . $this->escapeForDB($this->title) . '\' ,';
460
            $sql .= '\'' . $this->escapeForDB($this->body) . '\' ,';
461
            $sql .= '\'' . $this->escapeForDB($this->parent_page) . '\' ,';
462
            $sql .= '\'' . $this->escapeForDB($this->page_set_home) . '\' ,';
463
            $sql .= '\'' . $this->escapeForDB($this->page_set_order) . '\' ,';
464
            $sql .= '\'' . $this->escapeForDB($this->meta_description) . '\' ,';
465
            $sql .= '\'' . $this->escapeForDB($this->meta_keywords) . '\' ,';
466
            $sql .= 'UNIX_TIMESTAMP() ,';
467
            $sql .= '\'' . $this->escapeForDB($this->uid) . '\' ,';
468
            $sql .= '\'' . $this->escapeForDB($this->admin_lock) . '\' ,';
469
            $sql .= '\'' . $this->escapeForDB($this->active) . '\' ,';
470
            $sql .= '\'' . $this->escapeForDB($this->search_body) . '\' ,';
471
            $sql .= '\'' . $this->escapeForDB($this->toc_cache) . '\' ,';
472
            $sql .= '\'' . $this->escapeForDB($this->show_in_index) . '\' ,';
0 ignored issues
show
$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...
473
            $sql .= '\'' . $this->escapeForDB($this->gwiki_version) . '\' )';
474
            $result = $xoopsDB->query($sql);
475
            if ($result) {
476
                $result         = $xoopsDB->getInsertId();
477
                $this->gwiki_id = $result;
478
            }
479
        } else {
480
            $sql    = 'UPDATE ' . $xoopsDB->prefix('gwiki_pages') . " SET active = 0 WHERE keyword='{$page}' and active = 1 ";
481
            $result = $xoopsDB->query($sql);
482
            if ($result) {
483
                $previous_rows = $xoopsDB->getAffectedRows();
484
                $this->active  = 1;
485
                $sql           = 'INSERT INTO ' . $xoopsDB->prefix('gwiki_pages');
486
                $sql .= ' (keyword, display_keyword, title, body, parent_page, page_set_home, page_set_order';
487
                $sql .= ', meta_description, meta_keywords, lastmodified';
488
                $sql .= ', uid, admin_lock, active, search_body, toc_cache, show_in_index, gwiki_version)';
489
                $sql .= ' VALUES (';
490
                $sql .= '\'' . $page . '\' ,';
491
                $sql .= '\'' . $this->escapeForDB($this->display_keyword) . '\' ,';
492
                $sql .= '\'' . $this->escapeForDB($this->title) . '\' ,';
493
                $sql .= '\'' . $this->escapeForDB($this->body) . '\' ,';
494
                $sql .= '\'' . $this->escapeForDB($this->parent_page) . '\' ,';
495
                $sql .= '\'' . $this->escapeForDB($this->page_set_home) . '\' ,';
496
                $sql .= '\'' . $this->escapeForDB($this->page_set_order) . '\' ,';
497
                $sql .= '\'' . $this->escapeForDB($this->meta_description) . '\' ,';
498
                $sql .= '\'' . $this->escapeForDB($this->meta_keywords) . '\' ,';
499
                $sql .= 'UNIX_TIMESTAMP() ,';
500
                $sql .= '\'' . $this->escapeForDB($this->uid) . '\' ,';
501
                $sql .= '\'' . $this->escapeForDB($this->admin_lock) . '\' ,';
502
                $sql .= '\'' . $this->escapeForDB($this->active) . '\' ,';
503
                $sql .= '\'' . $this->escapeForDB($this->search_body) . '\' ,';
504
                $sql .= '\'' . $this->escapeForDB($this->toc_cache) . '\' ,';
505
                $sql .= '\'' . $this->escapeForDB($this->show_in_index) . '\' ,';
0 ignored issues
show
$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...
506
                $sql .= '\'' . $this->escapeForDB($this->gwiki_version) . '\' )';
507
                $result = $xoopsDB->query($sql);
508
                if ($result) {
509
                    $result         = $xoopsDB->getInsertId();
510
                    $this->gwiki_id = $result;
511
512
                    $this->updatePageLinks();
513
514
                    $notificationHandler = xoops_getHandler('notification');
515
                    $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...
516
                    $tags['PAGE_TITLE']  = $this->title;
517
                    if (empty($tags['PAGE_TITLE'])) {
518
                        $tags['PAGE_TITLE'] = $this->display_keyword;
519
                    }
520
                    if (empty($tags['PAGE_TITLE'])) {
521
                        $tags['PAGE_TITLE'] = $page;
522
                    }
523
                    $tags['PAGE_LINK'] = sprintf($this->wikiLinkURL, $page);
524
                    $tags['NAMESPACE'] = $this->currentprefix;
525
526
                    if ($previous_rows < 1) {
527
                        // only for new
528
                        $notificationHandler->triggerEvent('global', 0, 'new_page', $tags, array(), $this->module_id);
529
                        if ($this->currentprefixid) { // have namespace
530
                            $notificationHandler->triggerEvent('namespace', $this->currentprefixid, 'new_ns_page', $tags, array(), $this->module_id);
531
                        }
532
                    }
533
                    // for all cases (new is also an update)
534
                    $notificationHandler->triggerEvent('page', $this->page_id, 'page_watch', $tags, array(), $this->module_id);
535
                    $notificationHandler->triggerEvent('global', 0, 'upd_page', $tags, array(), $this->module_id);
536
                    if ($this->currentprefixid) { // have namespace
537
                        $notificationHandler->triggerEvent('namespace', $this->currentprefixid, 'upd_ns_page', $tags, array(), $this->module_id);
538
                    }
539
                }
540
            }
541
        }
542
543
        return $result;
544
    }
545
546
    /**
547
     * update gwiki_pagelinks table - expects $page to be current
548
     *
549
     * @param bool $render do a fresh page render before updating
550
     *
551
     * @return void
552
     */
553
    private function updatePageLinks($render = false)
554
    {
555
        global $xoopsDB;
556
557
        if ($render) {
558
            // eliminate things we don't want in search page because they
559
            // are misleading and/or change outside of the page itself
560
            $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...
561
            $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...
562
            $search[]  = "#\{toc\}#i";
563
            $replace[] = '';
564
            $search[]  = "#\{pagesettoc\}#i";
565
            $replace[] = '';
566
            $tempbody  = preg_replace($search, $replace, $this->body) . "\n\n";
567
568
            $this->renderPage($tempbody);
569
        }
570
        $page = $this->escapeForDB($this->keyword);
571
572
        $sql = 'DELETE FROM ' . $xoopsDB->prefix('gwiki_pagelinks');
573
        $sql .= ' WHERE from_keyword = \'' . $page . '\'';
574
        $result = $xoopsDB->query($sql);
0 ignored issues
show
$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...
575
576
        if (!empty($this->wikiPageLinks)) {
577
            $sql    = 'INSERT INTO ' . $xoopsDB->prefix('gwiki_pagelinks') . ' (from_keyword, to_keyword) VALUES ';
578
            $values = '';
579
            foreach ($this->wikiPageLinks as $i => $v) {
580
                if (!empty($values)) {
581
                    $values .= ', ';
582
                }
583
                $values .= '(\'' . $page . '\', \'' . $this->escapeForDB($i) . '\')';
584
            }
585
            $sql .= $values;
586
            $result = $xoopsDB->query($sql);
0 ignored issues
show
$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...
587
        }
588
589
        return;
590
    }
591
592
    /**
593
     * get the next higher unused page_set_order for a given page_set_home
594
     *
595
     * @param string $page_set_home keyword of page set home
596
     *
597
     * @return int
598
     */
599 View Code Duplication
    private function getNextPageSetOrder($page_set_home)
0 ignored issues
show
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...
600
    {
601
        global $xoopsDB;
602
603
        $page_set_order = 1;
604
605
        $keyword = $this->escapeForDB($page_set_home);
606
607
        $sql    = 'SELECT MAX(page_set_order) FROM ' . $xoopsDB->prefix('gwiki_pages') . " WHERE active = 1 and page_set_home = '{$keyword}' ";
608
        $result = $xoopsDB->query($sql);
609
        if ($result) {
610
            $myrow          = $xoopsDB->fetchRow($result);
611
            $page_set_order = $myrow[0] + 1;
612
        }
613
614
        return $page_set_order;
615
    }
616
617
    /**
618
     * Check if the current user may edit the current page
619
     * Since the class can be used outside the module where permissions are assigned, we have to work at this a bit
620
     *
621
     * @return boolean mayEdit
622
     */
623
    public function checkEdit()
624
    {
625
        global $xoopsUser, $xoopsDB;
626
627
        $mayEdit = false;
628
        $keyword = $this->keyword;
629
630
        $module_id = $this->module_id;
631
        $groups = XOOPS_GROUP_ANONYMOUS;
632
        if (is_object($xoopsUser)) {
633
            $groups = $xoopsUser->getGroups();
634
        }
635
636
        $gpermHandler = xoops_getHandler('groupperm');
637
638
        $edit_any   = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_EDIT_ANY_NUM, $groups, $module_id);
639
        $edit_pfx   = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_EDIT_PFX_NUM, $groups, $module_id);
640
        $create_any = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_CREATE_ANY_NUM, $groups, $module_id);
641
        $create_pfx = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_CREATE_PFX_NUM, $groups, $module_id);
642
643
        // check for namespace prefix
644
        $prefix = $this->getPrefix($keyword);
645
        if ($prefix) {
646
            if ($prefix['defined']) {
647 View Code Duplication
                if (is_array($groups)) {
0 ignored issues
show
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...
648
                    $groupwhere = ' IN (' . implode(', ', $groups) . ') ';
649
                } else {
650
                    $groupwhere = " = '" . $groups . "'";
651
                }
652
                $sql    = 'SELECT group_prefix_id FROM ' . $xoopsDB->prefix('gwiki_group_prefix') . ' WHERE prefix_id = \'' . $prefix['prefix_id'] . '\' AND group_id ' . $groupwhere;
653
                $result = $xoopsDB->query($sql);
654
                $rows   = $xoopsDB->getRowsNum($result);
655
                $xoopsDB->freeRecordSet($result);
656
                if ($rows) { // prefix is assigned to one or more of user's groups
657
                    if (($edit_pfx || $create_pfx) && $this->gwiki_id) {
658
                        $mayEdit = true;
659
                    }
660
                    if ($create_pfx && !$this->gwiki_id) {
661
                        $mayEdit = true;
662
                    }
663
                }
664
                if (($edit_any || $create_any) && $this->gwiki_id) {
665
                    $mayEdit = true;
666
                }
667
                if ($create_any && !$this->gwiki_id) {
668
                    $mayEdit = true;
669
                }
670
            } else { // allow edit, but no create if prefix is undefined
671
                if ($edit_any && $this->gwiki_id) {
672
                    $mayEdit = true;
673
                }
674
            }
675
        } else {
676
            if (($edit_any || $create_any) && $this->gwiki_id) {
677
                $mayEdit = true;
678
            }
679
            if ($create_any && !$this->gwiki_id) {
680
                $mayEdit = true;
681
            }
682
        }
683
684
        return $mayEdit;
685
    }
686
687
    /**
688
     * get user name based on id
689
     *
690
     * @param int $uid user id
691
     *
692
     * @return string
693
     */
694 View Code Duplication
    public function getUserName($uid)
0 ignored issues
show
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...
695
    {
696
        global $xoopsConfig;
697
698
        $uid = (int)$uid;
699
700
        if ($uid > 0) {
701
            $memberHandler = xoops_getHandler('member');
702
            $user          = $memberHandler->getUser($uid);
703
            if (is_object($user)) {
704
                return "<a href=\"" . XOOPS_URL . "/userinfo.php?uid=$uid\">" . htmlspecialchars($user->getVar('uname'), ENT_QUOTES) . '</a>';
705
            }
706
        }
707
708
        return $xoopsConfig['anonymous'];
709
    }
710
711
    /**
712
     * get array of prefixes user can edit
713
     *
714
     * @param boolean $createonly true to show only prefixes with create permission
715
     *
716
     * @return string[]|false
717
     */
718
    public function getUserNamespaces($createonly = false)
719
    {
720
        global $xoopsUser, $xoopsDB;
721
722
        $module_id = $this->module_id;
723
724
        $groups = XOOPS_GROUP_ANONYMOUS;
725
        if (is_object($xoopsUser)) {
726
            $groups = $xoopsUser->getGroups();
727
        }
728
729
        $gpermHandler = xoops_getHandler('groupperm');
730
731
        $edit_any   = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_EDIT_ANY_NUM, $groups, $module_id);
732
        $edit_pfx   = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_EDIT_PFX_NUM, $groups, $module_id);
733
        $create_any = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_CREATE_ANY_NUM, $groups, $module_id);
734
        $create_pfx = $gpermHandler->checkRight('wiki_authority', _MD_GWIKI_PAGE_PERM_CREATE_PFX_NUM, $groups, $module_id);
735
736 View Code Duplication
        if (is_array($groups)) {
0 ignored issues
show
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...
737
            $groupwhere = ' IN (' . implode(', ', $groups) . ') ';
738
        } else {
739
            $groupwhere = " = '" . $groups . "'";
740
        }
741
742
        $sql = 'SELECT distinct p.prefix_id, prefix FROM ';
743
        $sql .= $xoopsDB->prefix('gwiki_prefix') . ' p, ';
744
        $sql .= $xoopsDB->prefix('gwiki_group_prefix') . ' g ';
745
        $sql .= ' WHERE group_id ' . $groupwhere;
746
        $sql .= ' AND p.prefix_id = g.prefix_id';
747
        $sql .= ' ORDER BY prefix ';
748
        $prefixes = array();
749
        $result   = $xoopsDB->query($sql);
750
        if ($create_any) {
751
            $prefixes[] = array('prefix_id' => -1, 'prefix' => ' ');
752
        }
753
        while ($myrow = $xoopsDB->fetchArray($result)) {
754
            $prefixes[] = $myrow;
755
        }
756
757
        // make sure we have some edit/create permission. We need full keyword to be certain, so let edit sort it out.
758
        $mayEdit = ($edit_any || $create_any || $edit_pfx || $create_pfx);
759
        if ($createonly) {
760
            $mayEdit = ($create_any || $create_pfx);
761
        }
762
        if ($mayEdit) {
763
            return $prefixes;
764
        }
765
766
        return false;
767
    }
768
769
    /**
770
     * Get keyword for an id
771
     *
772
     * @param int $page_id id
773
     *
774
     * @return string|null wiki keyword (page)
775
     */
776
    public function getKeywordById($page_id)
777
    {
778
        global $xoopsDB;
779
780
        $keyword = null;
781
782
        $sql    = 'SELECT keyword FROM ' . $xoopsDB->prefix('gwiki_pageids') . " WHERE page_id = '{$page_id}' ";
783
        $result = $xoopsDB->query($sql);
784
        if ($result) {
785
            $myrow   = $xoopsDB->fetchRow($result);
786
            $keyword = $myrow[0];
787
        }
788
789
        return $keyword;
790
    }
791
792
    /**
793
     * lookup id for a keyword
794
     *
795
     * @param string $keyword keyword
796
     *
797
     * @return int
798
     */
799 View Code Duplication
    public function getPageId($keyword)
0 ignored issues
show
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...
800
    {
801
        global $xoopsDB;
802
803
        $page_id = 0;
804
805
        $keyword = $this->escapeForDB($keyword);
806
807
        $sql    = 'SELECT page_id FROM ' . $xoopsDB->prefix('gwiki_pageids') . " WHERE keyword = '{$keyword}' ";
808
        $result = $xoopsDB->query($sql);
809
        if ($result) {
810
            $myrow   = $xoopsDB->fetchRow($result);
811
            $page_id = $myrow[0];
812
        }
813
814
        return $page_id;
815
    }
816
817
    /**
818
     * record a page hit for a keyword
819
     *
820
     * @param string $keyword keyword
821
     *
822
     * @return int count of rows updated
823
     */
824
    public function registerHit($keyword)
825
    {
826
        global $xoopsDB;
827
828
        $keyword = $this->escapeForDB($keyword);
829
830
        $sql    = 'UPDATE ' . $xoopsDB->prefix('gwiki_pageids') . " SET hit_count = hit_count + 1 WHERE keyword = '{$keyword}' ";
831
        $result = $xoopsDB->queryF($sql);
0 ignored issues
show
$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...
832
833
        // nothing to do if it fails
834
        return $xoopsDB->getAffectedRows();
835
    }
836
837
    /**
838
     * set a specific revison as active for a keyword
839
     *
840
     * @param string $keyword wiki keyword
841
     * @param int    $id      id of revision to activate
842
     *
843
     * @return mixed
844
     */
845
    public function setRevision($keyword, $id)
846
    {
847
        global $xoopsDB;
848
849
        $keyword = $this->escapeForDB($keyword);
850
        $id      = (int)$id;
851
852
        $page = $this->getPage($keyword, $id);
853
        if (!$page) {
854
            return false;
855
        }
856
857
        $sql    = 'UPDATE ' . $xoopsDB->prefix('gwiki_pages') . " SET active = 0 WHERE keyword='{$keyword}' and active = 1 ";
858
        $result = $xoopsDB->query($sql);
0 ignored issues
show
$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...
859
860
        $sql    = 'UPDATE ' . $xoopsDB->prefix('gwiki_pages') . " SET active = 1 WHERE keyword='{$keyword}' AND gwiki_id='{$id}'";
861
        $result = $xoopsDB->query($sql);
862
863
        $this->updatePageLinks(true);
864
865
        return $result;
866
    }
867
868
    /**
869
     * load a page with optional revision id
870
     *
871
     * @param string   $keyword keyword
872
     * @param int|null $id      optional page id
873
     *
874
     * @return bool
875
     */
876
    public function getPage($keyword, $id = null)
877
    {
878
        global $xoopsDB;
879
880
        $this->resetPage();
881
        $this->keyword = $keyword;
882
        $prefix        = $this->getPrefix($keyword);
883
        if ($prefix && $prefix['defined']) {
884
            $this->currentprefix     = $prefix['prefix'];
885
            $this->currentprefixid   = $prefix['prefix_id'];
886
            $this->currenttemplateid = $prefix['prefix_template_id'];
887
        }
888
889
        $keyword = $this->escapeForDB($keyword);
890
891
        $this->page_id = $this->getPageId($keyword);
892
893
        if (empty($id)) {
894
            $sql = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_pages') . ' natural left join ' . $xoopsDB->prefix('gwiki_pageids') . " WHERE keyword='{$keyword}' and active = 1 ";
895
        } else {
896
            $id  = (int)$id;
897
            $sql = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_pages') . ' natural left join ' . $xoopsDB->prefix('gwiki_pageids') . " WHERE keyword='{$keyword}' and gwiki_id = {$id} ";
898
        }
899
        $result = $xoopsDB->query($sql);
900
        $page   = false;
901
        $rows   = $xoopsDB->getRowsNum($result);
902
        if ($rows > 0) {
903
            $page = $xoopsDB->fetchArray($result);
904
905
            $this->gwiki_id        = $page['gwiki_id'];
906
            $this->keyword         = $page['keyword'];
907
            $this->display_keyword = $page['display_keyword'];
908
            $this->title           = $page['title'];
909
            $this->body            = $page['body'];
910
            $this->parent_page     = $page['parent_page'];
911
912
            $this->page_set_home  = $page['page_set_home'];
913
            $this->page_set_order = $page['page_set_order'];
914
915
            $this->meta_description = $page['meta_description'];
916
            $this->meta_keywords    = $page['meta_keywords'];
917
            $this->lastmodified     = $page['lastmodified'];
918
            $this->uid              = $page['uid'];
919
            $this->admin_lock       = $page['admin_lock'];
920
            $this->active           = $page['active'];
921
            $this->search_body      = $page['search_body'];
922
            $this->toc_cache        = $page['toc_cache'];
923
            $this->show_in_index    = $page['show_in_index'];
924
925
            $this->gwiki_version = $page['gwiki_version'];
926
            $this->page_id       = $page['page_id'];
927
            $this->created       = $page['created'];
928
929
            $page['author']       = $this->getUserName($page['uid']);
930
            $page['revisiontime'] = date($this->dateFormat, $page['lastmodified']);
931
            $page['createdtime']  = date($this->dateFormat, $page['created']);
932
            $page['createdmonth'] = date('M', $page['created']);
933
            $page['createdday']   = date('d', $page['created']);
934
            $page['createdyear']  = date('Y', $page['created']);
935
936
            $temp = $this->renderPageSetNav($keyword);
937
            if ($temp) {
938
                $page['pageset'] = $temp;
939
            }
940
        }
941
942
        return $page;
943
    }
944
945
    /**
946
     * Check for a prefix (namespace)
947
     *
948
     * @param mixed $keyword - wiki page name
949
     *
950
     * @return bool
951
     */
952
    public function getPrefix($keyword)
953
    {
954
        /*
955
         gwiki_prefix columns
956
          prefix_id
957
          prefix
958
          prefix_home
959
          prefix_template_id
960
          prefix_is_external
961
          prefix_external_url < sprintf template for page in external namespace
962
        */
963
        global $xoopsDB;
964
965
        $prefix  = false;
966
        $keyword = $this->escapeForDB($keyword);
967
968
        $pos = strpos($keyword, ':');
969
        // split namespace and page reference on first colon
970
        if ($pos !== false && $pos > 0) {
971
            $pre    = substr($keyword, 0, $pos);
972
            $page   = substr($keyword, $pos + 1);
973
            $q_pre  = $this->escapeForDB($pre);
974
            $sql    = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_prefix') . " WHERE prefix='{$q_pre}' ";
975
            $result = $xoopsDB->query($sql);
976
            $rows   = $xoopsDB->getRowsNum($result);
977
            if ($rows > 0) {
978
                $prefix = $xoopsDB->fetchArray($result);
979
                if ($page === '') {
980
                    $page = $prefix['prefix_home'];
981
                } // supply home page if empty
982
                $prefix['defined'] = true;
983
                // external namespace
984
                if ($prefix['prefix_is_external']) {
985
                    $prefix['actual_page'] = sprintf($prefix['prefix_external_url'], $page);
986
                } else { // local namespace
987
                    $prefix['actual_page'] = $prefix['prefix'] . ':' . $page;
988
                }
989
            } else { // we have an undefined prefix
990
                $prefix['defined'] = false;
991
            }
992
        }
993
994
        return $prefix;
995
    }
996
997
    /**
998
     * get prefix string for an id
999
     *
1000
     * @param int $pid prefix id
1001
     *
1002
     * @return string namespace prefix, or empty string
1003
     */
1004
    public function getPrefixFromId($pid)
1005
    {
1006
        global $xoopsDB;
1007
1008
        $sql    = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_prefix') . ' WHERE prefix_id =' . $pid;
1009
        $result = $xoopsDB->query($sql);
1010
        while ($myrow = $xoopsDB->fetchArray($result)) {
1011
            return $myrow;
1012
        }
1013
1014
        return '';
1015
    }
1016
1017
    /**
1018
     * get template for the current page
1019
     *
1020
     * @return string template name
1021
     */
1022
    public function getTemplateName()
1023
    {
1024
        $template = 'gwiki_view.tpl';
1025
        if ($this->currenttemplateid) {
1026
            $template = $this->wikiDir . '_prefix_' . $this->currentprefixid . '.tpl';
1027
        }
1028
1029
        return $template;
1030
    }
1031
1032
    /**
1033
     * get attachment info associated with a page
1034
     *
1035
     * @param string $page keyword
1036
     *
1037
     * @return array
1038
     */
1039
    public function getAttachments($page)
1040
    {
1041
        global $xoopsDB;
1042
1043
        $this->attachments = array();
1044
        $q_keyword         = $this->escapeForDB($page);
1045
        $sql               = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_page_files') . " WHERE keyword='{$q_keyword}' ";
1046
        $result            = $xoopsDB->query($sql);
1047
        $rows              = $xoopsDB->getRowsNum($result);
0 ignored issues
show
$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...
1048
        while ($row = $xoopsDB->fetchArray($result)) {
1049
            $row['iconlink']     = XOOPS_URL . '/modules/' . $this->wikiDir . '/assets/icons/48px/' . $row['file_icon'] . '.png';
1050
            $row['userlink']     = $this->getUserName($row['file_uid']);
1051
            $row['size']         = number_format($row['file_size']);
1052
            $row['date']         = date($this->dateFormat, $row['file_upload_date']);
1053
            $this->attachments[] = $row;
1054
        }
1055
1056
        return $this->attachments;
1057
    }
1058
1059
    /**
1060
     * Make a link from a wiki keyword
1061
     *
1062
     * @param mixed $keyword - wiki page name
1063
     * @param mixed $altkey  - alternate text for link. If empty, display_keyword will be used.
1064
     *
1065
     * @return string
1066
     */
1067
    public function wikiLink($keyword, $altkey = null)
1068
    {
1069
        global $xoopsDB;
1070
1071
        // HACK - get rid of spaces in page
1072
        // WikiCreole site is filled with page references such as [[Creole 1.0 Poll]] which resolve as
1073
        // hrefs like http://wikicreole.org/wiki/Creole1.0Poll
1074
        //
1075
        // will assume this is considered normal wikiish behavior, and try to emulate.
1076
        // Also seems to capitalize each portion, ie 'Ab and Cd' yields 'AbAndCd' - emulate this, too.
1077
        $org_keyword = $keyword;
1078
        if (strpos(trim($keyword), ' ')) {
1079
            $keys = explode(' ', $keyword);
1080
            foreach ($keys as $i => $k) {
1081
                $keys[$i] = ucfirst($k);
1082
            }
1083
            $keyword = implode('', $keys);
1084
        }
1085
        // $keyword=str_replace (' ', '', $keyword);
1086
1087
        // check for namespace prefix
1088
        $prefix = $this->getPrefix($keyword);
1089
        if ($prefix && $prefix['defined']) {
1090
            $link = $prefix['actual_page'];
1091
            // external namespace
1092
            if ($prefix['prefix_is_external']) {
1093
                $linktext = $org_keyword;
1094
                if ($altkey) {
1095
                    $linktext = $altkey;
1096
                }
1097
                $linktext = stripslashes($linktext);
1098
                $ret      = '<a href="' . $link . '" target="_blank" title="' . _MD_GWIKI_PAGE_EXT_LINK_TT . '">' . $linktext . '<span class="wikiextlink"> </span></a>';
1099
1100
                return $ret;
1101
            } else { // interal namespace
1102
                $keyword = $link; // we may have modified the keyword
1103
            }
1104
        }
1105
1106
        $sql    = 'SELECT keyword, display_keyword, title FROM ' . $xoopsDB->prefix('gwiki_pages') . " WHERE keyword='{$keyword}' and active = 1 ";
1107
        $result = $xoopsDB->query($sql);
1108
        $rows   = $xoopsDB->getRowsNum($result);
1109
        if ($rows) { // existing page
1110
            list($keyword, $display_keyword, $title) = $xoopsDB->fetchRow($result);
1111
            $display_keyword = htmlentities($display_keyword, ENT_QUOTES);
1112
            if (empty($display_keyword)) {
1113
                $display_keyword = $org_keyword;
1114
            }
1115
            $keyword = strtolower($keyword);
1116
            $newpage = '';
1117
        } else { // new page link
1118
            $display_keyword = $org_keyword;
1119
            $newpage         = '<span class="wikinewpage"> </span>';
1120
            $title           = sprintf(_MD_GWIKI_PAGE_CREATE_TT, $keyword);
1121
        }
1122
        if (!empty($altkey)) {
1123
            $display_keyword = $altkey;
1124
        }
1125
        $title           = htmlspecialchars($title);
1126
        $display_keyword = stripslashes($display_keyword);
1127
1128
        // track where this page links
1129
        if (isset($this->wikiPageLinks[$keyword])) {
1130
            ++$this->wikiPageLinks[$keyword];
1131
        } else {
1132
            $this->wikiPageLinks[$keyword] = 1;
1133
        }
1134
1135
        $url = sprintf($this->wikiLinkURL, $keyword);
1136
1137
        return sprintf('<a href="%s" title="%s">%s%s</a>', $url, $title, $display_keyword, $newpage);
1138
    }
1139
1140
    /**
1141
     * callback
1142
     *
1143
     * @param string[] $matches preg_replace_callback matches
1144
     *
1145
     * @return string
1146
     */
1147
    private function wikiCCLink($matches)
1148
    {
1149
        return $this->wikiLink($matches[1]);
1150
    }
1151
1152
    /**
1153
     * get tabbed page index
1154
     *
1155
     * @return array
1156
     */
1157
    private function getIndexTabs()
1158
    {
1159
        global $xoopsDB;
1160
1161
        $tabs = array();
1162
1163
        $sql = 'SELECT SUBSTRING(display_keyword,1,1) as letter, count(*) as count ';
1164
        $sql .= ' FROM ' . $xoopsDB->prefix('gwiki_pages');
1165
        $sql .= ' WHERE active=1 AND show_in_index=1 ';
1166
        $sql .= ' GROUP BY letter ';
1167
1168
        $result = $xoopsDB->query($sql);
1169
1170
        $currentset = false;
1171
        $rows       = $xoopsDB->getRowsNum($result);
1172
        if ($rows) {
1173
            while ($row = $xoopsDB->fetchArray($result)) {
1174
                $row['letter'] = strtolower($row['letter']);
1175
                if ($this->pageIndexPrefix === $row['letter']) {
1176
                    $row['current'] = true;
1177
                    $currentset     = true;
1178
                } else {
1179
                    $row['current'] = false;
1180
                }
1181
                $tabs[] = $row;
1182
            }
1183
        }
1184
        $xoopsDB->freeRecordSet($result);
1185
1186
        if (!$currentset) {
1187
            $this->pageIndexPrefix = $tabs[0]['letter'];
1188
            $tabs[0]['current']    = true;
1189
        }
1190
1191
        return $tabs;
1192
    }
1193
1194
    /**
1195
     * get a page index
1196
     *
1197
     * @param string|null $prefix if not null, limit index to a prefix
1198
     *
1199
     * @return string rendered index
1200
     *
1201
     */
1202
    private function pageIndex($prefix = null)
1203
    {
1204
        global $xoopsDB;
1205
        $simplelayout = false;
1206
        $tablayout    = false;
1207
1208
        $body = '';
1209
1210
        $pageselect = 'active=1 AND show_in_index=1 ';
1211
1212
        if (!empty($prefix)) {
1213
            $pageselect .= ' AND keyword LIKE "' . $prefix . '%" ';
1214
        } else {
1215
            $sql = 'SELECT count(*) as count  FROM ' . $xoopsDB->prefix('gwiki_pages');
1216
            $sql .= ' WHERE ' . $pageselect;
1217
            $result = $xoopsDB->query($sql);
1218
            $row    = $xoopsDB->fetchArray($result);
1219
            $cnt    = $row['count'];
1220
            $xoopsDB->freeRecordSet($result);
1221
            if ($cnt > 500) {
1222
                $tablayout    = true;
1223
                $simplelayout = true; // tablayout is already grouped by first character
1224
                $tabs         = $this->getIndexTabs();
1225
                $pageselect .= ' AND display_keyword LIKE "' . $this->pageIndexPrefix . '%" ';
1226
            }
1227
        }
1228
1229
        $sql = 'SELECT keyword, display_keyword, title';
1230
        $sql .= ' FROM ' . $xoopsDB->prefix('gwiki_pages');
1231
        $sql .= ' WHERE ' . $pageselect;
1232
        $sql .= ' ORDER BY display_keyword ';
1233
        //      $sql.=' ORDER BY active, show_in_index, display_keyword ';
1234
1235
        $result = $xoopsDB->query($sql);
1236
        $rowcnt = $xoopsDB->getRowsNum($result);
1237
1238
        if ($rowcnt < 50) {
1239
            $simplelayout = true;
1240
        } // skip the fancy by letter breakout if this is a small index
1241
1242
        if ($tablayout) {
1243
            $body .= '<div class="wikiindex"><div class="wikiindextabs"><ul>';
1244
1245
            foreach ($tabs as $tab) {
0 ignored issues
show
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...
1246
                $class = '';
1247
                if ($tab['current']) {
1248
                    $class = ' id="wikiindextabactive"';
1249
                }
1250
                $url    = sprintf($this->wikiLinkURL, strtolower($this->keyword . '(\\' . $tab['letter'] . ')'));
1251
                $letter = strtoupper($tab['letter']);
1252
                $body .= "\n<li{$class}><a href=\"{$url}\">{$letter}</a></li>";
1253
            }
1254
            $body .= '</ul></div><div class="wikiindexbody">';
1255
        }
1256
1257
        $lastletter = '';
1258
        if ($simplelayout) {
1259
            $body .= '<ul>';
1260
        }
1261
        while ($content = $xoopsDB->fetchArray($result)) {
1262
            $display_keyword = $content['display_keyword'];
1263
            if (empty($display_keyword)) {
1264
                $display_keyword = $content['keyword'];
1265
            }
1266
            if (!$simplelayout) {
1267
                if (function_exists('mb_substr')) {
1268
                    $testletter = mb_strtoupper(mb_substr($display_keyword, 0, 1, 'UTF-8'), 'UTF-8');
1269
                } else {
1270
                    $testletter = strtoupper(substr($display_keyword, 0, 1));
1271
                }
1272
                if ($lastletter === '') {
1273
                    $lastletter = $testletter;
1274
                    $body .= "<h3>{$lastletter}</h3><ul>";
1275
                }
1276
                if ($lastletter !== $testletter) {
1277
                    $lastletter = $testletter;
1278
                    $body .= "</ul><h3>{$lastletter}</h3><ul>";
1279
                }
1280
            }
1281
            $title           = htmlspecialchars($content['title']);
1282
            $display_keyword = htmlspecialchars($display_keyword);
1283
            $url             = sprintf($this->wikiLinkURL, strtolower($content['keyword']));
1284
            $link            = sprintf('<a href="%s" title="%s">%s%s</a>', $url, $title, $display_keyword, '');
1285
            $body .= '<li>' . $link . ' : ' . $title . '</li>';
1286
        }
1287
        $xoopsDB->freeRecordSet($result);
1288
        if ($tablayout) {
1289
            $body .= '</ul></div></div>';
1290
        } elseif ($body != '') {
1291
            $body .= '</ul>';
1292
        }
1293
1294
        return $body . "\n\n";
1295
    }
1296
1297
    /**
1298
     * get a recently modfied page index
1299
     *
1300
     * @param string|null $prefix if not null, limit index to a prefix
1301
     *
1302
     * @return string rendered index
1303
     *
1304
     */
1305
    private function recentIndex($prefix = null)
1306
    {
1307
        global $xoopsDB;
1308
1309
        // only show active pages
1310
        $pageselect = 'active=1  AND show_in_index=1 ';
1311
        if (!empty($prefix)) {
1312
            $pageselect .= ' AND keyword LIKE "' . $prefix . '%" ';
1313
        }
1314
1315
        $body = '';
1316
1317
        $sql = 'SELECT keyword, display_keyword, title, lastmodified';
1318
        $sql .= ', FROM_UNIXTIME(lastmodified) as fmtlastmodified, uid';
1319
        $sql .= ' FROM ' . $xoopsDB->prefix('gwiki_pages');
1320
        $sql .= ' WHERE ' . $pageselect;
1321
        $sql .= ' ORDER BY lastmodified DESC LIMIT ' . $this->numberOfRecentItems;
1322
1323
        $result = $xoopsDB->query($sql);
1324
1325
        $lastdate = '';
1326
        while ($content = $xoopsDB->fetchArray($result)) {
1327
            $testdate = substr($content['fmtlastmodified'], 0, 10);
1328
            if ($lastdate === '') {
1329
                $lastdate = $testdate;
1330
                $body .= "<h3>{$lastdate}</h3><ul>";
1331
            }
1332
            if ($lastdate !== $testdate) {
1333
                $lastdate = $testdate;
1334
                $body .= "</ul><h3>{$lastdate}</h3><ul>";
1335
            }
1336
1337
            $title           = htmlspecialchars($content['title']);
1338
            $display_keyword = htmlspecialchars($content['display_keyword']);
1339
            $url             = sprintf($this->wikiLinkURL, strtolower($content['keyword']));
1340
            $link            = sprintf('<a href="%s" title="%s">%s%s</a>', $url, $title, $display_keyword, '');
1341
            $body .= '<li>' . $link . ' : ' . $title . '</li>';
1342
        }
1343
        $xoopsDB->freeRecordSet($result);
1344
        if ($body !== '') {
1345
            $body .= '</ul>';
1346
        }
1347
1348
        return $body . "\n\n";
1349
    }
1350
1351
    /**
1352
     * callback render match specified index
1353
     *
1354
     * @param string[] $matches preg_replace_callback matches
1355
     *
1356
     * @return bool|string
1357
     */
1358
    private function renderIndex($matches)
1359
    {
1360
        $type  = $matches[1];
1361
        $parms = '';
1362
        if (isset($matches[2])) {
1363
            $parms = trim($matches[2]);
1364
        }
1365
        if (strcasecmp($type, 'RecentChanges') === 0) {
1366
            return $this->recentIndex($parms);
1367
        }
1368
        if (strcasecmp($type, 'PageIndex') === 0) {
1369
            return $this->pageIndex($parms);
1370
        }
1371
1372
        return false;
1373
    }
1374
1375
    /**
1376
     * highlight search terms
1377
     * adapted from: http://stack:overflow.com/questions/2591046/highlight-text-except-html-tags
1378
     *
1379
     * @param string[] $capture matches
1380
     *
1381
     * @return string
1382
     */
1383
    private function mon_rplc_callback($capture)
1384
    {
1385
        $haystack = $capture[1];
1386
        $p1       = stripos($haystack, $this->highlightArg['needle']);
1387
        $l1       = strlen($this->highlightArg['needle']);
1388
        $ret      = '';
1389
        while ($p1 !== false) {
1390
            $ret .= substr($haystack, 0, $p1) . $this->highlightArg['pre'] . substr($haystack, $p1, $l1) . $this->highlightArg['post'];
1391
            $haystack = substr($haystack, $p1 + $l1);
1392
            $p1       = stripos($haystack, $this->highlightArg['needle']);
1393
        }
1394
        $ret .= $haystack . $capture[2];
1395
1396
        return $ret;
1397
    }
1398
1399
    /**
1400
     * split string aware of html tags
1401
     *
1402
     * @param string $needle string to find
1403
     * @param string $pre    string to include before each match
1404
     * @param string $post   string to include after each match
1405
     * @param string $txt    text to search
1406
     *
1407
     * @return string
1408
     */
1409
    private function split_on_tag($needle, $pre, $post, $txt)
1410
    {
1411
        $this->highlightArg = compact('needle', 'pre', 'post');
1412
1413
        return preg_replace_callback('#((?:(?!<[/a-z]).)*)([^>]*>|$)#si', array($this, 'mon_rplc_callback'), $txt);
1414
    }
1415
1416
    /**
1417
     * highlight words in page
1418
     *
1419
     * @param string $words space separated words to match
1420
     *
1421
     * @return string rendered page with words highlighted
1422
     */
1423
    public function highlightWords($words)
1424
    {
1425
        $words = str_replace('  ', ' ', $words);
1426
        $words = explode(' ', $words);
1427
        $body  = $this->renderedPage;
1428
        foreach ($words as $word) {
1429
            $body = $this->split_on_tag($word, '<span class="wiki_search_term">', '</span>', $body);
1430
        }
1431
1432
        return $body;
1433
    }
1434
1435
    /**
1436
     * Hold content not to be processed for wiki markup, generate a unique tag to locate later
1437
     *
1438
     * @param string $type   type of nowiki invocation (block, wcinline or inline)
1439
     * @param string $source content to hold
1440
     *
1441
     * @return string generated tag for held content
1442
     */
1443
    private function noWikiHold($type, $source)
1444
    {
1445
        ++$this->noWikiIndex;
1446
        switch ($type) {
1447
            case 'block':
1448
                $this->noWikiQueue[$this->noWikiIndex] = "<pre>\n{$source}\n</pre>";
1449
                break;
1450
            case 'wcinline':
1451
                $this->noWikiQueue[$this->noWikiIndex] = '<span class="wikinoinline">' . $source . '</span>';
1452
                break;
1453
            case 'inline':
1454
            default:
1455
                $this->noWikiQueue[$this->noWikiIndex] = $source;
1456
                break;
1457
        }
1458
1459
        $ret = "{PdNlNw:{$this->noWikiIndex}}";
1460
1461
        return $ret;
1462
    }
1463
1464
    /**
1465
     * no wiki block callback
1466
     *
1467
     * @param string[] $matches preg_replace_callback matches
1468
     *
1469
     * @return string
1470
     */
1471
    private function noWikiHoldBlock($matches)
1472
    {
1473
        return $this->noWikiHold('block', $matches[1]);
1474
    }
1475
1476
    /**
1477
     * no wiki inline callback
1478
     *
1479
     * @param string[] $matches preg_replace_callback matches
1480
     *
1481
     * @return string
1482
     */
1483
    private function noWikiHoldInline($matches)
1484
    {
1485
        return $this->noWikiHold('inline', $matches[1]);
1486
    }
1487
1488
    /**
1489
     * no wiki inline (WikiCreole style) callback
1490
     *
1491
     * @param string[] $matches preg_replace_callback matches
1492
     *
1493
     * @return string
1494
     */
1495
    private function noWikiHoldWCInline($matches)
1496
    {
1497
        return $this->noWikiHold('wcinline', $matches[1]);
1498
    }
1499
1500
    /**
1501
     * no wiki for code block callback
1502
     *
1503
     * @param string[] $matches preg_replace_callback matches
1504
     *
1505
     * @return string
1506
     */
1507
    private function noWikiHoldCode($matches)
1508
    {
1509
        return $matches[1] . $this->noWikiHold('block', $matches[2]) . $matches[3];
1510
    }
1511
1512
    /**
1513
     * emit save nowiki content callback
1514
     *
1515
     * @param string[] $matches preg_replace_callback matches
1516
     *
1517
     * @return string
1518
     */
1519
    private function noWikiEmit($matches)
1520
    {
1521
        $index = $matches[1];
1522
1523
        return $this->noWikiQueue[$index];
1524
    }
1525
1526
    /**
1527
     * table support callback
1528
     *
1529
     * @param string[] $matches preg_replace_callback matches
1530
     *
1531
     * @return string
1532
     */
1533
    private function renderTables($matches)
1534
    {
1535
        $source = $matches[0];
1536
        $rowcnt = 0;
1537
        $table  = "<table class=\"wikitable\">\n";
1538
        $rows   = explode("\n", $source);
1539
        foreach ($rows as $i => $row) {
1540
            $row = trim($row);
1541
            if (!empty($row)) {
1542
                if ($row[0] === '|') {
1543
                    $row = substr($row, 1);
1544
                }
1545
                if (substr($row, -1) === '|') {
1546
                    $row = substr($row, 0, -1);
1547
                }
1548
                $cols = explode('|', $row);
1549
                $table .= '<tr' . (($rowcnt % 2) ? ' class="even"' : ' class="odd"') . '>';
1550
                ++$rowcnt;
1551
                foreach ($cols as $col) {
1552
                    if (empty($col)) {
1553
                        $table .= '<td>&nbsp;</td>';
1554 View Code Duplication
                    } elseif ($col[0] === '=') {
0 ignored issues
show
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...
1555
                        $table .= '<th>' . substr($col, 1) . '</th>';
1556
                    } elseif ($col[0] === '>') {
1557
                        $table .= '<td class="right">' . substr($col, 1) . '</td>';
1558 View Code Duplication
                    } elseif ($col[0] === '+') {
0 ignored issues
show
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...
1559
                        $table .= '<td class="center">' . substr($col, 1) . '</td>';
1560
                    } elseif (substr($col, 0, 4) === '&lt;') {
1561
                        $table .= '<td class="left">' . substr($col, 4) . '</td>';
1562
                    } elseif (preg_match('/^\s*[0-9.$+\-]+\s*$/', $col)) {
1563
                        $class = 'number';
1564
                        if ((float)preg_replace("/[^-0-9\.]/", '', $col) < 0) {
1565
                            $class = 'number negative';
1566
                        }
1567
                        $table .= '<td class="' . $class . '">' . trim($col) . '</td>';
1568
                    } else {
1569
                        $table .= '<td>' . $col . '</td>';
1570
                    }
1571
                }
1572
                $table .= "</tr>\n";
1573
            }
1574
        }
1575
        $table .= "</table>\n";
1576
1577
        return $table;
1578
    }
1579
1580
    /**
1581
     * link support callback
1582
     *
1583
     * @param string[] $matches preg_replace_callback matches
1584
     *
1585
     * @return string
1586
     */
1587
    private function renderLink($matches)
1588
    {
1589
        $source = trim($matches[1]);
1590
        $pos    = strpos($source, '|');
1591
1592
        if ($pos === false) { // no delimter - whole thing is the link
1593
            $link     = $source;
1594
            $linktext = '';
1595
            // handle the pathological case of a possesive of a person page.
1596
            // Creole test includes "[[Ward Cunningham's]]" which leads to a
1597
            // wiki page WardCunningham. Included in spirit of compatibility.
1598
            if (substr($link, -2) === "'s") {
1599
                $templink = substr($link, 0, -3); // quote is slashed
1600
                // only if a wiki page
1601
                if (preg_match('/^([A-Za-z\x80-\xff0-9.:\- ]){2,}$/', $templink)) {
1602
                    $linktext = $link;
1603
                    $link     = $templink;
1604
                }
1605
            }
1606
        } else {
1607
            $link     = trim(substr($source, 0, $pos));
1608
            $linktext = trim(substr($source, $pos + 1));
1609
        }
1610
1611
        if (preg_match('/^([A-Za-z\x80-\xff0-9.:\- ]){2,}$/', $link)) {
1612
            //$link=str_replace (' ', '', $link);
1613
            if (empty($linktext)) {
1614
                $ret = $this->wikiLink($link);
1615
            } else {
1616
                $ret = $this->wikiLink($link, stripslashes($linktext));
1617
            }
1618
        } else {
1619
            $ext = true;
1620
            if (strncasecmp($link, XOOPS_URL, strlen(XOOPS_URL)) === 0) {
1621
                $ext = false;
1622
            } // matches our site
1623 View Code Duplication
            if (strcasecmp('siteurl:', substr($link, 0, 8)) === 0) { // explicit reference to our site
0 ignored issues
show
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...
1624
                $link = XOOPS_URL . substr($link, 8);
1625
                $ext  = false;
1626
            }
1627
            if (strpos($link, ':') === false) {
1628
                $ext = false;
1629
            } // no protocol, assume relative url
1630
            if ($linktext === '') {
1631
                $linktext = $link;
1632
            }
1633
            $linktext = stripslashes($linktext);
1634
            // $linktext=$this->noWikiHold('inline',stripslashes($linktext));
1635
            $ret = "<a href=\"{$link}\" title=\"{$linktext}\">{$linktext}</a>";
1636
            if ($ext) {
1637
                $ret = '<a href="' . $link . '" target="_blank" title="' . _MD_GWIKI_PAGE_EXT_LINK_TT . '">' . $linktext . '<span class="wikiextlink"> </span></a>';
1638
            }
1639
        }
1640
1641
        return $ret;
1642
    }
1643
1644
    /**
1645
     * header support callback
1646
     *
1647
     * @param string[] $matches preg_replace_callback matches
1648
     *
1649
     * @return string
1650
     */
1651
    private function renderHeader($matches)
1652
    {
1653
        $source                          = $matches[3];
1654
        $level                           = $matches[2];
1655
        $level                           = strlen($level) + 1;
1656
        $this->tocQueue[$this->tocIndex] = array('level' => $level, 'name' => $source);
1657
        $toc                             = "\n<h" . $level . ' id="' . $this->tocIdPrefix . $this->tocIndex . '" >' . $source . '</h' . $level . ">\n";
1658
        ++$this->tocIndex;
1659
1660
        return $toc;
1661
    }
1662
1663
    /**
1664
     * indent support callback
1665
     *
1666
     * @param string[] $matches preg_replace_callback matches
1667
     *
1668
     * @return string
1669
     */
1670
    private function renderIndent($matches)
1671
    {
1672
        $source = $matches[2];
1673
        $level  = $matches[1];
1674
        $level  = strlen($level);
1675
        $ret    = "\n<div class=\"wikiindent{$level}\">\n{$source}\n</div>";
1676
1677
        return $ret;
1678
    }
1679
1680
    /**
1681
     * table of contents support callback
1682
     *
1683
     * @param string[] $matches preg_replace_callback matches
1684
     *
1685
     * @return string
1686
     */
1687
    private function renderToc($matches)
0 ignored issues
show
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...
1688
    {
1689
        $tocq = $this->tocQueue;
1690
        $toc  = '';
1691
        foreach ($tocq as $i => $v) {
1692
            $toc .= '<li class="wikitoclevel' . $v['level'] . '"><a href="' . sprintf($this->tocAnchorFmt, $this->tocIdPrefix . $i) . '">' . strip_tags($v['name']) . '</a></li>';
1693
        }
1694
        if (!empty($toc)) {
1695
            $toc = '<div class="wikitoc"><div class="wikitocheader">' . _MD_GWIKI_TOC . '</div><ul class="wikitoclist">' . $toc . '</ul></div>';
1696
        }
1697
1698
        return $toc;
1699
    }
1700
1701
    /**
1702
     * fetch table of contents for a page
1703
     *
1704
     * @param string $page keyword
1705
     *
1706
     * @return array|bool
1707
     */
1708
    public function fetchPageSetToc(&$page)
1709
    {
1710
        global $xoopsDB;
1711
        $toc = false;
1712
1713
        $q_page = $this->escapeForDB($page);
1714
1715
        $sql = 'SELECT gwiki_id, keyword, display_keyword, page_set_home, page_set_order, toc_cache ';
1716
        $sql .= ' FROM ' . $xoopsDB->prefix('gwiki_pages');
1717
        $sql .= " WHERE active=1 and keyword='{$q_page}' ";
1718
1719
        $result = $xoopsDB->query($sql);
1720
1721
        $rows = $xoopsDB->getRowsNum($result);
1722
        if ($rows) {
1723
            $row = $xoopsDB->fetchArray($result);
1724
            if (!empty($row['page_set_home'])) {
1725
                $page   = $row['page_set_home']; // this is passed back up to caller!
1726
                $q_page = $this->escapeForDB($row['page_set_home']);
1727
                $xoopsDB->freeRecordSet($result);
1728
                $sql = 'SELECT gwiki_id, keyword, display_keyword, page_set_home, page_set_order, toc_cache ';
1729
                $sql .= ' FROM ' . $xoopsDB->prefix('gwiki_pages');
1730
                $sql .= " WHERE active=1 and page_set_home='{$q_page}' ";
1731
                $sql .= ' ORDER BY page_set_order, keyword ';
1732
1733
                $result = $xoopsDB->query($sql);
1734
                while ($row = $xoopsDB->fetchArray($result)) {
1735
                    $row['display_keyword'] = strip_tags($row['display_keyword']);
1736
                    if (!empty($row['toc_cache'])) {
1737
                        $tmp = unserialize($row['toc_cache']);
1738
                        foreach ($tmp as $i => $v) {
1739
                            $tmp[$i]['name'] = strip_tags($tmp[$i]['name']);
1740
                        }
1741
                        $row['toc'] = $tmp;
1742
1743
                        $toc[] = $row;
1744
                    }
1745
                }
1746
            }
1747
        }
1748
        $xoopsDB->freeRecordSet($result);
1749
1750
        return $toc;
1751
    }
1752
1753
    /**
1754
     * page set toc support callback
1755
     *
1756
     * @param string[] $matches preg_replace_callback matches
1757
     *
1758
     * @return string
1759
     */
1760
    public function renderPageSetTocWrapper($matches)
0 ignored issues
show
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...
1761
    {
1762
        return $this->renderPageSetToc($this->keyword, 6);
1763
    }
1764
1765
    /**
1766
     * render a table of contents
1767
     *
1768
     * @param string  $page     keyword
1769
     * @param integer $level    level limit
1770
     * @param string  $tocclass base class for toc. Current level will be appended
1771
     *
1772
     * @return bool|string
1773
     */
1774
    public function renderPageSetToc(&$page, $level, $tocclass = 'wikitocpage')
1775
    {
1776
        $toc = $this->fetchPageSetToc($page);
1777
        if (!$toc) {
1778
            return false;
1779
        }
1780
        $tocout = '';
1781
        foreach ($toc as $ti => $tv) {
0 ignored issues
show
The expression $toc of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1782
            //$link=sprintf($this->getWikiLinkURL(),$tv['keyword']);
1783
            foreach ($tv['toc'] as $i => $v) {
1784
                if ((int)$v['level'] <= $level) {
1785
                    $tocout .= '<li class="wikitoclevel' . $v['level'] . '"><a href="' . sprintf($this->getWikiLinkURL(), $tv['keyword'] . sprintf($this->tocAnchorFmt, $this->tocIdPrefix . $i)) . '">'
1786
                               . $v['name'] . '</a></li>';
1787
                }
1788
            }
1789
        }
1790
        if (!empty($tocout)) {
1791
            $tocout = '<div class="' . $tocclass . '"><ul class="wikitoclist">' . $tocout . '</ul></div>';
1792
        }
1793
1794
        return $tocout;
1795
    }
1796
1797
    /**
1798
     * render navigation for a page set
1799
     * @param $page
1800
     *
1801
     * @return mixed
1802
     */
1803
    public function renderPageSetNav($page)
1804
    {
1805
        $sethome = $page;
1806
        $toc     = $this->fetchPageSetToc($sethome); // this will set home
1807
        if (!$toc) {
1808
            return false;
1809
        }
1810
        $home    = -1;
1811
        $current = -1;
1812
        $prev    = -1;
1813
        $next    = -1;
1814
        foreach ($toc as $i => $v) {
0 ignored issues
show
The expression $toc of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1815
            if (strcasecmp($toc[$i]['keyword'], $page) === 0) {
1816
                $current = $i;
1817
            }
1818
            if (strcasecmp($toc[$i]['keyword'], $sethome) === 0) {
1819
                $home = $i;
1820
            }
1821
        }
1822
1823
        if ($current > -1) {
1824
            $prev = $current - 1;
1825
            $next = $current + 1;
1826
        }
1827
1828
        $first = 0;
1829
        $last  = count($toc) - 1;
1830
1831
        // should these wrap instead?
1832
        if ($next > $last) {
1833
            $next = $last;
1834
        }
1835
        if ($prev < 0) {
1836
            $prev = 0;
1837
        }
1838
        if ($home < 0) {
1839
            $home = 0;
1840
        }
1841
1842
        $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...
1843
            'link' => sprintf($this->getWikiLinkURL(), $toc[$first]['keyword']),
1844
            'text' => htmlentities($toc[$first]['display_keyword'], ENT_QUOTES),
1845
            'desc' => _MD_GWIKI_PAGENAV_FIRST
1846
        );
1847
1848
        $pageset['prev'] = array(
1849
            'link' => sprintf($this->getWikiLinkURL(), $toc[$prev]['keyword']),
1850
            'text' => htmlentities($toc[$prev]['display_keyword'], ENT_QUOTES),
1851
            'desc' => _MD_GWIKI_PAGENAV_PREV
1852
        );
1853
1854
        $pageset['home'] = array(
1855
            'link' => sprintf($this->getWikiLinkURL(), $toc[$home]['keyword']),
1856
            'text' => htmlentities($toc[$home]['display_keyword'], ENT_QUOTES),
1857
            'desc' => _MD_GWIKI_PAGENAV_TOP
1858
        );
1859
1860
        $pageset['next'] = array(
1861
            'link' => sprintf($this->getWikiLinkURL(), $toc[$next]['keyword']),
1862
            'text' => htmlentities($toc[$next]['display_keyword'], ENT_QUOTES),
1863
            'desc' => _MD_GWIKI_PAGENAV_NEXT
1864
        );
1865
1866
        $pageset['last'] = array(
1867
            'link' => sprintf($this->getWikiLinkURL(), $toc[$last]['keyword']),
1868
            'text' => htmlentities($toc[$last]['display_keyword'], ENT_QUOTES),
1869
            'desc' => _MD_GWIKI_PAGENAV_LAST
1870
        );
1871
1872 View Code Duplication
        if (strcasecmp($toc[$first]['keyword'], $page) === 0) {
0 ignored issues
show
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...
1873
            $pageset['first']['link'] = 'javascript:void(0)';
1874
        }
1875 View Code Duplication
        if (strcasecmp($toc[$prev]['keyword'], $page) === 0) {
0 ignored issues
show
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...
1876
            $pageset['prev']['link'] = 'javascript:void(0)';
1877
        }
1878 View Code Duplication
        if (strcasecmp($toc[$home]['keyword'], $page) === 0) {
0 ignored issues
show
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...
1879
            $pageset['home']['link'] = 'javascript:void(0)';
1880
        }
1881 View Code Duplication
        if (strcasecmp($toc[$next]['keyword'], $page) === 0) {
0 ignored issues
show
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...
1882
            $pageset['next']['link'] = 'javascript:void(0)';
1883
        }
1884 View Code Duplication
        if (strcasecmp($toc[$last]['keyword'], $page) === 0) {
0 ignored issues
show
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...
1885
            $pageset['last']['link'] = 'javascript:void(0)';
1886
        }
1887
1888
        return $pageset;
1889
    }
1890
1891
    /**
1892
     * get image libraries for a page
1893
     *
1894
     * @param string $keyword keyword of page
1895
     *
1896
     * @return string[]
1897
     */
1898
    public function getImageLib($keyword)
1899
    {
1900
        $lib = $this->imageLib;
1901
        array_unshift($lib, $keyword);
1902
1903
        return array_unique($lib);
1904
    }
1905
1906
    /**
1907
     * get an image relative to specified page
1908
     *
1909
     * return array includes:
1910
     *   image_id
1911
     *   keyword
1912
     *   image_name
1913
     *   image_alt_text
1914
     *   image_file
1915
     *
1916
     * @param string $keyword keyword of page
1917
     * @param string $name    image name
1918
     *
1919
     * @return array|bool image data or false if invalid or not found
1920
     */
1921
    public function getPageImage($keyword, $name)
1922
    {
1923
        global $xoopsDB;
1924
1925
        if (strncasecmp($name, 'http://', 7) === 0 || strncasecmp($name, 'https://', 8) === 0) {
1926
            return false;
1927
        }
1928
        $lib = $this->imageLib;
1929
        array_unshift($lib, $keyword);
1930
        foreach ($lib as $page) {
1931
            $sql = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_page_images') . ' WHERE keyword=\'' . $this->escapeForDB($page) . '\' ';
1932
            $sql .= ' AND image_name=\'' . $this->escapeForDB($name) . '\' ';
1933
            $result = $xoopsDB->query($sql);
1934
            if ($image = $xoopsDB->fetchArray($result)) {
1935
                return $image;
1936
            }
1937
        }
1938
1939
        return false;
1940
    }
1941
1942
    /**
1943
     * render image support callback
1944
     *
1945
     * @param string[] $matches preg_replace_callback matches
1946
     *
1947
     * @return string
1948
     */
1949
    private function renderImage($matches)
1950
    {
1951
        $source = trim($matches[1]);
1952
        $pos    = strpos($source, '|');
1953
        //if($pos===false) $pos=strpos($source,' ');
1954
        if ($pos === false) { // no delimter - whole thing is the image url
1955
            $link  = $source;
1956
            $parms = array();
1957
        } else {
1958
            $link  = trim(substr($source, 0, $pos));
1959
            $parms = explode('|', trim(substr($source, $pos + 1)));
1960
            foreach ($parms as $i => $parm) {
1961
                $parms[$i] = trim($parm);
1962
            }
1963
        }
1964 View Code Duplication
        if (strcasecmp('siteurl:', substr($link, 0, 8)) === 0) { // explicit reference to our site
0 ignored issues
show
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...
1965
            $link = XOOPS_URL . substr($link, 8);
1966
        }
1967
        $showthumb     = false;
1968
        $showthumblink = false;
1969
        if (strcasecmp('thumb:', substr($link, 0, 6)) === 0) { // explicit request for thumbnail, links to full image
1970
            $revertlink    = $link;
1971
            $link          = substr($link, 6);
1972
            $showthumblink = true;
1973
        }
1974
        $alttext = empty($parms[0]) ? '' : $parms[0];
1975
        $align   = empty($parms[1]) ? '' : $parms[1];
0 ignored issues
show
$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...
1976
        $maxpx   = empty($parms[2]) ? '' : (int)$parms[2];
1977
1978
        // align must be left, right, center or empty
1979
        $align = '';
1980
        if (strcasecmp($align, 'left') === 0) {
1981
            $align = 'left';
1982
        } elseif (strcasecmp($align, 'right') === 0) {
1983
            $align = 'right';
1984
        } elseif (strcasecmp($align, 'center') === 0) {
1985
            $align = 'center';
1986
        }
1987
1988
        $alignparm = '';
1989
        if ($align === 'left' || $align === 'right' || $align === 'center') {
1990
            $alignparm = ', ' . $align;
1991
        }
1992
1993
        // look up link in page_images table, if found use that, otherwise just pass on link as is
1994
        $image = $this->getPageImage($this->keyword, $link);
1995
        if ($image) {
1996
            // image array includes:
1997
            //   image_id
1998
            //   keyword
1999
            //   image_name
2000
            //   image_alt_text
2001
            //   image_file
2002
            //   use_to_represent
2003
            $link = XOOPS_URL . '/uploads/' . $this->wikiDir . '/' . $image['image_file'];
2004
            if (empty($alttext)) {
2005
                $alttext = $image['image_alt_text'];
2006
            }
2007
        } else {
2008
            // thumbs don't apply, so put everything back the way it was
2009
            if ($showthumblink) {
2010
                $link          = $revertlink;
0 ignored issues
show
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...
2011
                $showthumblink = false;
2012
            }
2013
        }
2014
2015
        $alt = '';
2016
        //      $alttext=htmlspecialchars($alttext);
2017
        if (!empty($alttext)) {
2018
            $alt = " alt=\"{$alttext}\"  title=\"{$alttext}\" ";
2019
        }
2020
2021
        $maxpxstyle = '';
2022
        if (!empty($maxpx)) {
2023
            $maxpxstyle = " style=\"max-width:{$maxpx}px; max-height:{$maxpx}px; width:auto; height:auto;\" ";
2024
            $showthumb  = true; // trigger automatic thumbnail use
2025
        }
2026
2027
        if ($showthumb) {
2028
            $thumbsize = $this->defaultThumbSize;
2029
            if (!empty($maxpx)) {
2030
                $thumbsize = $maxpx;
2031
            }
2032
            $link = XOOPS_URL . '/modules/' . $this->wikiDir . '/getthumb.php?page=' . $image['keyword'] . '&name=' . urlencode($image['image_name']) . '&size=' . $thumbsize;
2033
        }
2034
2035
        if ($showthumblink) {
2036
            $ret       = '';
2037
            $thumbsize = $this->defaultThumbSize;
2038
            if (!empty($maxpx)) {
2039
                $thumbsize = $maxpx;
2040
            }
2041
            $thumb = XOOPS_URL . '/modules/' . $this->wikiDir . '/getthumb.php?page=' . $image['keyword'] . '&name=' . urlencode($image['image_name']) . '&size=' . $thumbsize;
2042
            $img   = XOOPS_URL . '/uploads/' . $this->wikiDir . '/' . $image['image_file'];
2043
            $ret .= '<a href="' . $img . '" ' . $alt . '><img src="' . $thumb . '"' . $alt . $maxpxstyle . '/></a>';
2044
        } else {
2045
            $ret = "<img class=\"wikiimage{$alignparm}\" src=\"{$link}\" {$alt}{$maxpxstyle} />";
2046
        }
2047
2048
        if ($align === 'center') {
2049
            $ret = '<div style="margin: 0 auto; text-align: center;">' . $ret . '</div>';
2050
        }
2051
2052
        return $ret;
2053
    }
2054
2055
    /**
2056
     * gallery support callback
2057
     *
2058
     * @param string[] $matches preg_replace_callback matches
2059
     *
2060
     * @return string
2061
     */
2062
    private function renderGallery($matches)
2063
    {
2064
        global $xoopsDB;
2065
2066
        $source = '';
2067
        if (isset($matches[1])) {
2068
            $source = $matches[1];
2069
        }
2070
        $maxpx = (int)trim($source);
2071
        if ($maxpx < 10) {
2072
            $maxpx = $this->defaultThumbSize;
2073
        }
2074
        $page = $this->keyword;
2075
2076
        $sql    = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_page_images') . ' WHERE keyword = \'' . $page . '\' ' . ' ORDER BY image_name ';
2077
        $result = $xoopsDB->query($sql);
2078
2079
        $dir  = $this->wikiDir;
2080
        $body = '<div class="wikigallery"><ul class="wikigalleryimg">';
2081
2082
        for ($i = 0, $iMax = $xoopsDB->getRowsNum($result); $i < $iMax; ++$i) {
2083
            $image = $xoopsDB->fetchArray($result);
2084
            $img   = XOOPS_URL . '/uploads/' . $dir . '/' . $image['image_file'];
2085
            $thumb = XOOPS_URL . '/modules/' . $dir . '/getthumb.php?page=' . $image['keyword'] . '&name=' . urlencode($image['image_name']) . '&size=' . $maxpx;
2086
            $alt   = htmlentities($image['image_alt_text'], ENT_QUOTES);
2087
            $name  = htmlentities($image['image_name'], ENT_QUOTES);
2088
            if (empty($alt)) {
2089
                $alt = $name;
2090
            }
2091
            $body .= '<li><a href="' . $img . '" title="' . $name . '"><img src="' . $thumb . '" alt="' . $alt . '" title="' . $alt . '" /></a></li>' . "\n";
2092
        }
2093
2094
        $body .= '</ul><br style="clear:both;" /></div>';
2095
2096
        return $body;
2097
    }
2098
2099
    /**
2100
     * list support callback
2101
     *
2102
     * @param string[] $matches preg_replace_callback matches
2103
     *
2104
     * @return string
2105
     */
2106
    private function renderLists($matches)
2107
    {
2108
        $lines = explode("\n", $matches[0]);
2109
        $last  = '';
2110
        foreach ($lines as $i => $line) {
2111
            $line = ltrim($line);
2112
            if (!empty($line)) {
2113
                $list    = '';
2114
                $p       = strpos($line, ' ');
2115
                $current = substr($line, 0, $p);
2116
                $x       = 0;
2117
                while (!empty($last[$x]) && !empty($current[$x]) && $last[$x] === $current[$x]) {
2118
                    ++$x;
2119
                }
2120
                // $x is where the last and current list prefixes differ
2121
                // close anything from $x to end in last
2122
                $close = strrev(substr($last, $x));
2123
                $y     = 0;
2124 View Code Duplication
                while (!empty($close[$y])) {
0 ignored issues
show
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...
2125
                    if ($close[$y] === '*') {
2126
                        $list .= '</li></ul>';
2127
                    } //.($x>0?'</li>':'');
2128
                    if ($close[$y] === '#') {
2129
                        $list .= '</li></ol>';
2130
                    } //.($x>0?'</li>':'');
2131
                    ++$y;
2132
                }
2133
                // open anything from $x to end in
2134
                $open = substr($current, $x);
2135
                $y    = 0;
2136 View Code Duplication
                while (!empty($open[$y])) {
0 ignored issues
show
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...
2137
                    if ($open[$y] === '*') {
2138
                        $list .= '<ul class="wikiulist">';
2139
                    }
2140
                    if ($open[$y] === '#') {
2141
                        $list .= '<ol class="wikiolist">';
2142
                    }
2143
                    ++$y;
2144
                }
2145
                $endli     = ($last === $current) ? '</li>' : '';
2146
                $last      = $current;
2147
                $lines[$i] = $list . $endli . "\n<li> " . substr($line, $p + 1);
2148
            }
2149
        }
2150
2151
        // put list back together
2152
        $list = "\n";
2153
        foreach ($lines as $line) {
2154
            if (!empty($line)) {
2155
                $list .= $line;
2156
            }
2157
        }
2158
        // close anything left open
2159
        $close = strrev($last);
2160
        $y     = 0;
2161 View Code Duplication
        while (!empty($close[$y])) {
0 ignored issues
show
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...
2162
            if ($close[$y] === '*') {
2163
                $list .= "</li></ul>\n";
2164
            }
2165
            if ($close[$y] === '#') {
2166
                $list .= "</li></ol>\n";
2167
            }
2168
            ++$y;
2169
        }
2170
2171
        return $list;
2172
    }
2173
2174
    /**
2175
     * reference support callback
2176
     *
2177
     * @param string[] $matches preg_replace_callback matches
2178
     *
2179
     * @return string
2180
     */
2181
    private function renderRef($matches)
2182
    {
2183
        $refinfo      = $matches[1];
2184
        $source       = $matches[2];
2185
        $first_ref    = false;
2186
        $refs         = explode('|', trim($refinfo) . '|||');
2187
        $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...
2188
        $rq['first']  = $refs[1];
2189
        $rq['repeat'] = $refs[2];
2190
        $rq['source'] = $source;
2191
        $refid        = (-1);
2192
        if (!empty($rq['id'])) {
2193
            foreach ($this->refQueue as $i => $v) {
2194
                if ($v['id'] === $rq['id']) {
2195
                    $refid = $i;
2196
                }
2197
            }
2198
        }
2199
        if ($refid === (-1)) {
2200
            $refid                           = $this->refIndex;
2201
            $first_ref                       = true;
2202
            $this->refQueue[$this->refIndex] = $rq;
2203
            ++$this->refIndex;
2204
        }
2205
        $paren_ref = false;
2206
        if (!empty($this->refQueue[$refid]['first'])) {
2207
            $paren_ref = true;
2208
        }
2209
        if ($paren_ref) {
2210
            $ref_text = $this->refQueue[$refid]['first'];
2211
            if (!$first_ref) {
2212
                if (!empty($this->refQueue[$refid]['repeat'])) {
2213
                    $ref_text = $this->refQueue[$refid]['repeat'];
2214
                }
2215
            }
2216
            $r = '<span class="wikiparenref"><a href="#ref' . $refid . '">(' . $ref_text . ')</a></span>';
2217
        } else {
2218
            $r = '<span class="wikinumref"><a href="#ref' . $refid . '">' . ($refid + 1) . '</a></span>';
2219
        }
2220
2221
        return $r;
2222
    }
2223
2224
    /**
2225
     * reference list support callback
2226
     *
2227
     * @param string[] $matches preg_replace_callback matches
2228
     *
2229
     * @return string
2230
     */
2231
    private function renderRefList($matches)
0 ignored issues
show
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...
2232
    {
2233
        $this->refShown = true;
2234
        $r              = '<div class="wikicitelist">';
2235
2236
        foreach ($this->refQueue as $i => $v) {
2237
            $refid = $i;
2238
            if (empty($v['first'])) {
2239
                $r .= '<div class="wikicitenumitem" id="ref' . $refid . '"><span class="wikicitenum">' . ($refid + 1) . '. </span>' . $v['source'] . "</div>\n";
2240
            } else {
2241
                $r .= '<div class="wikiciteparenitem" id="ref' . $refid . '">' . $v['source'] . "</div>\n";
2242
            }
2243
        }
2244
2245
        $r .= '</div>';
2246
2247
        return $r;
2248
    }
2249
2250
    /**
2251
     * box support callback
2252
     *
2253
     * @param string[] $matches preg_replace_callback matches
2254
     *
2255
     * @return string
2256
     */
2257
    private function renderBox($matches)
2258
    {
2259
        $type  = $matches[1];
2260
        $title = $matches[2];
2261
        $body  = $matches[3];
2262
        // make sure we have a valid type
2263
        $type = strtolower($type);
2264
        if (!($type === 'code' || $type === 'info' || $type === 'note' || $type === 'tip' || $type === 'warn'
2265
              || $type === 'folded')
2266
        ) {
2267
            $type = 'info';
2268
        }
2269
2270
        // $title may include options ( title | align ) :
2271
        //   align: adds additonal class 'left' or 'right' to box so css can alter float, size, etc.
2272
        $title    = trim($title);
2273
        $eclass   = '';
2274
        $ejs      = '';
2275
        $etooltip = '';
2276
        $pos      = strpos($title, '|');
2277
        if ($pos !== false) { // if no delimiter - whole thing is the title
2278
            $parms = explode('|', $title);
2279
            $title = $parms[0];
2280
            if (!empty($parms[1]) && ($parms[1] === 'left' || $parms[1] === 'right')) {
2281
                $eclass = ' ' . $parms[1];
2282
            }
2283
        }
2284
        if ($type === 'folded') {
2285
            $foldclass   = 'wikifolded' . $eclass;
2286
            $unfoldclass = 'wikiunfolded' . $eclass;
2287
            $ejs         = ' onclick="var c=this.className; if(c==\'' . $foldclass . '\') this.className=\'' . $unfoldclass . '\'; else this.className=\'' . $foldclass . '\';"';
2288
            $etooltip    = '<span>' . _MD_GWIKI_FOLDED_TT . '</span>';
2289
        }
2290
2291
        $ret = '<div class="wiki' . $type . $eclass . '"' . $ejs . '><div class="wiki' . $type . 'icon"></div><div class="wiki' . $type . 'title">' . $title . $etooltip . '</div><div class="wiki'
2292
               . $type . 'inner">' . $body . '<br clear="all" /></div></div>' . "\n\n";
2293
2294
        return $ret;
2295
    }
2296
2297
    /**
2298
     * Convert entities
2299
     *
2300
     * @param string $body wiki text to process
2301
     *
2302
     * @return string
2303
     */
2304
    private function convertEntities($body)
2305
    {
2306
        // convert some entites
2307
        $sym   = array();
2308
        $ent   = array();
2309
        $sym[] = '{cent}';
2310
        $ent[] = '&cent;';
2311
        $sym[] = '{pound}';
2312
        $ent[] = '&pound;';
2313
        $sym[] = '{yen}';
2314
        $ent[] = '&yen;';
2315
        $sym[] = '{euro}';
2316
        $ent[] = '&euro;';
2317
        $sym[] = '{c}';
2318
        $ent[] = '&copy;';
2319
        $sym[] = '(c)';
2320
        $ent[] = '&copy;';
2321
        $sym[] = '{r}';
2322
        $ent[] = '&reg;';
2323
        $sym[] = '(r)';
2324
        $ent[] = '&reg;';
2325
        $sym[] = '{tm}';
2326
        $ent[] = '&trade;';
2327
        $sym[] = '(tm)';
2328
        $ent[] = '&trade;';
2329
        $sym[] = '{sm}';
2330
        // very poor font support for unicode code point for service mark, fake with markup
2331
        $ent[] = '<span style="font-size: 50%; vertical-align: super;">SM</span>';
2332
        $sym[] = '{nbsp}';
2333
        $ent[] = '&nbsp;';
2334
2335
        $body = str_ireplace($sym, $ent, $body);
2336
2337
        return $body;
2338
    }
2339
2340
    /**
2341
     * Render a teaser section. If page includes a {more} tag, teaser will be text that preceeds it.
2342
     * Otherwise try to break semantically at about 400 characters.
2343
     *
2344
     * @param string|null $body  text to process, defaults to current page body
2345
     * @param string|null $title title to use
2346
     *
2347
     * @return string
2348
     */
2349
    public function renderTeaser($body = null, $title = null)
2350
    {
2351
        // chop body at more tag if it is set
2352
        $splitsize = 400; // arbitrary size to use when no {more} tag
2353
        if (empty($body)) {
2354
            $body = $this->body;
2355
        }
2356
        $pos = stripos($body, '{more}');
2357
        if ($pos === false && strlen($body) > $splitsize) {
2358
            $search  = "#\r\n?#";
2359
            $replace = "\n";
2360
            $body    = preg_replace($search, $replace, $body);
2361
            $pos     = stripos($body, "\n\n", $splitsize); // hopefully the end of a paragraph
2362
        }
2363
        if ($pos !== false) {
2364
            $body = substr($body, 0, $pos);
2365
            $url  = sprintf($this->wikiLinkURL, $this->keyword);
2366
        }
2367
2368
        $body = str_ireplace('{toc}', '', $body);
2369
        $body = $this->renderPage($body, $title);
2370
        if ($pos !== false) {
2371
            $body .= '<a href="' . $url . '#more"><span class="wikimore">' . _MD_GWIKI_MORE . '</span></a>';
0 ignored issues
show
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...
2372
        }
2373
2374
        return $body;
2375
    }
2376
2377
    /**
2378
     * block quote support callback
2379
     *
2380
     * @param string[] $matches preg_replace_callback matches
2381
     *
2382
     * @return string
2383
     */
2384
    private function renderBlockquote($matches)
2385
    {
2386
        $src = str_replace("\n", ' ', preg_replace('#^> #m', '', $matches[0]));
2387
2388
        return '<blockquote class=\"wikiquote\">' . $src . "</blockquote>\n";
2389
    }
2390
2391
    /**
2392
     * preformatted support callback
2393
     *
2394
     * @param string[] $matches preg_replace_callback matches
2395
     *
2396
     * @return string
2397
     */
2398
    private function renderPreformat($matches)
2399
    {
2400
        $src = preg_replace('#^. #m', '', $matches[0]);
2401
2402
        return '<pre>' . $src . "</pre>\n";
2403
    }
2404
2405
    /**
2406
     * Render a page
2407
     *
2408
     * @param string|null $body  text to process, defaults to current page body
2409
     * @param string|null $title title to use, defaults to current page title
2410
     *
2411
     * @return string
2412
     */
2413
    public function renderPage($body = null, $title = null)
2414
    {
2415
        if (empty($body)) {
2416
            $body = $this->body;
2417
        }
2418
        $this->renderedPage = '';
2419
        $this->noWikiQueue  = array();
2420
        $this->noWikiIndex  = 0;
2421
        $this->refShown     = false;
2422
2423
        if (empty($title)) {
2424
            $title = $this->title;
2425
        }
2426
        // do first because title should always be #toc0 - set in template
2427
        $this->renderHeader(array('', '', '', $title));
2428
2429
        $body .= "\n";
2430
2431
        // eliminate double line endings
2432
        $search  = "#\r\n?#";
2433
        $replace = "\n";
2434
        $body    = preg_replace($search, $replace, $body);
2435
2436
        // neuter html tags
2437
        $search  = '#<#';
2438
        $replace = '&lt;';
2439
        $body    = preg_replace($search, $replace, $body);
2440
2441
        // neuter single quotes
2442
        $search  = "#'#";
2443
        $replace = "\\'";
2444
        $body    = preg_replace($search, $replace, $body);
2445
2446
        // nowiki - tilde escape
2447
        $search  = "#~([^ \t\r\n\v\f])#U";
2448
        $replace = array($this, 'noWikiHoldInline');
2449
        $body    = preg_replace_callback($search, $replace, $body);
2450
2451
        // nowiki content gwiki style
2452
        $search  = '#{nowiki}(.*){endnowiki}#Umsi';
2453
        $replace = array($this, 'noWikiHoldInline');
2454
        $body    = preg_replace_callback($search, $replace, $body);
2455
2456
        // nowiki content block creole style (a nowiki that forces a style, how odd.)
2457
        $search  = "#^{{{\n(.*)^}}}\n#Umsi";
2458
        $replace = array($this, 'noWikiHoldBlock');
2459
        $body    = preg_replace_callback($search, $replace, $body);
2460
2461
        // nowiki content inline creole style
2462
        $search  = '#{{{(.*)}}}#U';
2463
        $replace = array($this, 'noWikiHoldWCInline');
2464
        $body    = preg_replace_callback($search, $replace, $body);
2465
2466
        // automatically nowiki content of code box - {code title}xxx{endcode}
2467
        $search  = "#({code [^\"<\n]+?})(.*?)({endcode})#si";
2468
        $replace = array($this, 'noWikiHoldCode');
2469
        $body    = preg_replace_callback($search, $replace, $body);
2470
2471
        // center ++ xxx
2472
        $search  = "#^(\+{2})(.*)(?=\n\n|\Z)#Usm";
2473
        $replace = "<div style=\"text-align: center;\" class=\"wikicenter\">\n\\2\n</div>\n";
2474
        $body    = preg_replace($search, $replace, $body);
2475
2476
        // : indent up to 5 levels
2477
        $search  = "#^(\:{1,5})\s(.*)(?=\n\n|\Z)#Usm";
2478
        $replace = array($this, 'renderIndent');
2479
        $body    = preg_replace_callback($search, $replace, $body);
2480
2481
        // lists
2482
        $search  = "#^( *[*\#]{1,} (.*)\n)+#m";
2483
        $replace = array($this, 'renderLists');
2484
        $body    = preg_replace_callback($search, $replace, $body);
2485
2486
        // bold **xxx**
2487
        $search  = "#\*{2}(.*?)(\*{2}|(?=\n\n))#s";
2488
        $replace = "<strong class=\"wikistrong\">\\1</strong>";
2489
        $body    = preg_replace($search, $replace, $body);
2490
2491
        // italic //xxx//
2492
        $search  = "#(?<![:])/{2}(.*?[^:])(/{2}|(?=\n\n))#s";
2493
        $replace = "<em class=\"wikiem\">\\1</em>";
2494
        $body    = preg_replace($search, $replace, $body);
2495
2496
        // horizontal rule ---- (not an empty strikethru; creole says 4 or more so this needs to go first)
2497
        $search  = "#^-{4,}$#m";
2498
        $replace = "\n<hr  class=\"wikihr\"/>\n";
2499
        $body    = preg_replace($search, $replace, $body);
2500
2501
        // strikethru --xxx-- (this does NOT cross lines, as '--' is a common typographic convention
2502
        $search  = "#-{2}([^\s]{1}.*?)(-{2})#";
2503
        $replace = "<del class=\"wikidel\">\\1</del>";
2504
        $body    = preg_replace($search, $replace, $body);
2505
2506
        // underline __xxx__
2507
        $search  = "#(?<=\s)_{2}(.*?)(_{2}|(?=\n\n))#s";
2508
        $replace = "<span class=\"wikiu\">\\1</span>";
2509
        $body    = preg_replace($search, $replace, $body);
2510
2511
        // superscript ^^xxx^^
2512
        $search  = "#\^{2}(.*?)(\^{2}|(?=\n\n))#s";
2513
        $replace = "<sup class=\"wikisup\">\\1</sup>";
2514
        $body    = preg_replace($search, $replace, $body);
2515
2516
        // subscript ,,xxx,,
2517
        $search  = "#,{2}(.*?)(,{2}|(?=\n\n))#s";
2518
        $replace = "<sub class=\"wikisub\">\\1</sub>";
2519
        $body    = preg_replace($search, $replace, $body);
2520
2521
        // monospace ##xxx##
2522
        $search  = "#\#{2}(.*?)(\#{2}|(?=\n\n))#s";
2523
        $replace = "<span class=\"wikitt\">\\1</span>";
2524
        $body    = preg_replace($search, $replace, $body);
2525
2526
        // color !!color:xxx!!
2527
        $search  = "#!{2}(\#{0,1}[0-9A-Za-z]*):(.*?)(!{2}|(?=\n\n))#s";
2528
        $replace = "<span style=\"color:\\1;\">\\2</span>";
2529
        $body    = preg_replace($search, $replace, $body);
2530
2531
        // color !!color,background:xxx!!
2532
        $search  = "#!{2}(\#{0,1}[0-9A-Za-z]*),(\#{0,1}[0-9A-Za-z]*):(.*?)(!{2}|(?=\n\n))#s";
2533
        $replace = "<span style=\"color:\\1; background-color:\\2;\">\\3</span>";
2534
        $body    = preg_replace($search, $replace, $body);
2535
2536
        // forced line break creole style \\, just a bare break tag
2537
        $search  = "#(\\\{2})#i";
2538
        $replace = '<br>';
2539
        $body    = preg_replace($search, $replace, $body);
2540
2541
        // forced line break blog [[br]] or gwiki {break} styles, themed - by default clear all
2542
        $search  = "#(\[\[BR\]\]|{break})#i";
2543
        $replace = '<br class="wikibreak" />';
2544
        $body    = preg_replace($search, $replace, $body);
2545
2546
        // image {{image url|alt text|align|max width in pixels}}
2547
        $search  = "#\{{2}(.*)\}{2}#Um";
2548
        $replace = array($this, 'renderImage');
2549
        $body    = preg_replace_callback($search, $replace, $body);
2550
2551
        // info box {info title}xxx{endinfo}
2552
        $search  = "#{(info) ([^\"<\n]+?)?}(.*?){endinfo}#si";
2553
        $replace = array($this, 'renderBox');
2554
        $body    = preg_replace_callback($search, $replace, $body);
2555
2556
        // note box {note title}xxx{endnote}
2557
        $search  = "#{(note) ([^\"<\n]+?)?}(.*?){endnote}#si";
2558
        $replace = array($this, 'renderBox');
2559
        $body    = preg_replace_callback($search, $replace, $body);
2560
2561
        // tip box {tip title}xxx{endtip}
2562
        $search  = "#{(tip) ([^\"<\n]+?)?}(.*?){endtip}#si";
2563
        $replace = array($this, 'renderBox');
2564
        $body    = preg_replace_callback($search, $replace, $body);
2565
2566
        // warning box {warning title}xxx{endwarning}
2567
        $search  = "#{(warn)ing ([^\"<\n]+?)?}(.*?){endwarning}#si";
2568
        $replace = array($this, 'renderBox');
2569
        $body    = preg_replace_callback($search, $replace, $body);
2570
2571
        // code (preformatted) box {code title}xxx{endcode}
2572
        $search  = "#{(code) ([^\"<\n]+?)?}(.*?){endcode}#si";
2573
        $replace = array($this, 'renderBox');
2574
        $body    = preg_replace_callback($search, $replace, $body);
2575
2576
        // folded box {folded title}xxx{endfolded}
2577
        $search  = "#{(folded) ([^\"<\n]+?)?}(.*?){endfolded}#si";
2578
        $replace = array($this, 'renderBox');
2579
        $body    = preg_replace_callback($search, $replace, $body);
2580
2581
        // urls - smells like a link
2582
        $search  = "#(?<=\s)((http|https|ftp|ftps)://.{2,}\..*)(?=[,.?!:;]{0,1}\s)#Ui";
2583
        $replace = array($this, 'renderLink');
2584
        $body    = preg_replace_callback($search, $replace, $body);
2585
2586
        // link [[link|linktext]]
2587
        $search  = "#\[{2}(.*)\]{2}#Um";
2588
        $replace = array($this, 'renderLink');
2589
        $body    = preg_replace_callback($search, $replace, $body);
2590
2591
        // email [email protected]
2592
        $search  = "#(?<=\s)([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4})(?=\s)#i";
2593
        $replace = '<a href="mailto:\\1">\\1</a>';
2594
        $body    = preg_replace($search, $replace, $body);
2595
2596
        // CamelCase wiki link "#^([A-Z][a-z\:]+){2,}\d*$#"
2597
        // Could be between whitespace on either end or between > on start and/or < on end
2598
        if ($this->useCamelCase) {
2599
            $search  = "#(?<=\s|>)" . _WIKI_CAMELCASE_REGEX . "(?=\s|</l|</t)#";
2600
            $replace = array($this, 'wikiCCLink');
2601
            $body    = preg_replace_callback($search, $replace, $body);
2602
        }
2603
2604
        // =====headings up to 5 levels
2605
        $search  = "#(^\s{0,})(={1,5})([^=].*[^=])(={0,5})\s*$#Um";
2606
        $replace = array($this, 'renderHeader');
2607
        $body    = preg_replace_callback($search, $replace, $body);
2608
2609
        // blockquote > xxx
2610
        $search  = "#^(> .*\n)+#m";
2611
        $replace = array($this, 'renderBlockquote');
2612
        $body    = preg_replace_callback($search, $replace, $body);
2613
2614
        // preformated  .xxx
2615
        $search  = "#^(\. .*\n)+#m";
2616
        $replace = array($this, 'renderPreformat');
2617
        $body    = preg_replace_callback($search, $replace, $body);
2618
2619
        // reference {ref id|first-ref}source{endref}
2620
        $search  = "#{ref( [^\"<\n]+?)?}(.*?){endref}#si";
2621
        $replace = array($this, 'renderRef');
2622
        $body    = preg_replace_callback($search, $replace, $body);
2623
2624
        // forced line break blog [[br]] or gwiki {break} styles, themed - by default clear all
2625
        $search  = '#({reflist})#i';
2626
        $replace = array($this, 'renderRefList');
2627
        $body    = preg_replace_callback($search, $replace, $body);
2628
2629
        // index or change list {pageindex prefix}
2630
        $search  = "#{(PageIndex|RecentChanges)([^\"<\n]+?)?}#si";
2631
        $replace = array($this, 'renderIndex');
2632
        $body    = preg_replace_callback($search, $replace, $body);
2633
2634
        // table of contents
2635
        $search  = "#\{toc\}#i";
2636
        $replace = array($this, 'renderToc');
2637
        $body    = preg_replace_callback($search, $replace, $body);
2638
2639
        // page set table of contents
2640
        $search  = "#\{pagesettoc\}#i";
2641
        $replace = array($this, 'renderPageSetTocWrapper');
2642
        $body    = preg_replace_callback($search, $replace, $body);
2643
2644
        // image gallery {gallery size}
2645
        $search  = "#{gallery([^\"<\n]+?)?}#si";
2646
        $replace = array($this, 'renderGallery');
2647
        $body    = preg_replace_callback($search, $replace, $body);
2648
2649
        // more anchor - indicates end of teaser/summary
2650
        $search  = "#\{more\}#i";
2651
        $replace = '<span id="more"></span>';
2652
        $body    = preg_replace($search, $replace, $body);
2653
2654
        // tables
2655
        $search  = "#^( *\|((.*)\|){1,}\s*\n)+#m";
2656
        $replace = array($this, 'renderTables');
2657
        $body    = preg_replace_callback($search, $replace, $body);
2658
2659
        // paragraph on 2 consecutive newlines
2660
        $search  = "#\n{2}#";
2661
        $replace = "\n<p>";
2662
        $body    = preg_replace($search, $replace, $body);
2663
2664
        // restore cached nowiki content, all styles
2665
        // (if you need to use {PdNlNw:#} in your page, put it in a nowiki tag)
2666
        $search  = '#{PdNlNw:([0-9]{1,})}#';
2667
        $replace = array($this, 'noWikiEmit');
2668
        $body    = preg_replace_callback($search, $replace, $body);
2669
2670
        if ($this->refShown === false && $this->refIndex > 0) {
2671
            $body .= $this->renderRefList(null);
0 ignored issues
show
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...
2672
        }
2673
        $body = stripslashes($this->convertEntities($body));
2674
2675
        $this->renderedPage = $body;
2676
2677
        return $this->renderedPage;
2678
    }
2679
}
2680