Completed
Push — develop ( 709e5e...5b92e2 )
by Dmytro
07:32 queued 01:03
created

DocumentParser::updatePubStatus()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 43
Code Lines 26

Duplication

Lines 14
Ratio 32.56 %

Importance

Changes 0
Metric Value
cc 7
eloc 26
nc 5
nop 0
dl 14
loc 43
rs 6.7272
c 0
b 0
f 0
1
<?php
2
/**
3
 *    MODX Document Parser
4
 *    Function: This class contains the main document parsing functions
5
 *
6
 */
7
if (!defined('E_DEPRECATED')) {
8
    define('E_DEPRECATED', 8192);
9
}
10
if (!defined('E_USER_DEPRECATED')) {
11
    define('E_USER_DEPRECATED', 16384);
12
}
13
14
class DocumentParser
0 ignored issues
show
Coding Style introduced by
The property $table_prefix is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
The property $error_reporting is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
The property $decoded_request_uri is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
15
{
16
    /**
17
     * This is New evolution
18
     * @var string
19
     */
20
    public $apiVersion = '1.0.0';
21
22
    /**
23
     * db object
24
     * @var DBAPI
25
     * @see /manager/includes/extenders/ex_dbapi.inc.php
26
     * @example $this->loadExtension('DBAPI')
27
     */
28
    public $db;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $db. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
29
30
    /**
31
     * @var MODxMailer
32
     * @see /manager/includes/extenders/ex_modxmailer.inc.php
33
     * @example $this->loadExtension('MODxMailer');
34
     */
35
    public $mail;
36
37
    /**
38
     * @var PHPCOMPAT
39
     * @see /manager/includes/extenders/ex_phpcompat.inc.php
40
     * @example $this->loadExtension('PHPCOMPAT');
41
     */
42
    public $phpcompat;
43
44
    /**
45
     * @var MODIFIERS
46
     * @see /manager/includes/extenders/ex_modifiers.inc.php
47
     * @example $this->loadExtension('MODIFIERS');
48
     */
49
    public $filter;
50
51
    /**
52
     * @var EXPORT_SITE
53
     * @see /manager/includes/extenders/ex_export_site.inc.php
54
     * @example $this->loadExtension('EXPORT_SITE');
55
     */
56
    public $export;
57
58
    /**
59
     * @var MakeTable
60
     * @see /manager/includes/extenders/ex_maketable.inc.php
61
     * @example $this->loadExtension('makeTable');
62
     */
63
    public $table;
64
65
    /**
66
     * @var ManagerAPI
67
     * @see /manager/includes/extenders/ex_managerapi.inc.php
68
     * @example $this->loadExtension('ManagerAPI');
69
     */
70
    public $manager;
71
72
    /**
73
     * @var PasswordHash
74
     * @see manager/includes/extenders/ex_phpass.inc.php
75
     * @example $this->loadExtension('phpass');
76
     */
77
    public $phpass;
78
79
    /**
80
     * event object
81
     * @var SystemEvent
82
     */
83
84
    public $event;
85
    /**
86
     * event object
87
     * @var SystemEvent
88
     */
89
    public $Event;
90
91
    /**
92
     * @var array
93
     */
94
    public $pluginEvent = array();
95
96
    /**
97
     * @var array
98
     */
99
    public $config = array();
100
    /**
101
     * @var array
102
     */
103
    public $dbConfig = array();
104
    public $configGlobal = null; // contains backup of settings overwritten by user-settings
105
    public $rs;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
106
    public $result;
107
    public $sql;
108
    public $table_prefix;
109
    public $debug = false;
110
    public $documentIdentifier;
111
    public $documentMethod;
112
    public $documentGenerated;
113
    public $documentContent;
114
    public $documentOutput;
115
    public $tstart;
116
    public $mstart;
117
    public $minParserPasses;
118
    public $maxParserPasses;
119
    public $documentObject;
120
    public $templateObject;
121
    public $snippetObjects;
122
    public $stopOnNotice = false;
123
    public $executedQueries;
124
    public $queryTime;
125
    public $currentSnippet;
126
    public $documentName;
127
    public $aliases;
128
    public $visitor;
129
    public $entrypage;
130
    public $documentListing;
131
    /**
132
     * feed the parser the execution start time
133
     * @var bool
134
     */
135
    public $dumpSnippets = false;
136
    public $snippetsCode;
137
    public $snippetsTime = array();
138
    public $chunkCache;
139
    public $snippetCache;
140
    public $contentTypes;
141
    public $dumpSQL = false;
142
    public $queryCode;
143
    public $virtualDir;
144
    public $placeholders;
145
    public $sjscripts = array();
146
    public $jscripts = array();
147
    public $loadedjscripts = array();
148
    public $documentMap;
149
    public $forwards = 3;
150
    public $error_reporting = 1;
151
    public $dumpPlugins = false;
152
    public $pluginsCode;
153
    public $pluginsTime = array();
154
    public $pluginCache = array();
155
    public $aliasListing;
156
    public $lockedElements = null;
157
    public $tmpCache = array();
158
    private $version = array();
159
    public $extensions = array();
160
    public $cacheKey = null;
161
    public $recentUpdate = 0;
162
    public $useConditional = false;
163
    protected $systemCacheKey = null;
164
    public $snipLapCount = 0;
165
    public $messageQuitCount;
166
    public $time;
167
    public $sid;
168
    private $q;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $q. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
169
    public $decoded_request_uri;
170
    /**
171
     * @var OldFunctions
172
     */
173
    public $old;
174
175
    /**
176
     * Document constructor
177
     *
178
     * @return DocumentParser
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
179
     */
180
    public function __construct()
0 ignored issues
show
Coding Style introduced by
__construct uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
181
    {
182
        if ($this->isLoggedIn()) {
183
            ini_set('display_errors', 1);
184
        }
185
        global $database_server;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
186
        if (substr(PHP_OS, 0, 3) === 'WIN' && $database_server === 'localhost') {
187
            $database_server = '127.0.0.1';
188
        }
189
        $this->loadExtension('DBAPI') or die('Could not load DBAPI class.'); // load DBAPI class
0 ignored issues
show
Coding Style Compatibility introduced by
The method __construct() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
190
        $this->dbConfig = &$this->db->config; // alias for backward compatibility
191
        // events
192
        $this->event = new SystemEvent();
193
        $this->Event = &$this->event; //alias for backward compatibility
194
        // set track_errors ini variable
195
        @ ini_set("track_errors", "1"); // enable error tracking in $php_errormsg
196
        $this->time = $_SERVER['REQUEST_TIME']; // for having global timestamp
197
198
        $this->q = self::_getCleanQueryString();
199
    }
200
201
    /**
202
     * @param $method_name
203
     * @param $arguments
204
     * @return mixed
205
     */
206
    function __call($method_name, $arguments)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Coding Style Naming introduced by
The parameter $method_name is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
207
    {
208
        include_once(MODX_MANAGER_PATH . 'includes/extenders/deprecated.functions.inc.php');
209
        if (method_exists($this->old, $method_name)) {
210
            $error_type = 1;
211
        } else {
212
            $error_type = 3;
213
        }
214
215
        if (!isset($this->config['error_reporting']) || 1 < $this->config['error_reporting']) {
216
            if ($error_type == 1) {
217
                $title = 'Call deprecated method';
218
                $msg = $this->htmlspecialchars("\$modx->{$method_name}() is deprecated function");
219
            } else {
220
                $title = 'Call undefined method';
221
                $msg = $this->htmlspecialchars("\$modx->{$method_name}() is undefined function");
222
            }
223
            $info = debug_backtrace();
224
            $m[] = $msg;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$m was never initialized. Although not strictly required by PHP, it is generally a good practice to add $m = 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...
Comprehensibility introduced by
Avoid variables with short names like $m. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
225
            if (!empty($this->currentSnippet)) {
226
                $m[] = 'Snippet - ' . $this->currentSnippet;
227
            } elseif (!empty($this->event->activePlugin)) {
228
                $m[] = 'Plugin - ' . $this->event->activePlugin;
229
            }
230
            $m[] = $this->decoded_request_uri;
231
            $m[] = str_replace('\\', '/', $info[0]['file']) . '(line:' . $info[0]['line'] . ')';
232
            $msg = implode('<br />', $m);
233
            $this->logEvent(0, $error_type, $msg, $title);
234
        }
235
        if (method_exists($this->old, $method_name)) {
236
            return call_user_func_array(array($this->old, $method_name), $arguments);
237
        }
238
    }
239
240
    /**
241
     * @param string $connector
242
     * @return bool
243
     */
244
    public function checkSQLconnect($connector = 'db')
245
    {
246
        $flag = false;
247
        if (is_scalar($connector) && !empty($connector) && isset($this->{$connector}) && $this->{$connector} instanceof DBAPI) {
248
            $flag = (bool)$this->{$connector}->conn;
249
        }
250
        return $flag;
251
    }
252
253
    /**
254
     * Loads an extension from the extenders folder.
255
     * You can load any extension creating a boot file:
256
     * MODX_MANAGER_PATH."includes/extenders/ex_{$extname}.inc.php"
257
     * $extname - extension name in lowercase
258
     *
259
     * @param $extname
260
     * @param bool $reload
261
     * @return bool
262
     */
263
    public function loadExtension($extname, $reload = true)
264
    {
265
        $out = false;
266
        $flag = ($reload || !in_array($extname, $this->extensions));
267
        if ($this->checkSQLconnect('db') && $flag) {
268
            $evtOut = $this->invokeEvent('OnBeforeLoadExtension', array('name' => $extname, 'reload' => $reload));
269
            if (is_array($evtOut) && count($evtOut) > 0) {
270
                $out = array_pop($evtOut);
271
            }
272
        }
273
        if (!$out && $flag) {
274
            $extname = trim(str_replace(array('..', '/', '\\'), '', strtolower($extname)));
275
            $filename = MODX_MANAGER_PATH . "includes/extenders/ex_{$extname}.inc.php";
276
            $out = is_file($filename) ? include $filename : false;
277
        }
278
        if ($out && !in_array($extname, $this->extensions)) {
279
            $this->extensions[] = $extname;
280
        }
281
        return $out;
282
    }
283
284
    /**
285
     * Returns the current micro time
286
     *
287
     * @return float
288
     */
289
    public function getMicroTime()
290
    {
291
        list ($usec, $sec) = explode(' ', microtime());
292
        return ((float)$usec + (float)$sec);
293
    }
294
295
    /**
296
     * Redirect
297
     *
298
     * @param string $url
299
     * @param int $count_attempts
300
     * @param string $type $type
301
     * @param string $responseCode
302
     * @return bool|null
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use false|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
303
     * @global string $base_url
304
     * @global string $site_url
305
     */
306
    public function sendRedirect($url, $count_attempts = 0, $type = '', $responseCode = '')
0 ignored issues
show
Coding Style introduced by
sendRedirect uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style Naming introduced by
The parameter $count_attempts is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
307
    {
308
        $header = '';
309
        if (empty ($url)) {
310
            return false;
311
        }
312
        if ($count_attempts == 1) {
313
            // append the redirect count string to the url
314
            $currentNumberOfRedirects = isset ($_REQUEST['err']) ? $_REQUEST['err'] : 0;
315
            if ($currentNumberOfRedirects > 3) {
316
                $this->messageQuit('Redirection attempt failed - please ensure the document you\'re trying to redirect to exists. <p>Redirection URL: <i>' . $url . '</i></p>');
317
            } else {
318
                $currentNumberOfRedirects += 1;
0 ignored issues
show
Coding Style introduced by
Increment operators should be used where possible; found "$currentNumberOfRedirects += 1;" but expected "$currentNumberOfRedirects++"
Loading history...
319
                if (strpos($url, "?") > 0) {
320
                    $url .= "&err=$currentNumberOfRedirects";
321
                } else {
322
                    $url .= "?err=$currentNumberOfRedirects";
323
                }
324
            }
325
        }
326
        if ($type == 'REDIRECT_REFRESH') {
327
            $header = 'Refresh: 0;URL=' . $url;
328
        } elseif ($type == 'REDIRECT_META') {
329
            $header = '<META HTTP-EQUIV="Refresh" CONTENT="0; URL=' . $url . '" />';
330
            echo $header;
331
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method sendRedirect() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
332
        } elseif ($type == 'REDIRECT_HEADER' || empty ($type)) {
333
            // check if url has /$base_url
334
            global $base_url, $site_url;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
335
            if (substr($url, 0, strlen($base_url)) == $base_url) {
336
                // append $site_url to make it work with Location:
337
                $url = $site_url . substr($url, strlen($base_url));
338
            }
339
            if (strpos($url, "\n") === false) {
340
                $header = 'Location: ' . $url;
341
            } else {
342
                $this->messageQuit('No newline allowed in redirect url.');
343
            }
344
        }
345
        if ($responseCode && (strpos($responseCode, '30') !== false)) {
346
            header($responseCode);
347
        }
348
349
        if(!empty($header)) {
350
            header($header);
351
        }
352
353
        exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method sendRedirect() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
354
    }
355
356
    /**
357
     * Forward to another page
358
     *
359
     * @param int|string $id
360
     * @param string $responseCode
361
     */
362
    public function sendForward($id, $responseCode = '')
363
    {
364
        if ($this->forwards > 0) {
365
            $this->forwards = $this->forwards - 1;
366
            $this->documentIdentifier = $id;
367
            $this->documentMethod = 'id';
368
            if ($responseCode) {
369
                header($responseCode);
370
            }
371
            $this->prepareResponse();
372
            exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method sendForward() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
373
        } else {
374
            $this->messageQuit("Internal Server Error id={$id}");
375
            header('HTTP/1.0 500 Internal Server Error');
376
            die('<h1>ERROR: Too many forward attempts!</h1><p>The request could not be completed due to too many unsuccessful forward attempts.</p>');
0 ignored issues
show
Coding Style Compatibility introduced by
The method sendForward() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
377
        }
378
    }
379
380
    /**
381
     * Redirect to the error page, by calling sendForward(). This is called for example when the page was not found.
382
     * @param bool $noEvent
383
     */
384
    public function sendErrorPage($noEvent = false)
385
    {
386
        $this->systemCacheKey = 'notfound';
387
        if (!$noEvent) {
388
            // invoke OnPageNotFound event
389
            $this->invokeEvent('OnPageNotFound');
390
        }
391
        $url = $this->config['error_page'] ? $this->config['error_page'] : $this->config['site_start'];
392
393
        $this->sendForward($url, 'HTTP/1.0 404 Not Found');
394
        exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method sendErrorPage() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
395
    }
396
397
    /**
398
     * @param bool $noEvent
399
     */
400
    public function sendUnauthorizedPage($noEvent = false)
0 ignored issues
show
Coding Style introduced by
sendUnauthorizedPage uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
401
    {
402
        // invoke OnPageUnauthorized event
403
        $_REQUEST['refurl'] = $this->documentIdentifier;
404
        $this->systemCacheKey = 'unauth';
405
        if (!$noEvent) {
406
            $this->invokeEvent('OnPageUnauthorized');
407
        }
408
        if ($this->config['unauthorized_page']) {
409
            $unauthorizedPage = $this->config['unauthorized_page'];
410
        } elseif ($this->config['error_page']) {
411
            $unauthorizedPage = $this->config['error_page'];
412
        } else {
413
            $unauthorizedPage = $this->config['site_start'];
414
        }
415
        $this->sendForward($unauthorizedPage, 'HTTP/1.1 401 Unauthorized');
416
        exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method sendUnauthorizedPage() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
417
    }
418
419
    /**
420
     * Get MODX settings including, but not limited to, the system_settings table
421
     */
422
    public function getSettings()
423
    {
424
        if (!isset($this->config['site_name'])) {
425
            $this->recoverySiteCache();
426
        }
427
428
        // setup default site id - new installation should generate a unique id for the site.
429
        if (!isset($this->config['site_id'])) {
430
            $this->config['site_id'] = "MzGeQ2faT4Dw06+U49x3";
431
        }
432
433
        // store base_url and base_path inside config array
434
        $this->config['base_url'] = MODX_BASE_URL;
435
        $this->config['base_path'] = MODX_BASE_PATH;
436
        $this->config['site_url'] = MODX_SITE_URL;
437
        $this->config['valid_hostnames'] = MODX_SITE_HOSTNAMES;
438
        $this->config['site_manager_url'] = MODX_MANAGER_URL;
439
        $this->config['site_manager_path'] = MODX_MANAGER_PATH;
440
        $this->error_reporting = $this->config['error_reporting'];
441
        $this->config['filemanager_path'] = str_replace('[(base_path)]', MODX_BASE_PATH, $this->config['filemanager_path']);
442
        $this->config['rb_base_dir'] = str_replace('[(base_path)]', MODX_BASE_PATH, $this->config['rb_base_dir']);
443
444
        if (!isset($this->config['enable_at_syntax'])) {
445
            $this->config['enable_at_syntax'] = 1;
446
        } // @TODO: This line is temporary, should be remove in next version
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
447
448
        // now merge user settings into evo-configuration
449
        $this->getUserSettings();
450
    }
451
452
    private function recoverySiteCache()
453
    {
454
        $site_cache_dir = MODX_BASE_PATH . $this->getCacheFolder();
455
        $site_cache_path = $site_cache_dir . 'siteCache.idx.php';
456
457
        if (is_file($site_cache_path)) {
458
            include($site_cache_path);
459
        }
460
        if (isset($this->config['site_name'])) {
461
            return;
462
        }
463
464
        include_once(MODX_MANAGER_PATH . 'processors/cache_sync.class.processor.php');
465
        $cache = new synccache();
466
        $cache->setCachepath($site_cache_dir);
467
        $cache->setReport(false);
468
        $cache->buildCache($this);
469
470
        clearstatcache();
471
        if (is_file($site_cache_path)) {
472
            include($site_cache_path);
473
        }
474
        if (isset($this->config['site_name'])) {
475
            return;
476
        }
477
478
        $rs = $this->db->select('setting_name, setting_value', '[+prefix+]system_settings');
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
479
        while ($row = $this->db->getRow($rs)) {
480
            $this->config[$row['setting_name']] = $row['setting_value'];
481
        }
482
483
        if (!$this->config['enable_filter']) {
484
            return;
485
        }
486
487
        $where = "plugincode LIKE '%phx.parser.class.inc.php%OnParseDocument();%' AND disabled != 1";
488
        $rs = $this->db->select('id', '[+prefix+]site_plugins', $where);
489
        if ($this->db->getRecordCount($rs)) {
490
            $this->config['enable_filter'] = '0';
491
        }
492
    }
493
494
    /**
495
     * Get user settings and merge into MODX configuration
496
     * @return array
497
     */
498
    public function getUserSettings()
0 ignored issues
show
Coding Style introduced by
getUserSettings uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
499
    {
500
        $tbl_web_user_settings = $this->getFullTableName('web_user_settings');
501
        $tbl_user_settings = $this->getFullTableName('user_settings');
502
503
        // load user setting if user is logged in
504
        $usrSettings = array();
505
        if ($id = $this->getLoginUserID()) {
506
            $usrType = $this->getLoginUserType();
507
            if (isset ($usrType) && $usrType == 'manager') {
508
                $usrType = 'mgr';
509
            }
510
511
            if ($usrType == 'mgr' && $this->isBackend()) {
512
                // invoke the OnBeforeManagerPageInit event, only if in backend
513
                $this->invokeEvent("OnBeforeManagerPageInit");
514
            }
515
516
            if (isset ($_SESSION[$usrType . 'UsrConfigSet'])) {
517
                $usrSettings = &$_SESSION[$usrType . 'UsrConfigSet'];
518
            } else {
519
                if ($usrType == 'web') {
520
                    $from = $tbl_web_user_settings;
521
                    $where = "webuser='{$id}'";
522
                } else {
523
                    $from = $tbl_user_settings;
524
                    $where = "user='{$id}'";
525
                }
526
527
                $which_browser_default = $this->configGlobal['which_browser'] ? $this->configGlobal['which_browser'] : $this->config['which_browser'];
528
529
                $result = $this->db->select('setting_name, setting_value', $from, $where);
530
                while ($row = $this->db->getRow($result)) {
531 View Code Duplication
                    if ($row['setting_name'] == 'which_browser' && $row['setting_value'] == 'default') {
532
                        $row['setting_value'] = $which_browser_default;
533
                    }
534
                    $usrSettings[$row['setting_name']] = $row['setting_value'];
535
                }
536
                if (isset ($usrType)) {
537
                    $_SESSION[$usrType . 'UsrConfigSet'] = $usrSettings;
538
                } // store user settings in session
539
            }
540
        }
541
        if ($this->isFrontend() && $mgrid = $this->getLoginUserID('mgr')) {
542
            $musrSettings = array();
543
            if (isset ($_SESSION['mgrUsrConfigSet'])) {
544
                $musrSettings = &$_SESSION['mgrUsrConfigSet'];
545
            } else {
546
                if ($result = $this->db->select('setting_name, setting_value', $tbl_user_settings, "user='{$mgrid}'")) {
547
                    while ($row = $this->db->getRow($result)) {
548
                        $musrSettings[$row['setting_name']] = $row['setting_value'];
549
                    }
550
                    $_SESSION['mgrUsrConfigSet'] = $musrSettings; // store user settings in session
551
                }
552
            }
553
            if (!empty ($musrSettings)) {
554
                $usrSettings = array_merge($musrSettings, $usrSettings);
555
            }
556
        }
557
        // save global values before overwriting/merging array
558
        foreach ($usrSettings as $param => $value) {
559
            if (isset($this->config[$param])) {
560
                $this->configGlobal[$param] = $this->config[$param];
561
            }
562
        }
563
564
        $this->config = array_merge($this->config, $usrSettings);
565
        $this->config['filemanager_path'] = str_replace('[(base_path)]', MODX_BASE_PATH, $this->config['filemanager_path']);
566
        $this->config['rb_base_dir'] = str_replace('[(base_path)]', MODX_BASE_PATH, $this->config['rb_base_dir']);
567
568
        return $usrSettings;
569
    }
570
571
    /**
572
     * Returns the document identifier of the current request
573
     *
574
     * @param string $method id and alias are allowed
575
     * @return int
576
     */
577
    public function getDocumentIdentifier($method)
0 ignored issues
show
Coding Style introduced by
getDocumentIdentifier uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
getDocumentIdentifier uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
578
    {
579
        // function to test the query and find the retrieval method
580
        if ($method === 'alias') {
581
            return $this->db->escape($_REQUEST['q']);
582
        }
583
584
        $id_ = filter_input(INPUT_GET, 'id');
585
        if ($id_) {
586
            if (preg_match('@^[1-9][0-9]*$@', $id_)) {
587
                return $id_;
588
            } else {
589
                $this->sendErrorPage();
590
            }
591
        } elseif (strpos($_SERVER['REQUEST_URI'], 'index.php/') !== false) {
592
            $this->sendErrorPage();
593
        } else {
594
            return $this->config['site_start'];
595
        }
596
    }
597
598
    /**
599
     * Check for manager or webuser login session since v1.2
600
     *
601
     * @param string $context
602
     * @return bool
603
     */
604
    public function isLoggedIn($context = 'mgr')
0 ignored issues
show
Coding Style introduced by
isLoggedIn uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
605
    {
606
        if (substr($context, 0, 1) == 'm') {
607
            $_ = 'mgrValidated';
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $_. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
608
        } else {
609
            $_ = 'webValidated';
610
        }
611
612
        if (MODX_CLI || (isset($_SESSION[$_]) && !empty($_SESSION[$_]))) {
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return (bool) (MODX_CLI ...!empty($_SESSION[$_]));.
Loading history...
613
            return true;
614
        } else {
615
            return false;
616
        }
617
    }
618
619
    /**
620
     * Check for manager login session
621
     *
622
     * @return boolean
623
     */
624
    public function checkSession()
625
    {
626
        return $this->isLoggedin();
627
    }
628
629
    /**
630
     * Checks, if a the result is a preview
631
     *
632
     * @return boolean
633
     */
634
    public function checkPreview()
0 ignored issues
show
Coding Style introduced by
checkPreview uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
635
    {
636
        if ($this->isLoggedIn() == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
637
            if (isset ($_REQUEST['z']) && $_REQUEST['z'] == 'manprev') {
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return isset($_REQUEST['...UEST['z'] == 'manprev';.
Loading history...
638
                return true;
639
            } else {
640
                return false;
641
            }
642
        } else {
643
            return false;
644
        }
645
    }
646
647
    /**
648
     * check if site is offline
649
     *
650
     * @return boolean
651
     */
652
    public function checkSiteStatus()
653
    {
654
        if ($this->config['site_status']) {
655
            return true;
656
        }  // site online
657
        elseif ($this->isLoggedin()) {
658
            return true;
659
        }  // site offline but launched via the manager
660
        else {
661
            return false;
662
        } // site is offline
663
    }
664
665
    /**
666
     * Create a 'clean' document identifier with path information, friendly URL suffix and prefix.
667
     *
668
     * @param string $qOrig
669
     * @return string
670
     */
671
    public function cleanDocumentIdentifier($qOrig)
672
    {
673
        if (!$qOrig) {
674
            $qOrig = $this->config['site_start'];
675
        }
676
        $q = $qOrig;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $q. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
677
678
        $pre = $this->config['friendly_url_prefix'];
679
        $suf = $this->config['friendly_url_suffix'];
680
        $pre = preg_quote($pre, '/');
681
        $suf = preg_quote($suf, '/');
682 View Code Duplication
        if ($pre && preg_match('@^' . $pre . '(.*)$@', $q, $_)) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $_. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
683
            $q = $_[1];
684
        }
685 View Code Duplication
        if ($suf && preg_match('@(.*)' . $suf . '$@', $q, $_)) {
686
            $q = $_[1];
687
        }
688
689
        /* First remove any / before or after */
690
        $q = trim($q, '/');
691
692
        /* Save path if any */
693
        /* FS#476 and FS#308: only return virtualDir if friendly paths are enabled */
694
        if ($this->config['use_alias_path'] == 1) {
695
            $_ = strrpos($q, '/');
696
            $this->virtualDir = $_ !== false ? substr($q, 0, $_) : '';
697
            if ($_ !== false) {
698
                $q = preg_replace('@.*/@', '', $q);
699
            }
700
        } else {
701
            $this->virtualDir = '';
702
        }
703
704
        if (preg_match('@^[1-9][0-9]*$@', $q) && !isset($this->documentListing[$q])) { /* we got an ID returned, check to make sure it's not an alias */
705
            /* FS#476 and FS#308: check that id is valid in terms of virtualDir structure */
706
            if ($this->config['use_alias_path'] == 1) {
707
                if (($this->virtualDir != '' && !isset($this->documentListing[$this->virtualDir . '/' . $q]) || ($this->virtualDir == '' && !isset($this->documentListing[$q]))) && (($this->virtualDir != '' && isset($this->documentListing[$this->virtualDir]) && in_array($q, $this->getChildIds($this->documentListing[$this->virtualDir], 1))) || ($this->virtualDir == '' && in_array($q, $this->getChildIds(0, 1))))) {
708
                    $this->documentMethod = 'id';
709
                    return $q;
710
                } else { /* not a valid id in terms of virtualDir, treat as alias */
711
                    $this->documentMethod = 'alias';
712
                    return $q;
713
                }
714
            } else {
715
                $this->documentMethod = 'id';
716
                return $q;
717
            }
718
        } else { /* we didn't get an ID back, so instead we assume it's an alias */
719
            if ($this->config['friendly_alias_urls'] != 1) {
720
                $q = $qOrig;
721
            }
722
            $this->documentMethod = 'alias';
723
            return $q;
724
        }
725
    }
726
727
    /**
728
     * @return string
729
     */
730
    public function getCacheFolder()
731
    {
732
        return "assets/cache/";
733
    }
734
735
    /**
736
     * @param $key
737
     * @return string
738
     */
739
    public function getHashFile($key)
740
    {
741
        return $this->getCacheFolder() . "docid_" . $key . ".pageCache.php";
742
    }
743
744
    /**
745
     * @param $id
746
     * @return array|mixed|null|string
747
     */
748
    public function makePageCacheKey($id){
0 ignored issues
show
Coding Style introduced by
makePageCacheKey uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
749
        $hash = $id;
750
        $tmp = null;
751
        $params = array();
752
        if(!empty($this->systemCacheKey)){
753
            $hash = $this->systemCacheKey;
754
        }else {
755
            if (!empty($_GET)) {
756
                // Sort GET parameters so that the order of parameters on the HTTP request don't affect the generated cache ID.
757
                $params = $_GET;
758
                ksort($params);
759
                $hash .= '_'.md5(http_build_query($params));
760
            }
761
        }
762
        $evtOut = $this->invokeEvent("OnMakePageCacheKey", array ("hash" => $hash, "id" => $id, 'params' => $params));
763
        if (is_array($evtOut) && count($evtOut) > 0){
764
            $tmp = array_pop($evtOut);
765
        }
766
        return empty($tmp) ? $hash : $tmp;
767
    }
768
769
    /**
770
     * @param $id
771
     * @param bool $loading
772
     * @return string
773
     */
774
    public function checkCache($id, $loading = false)
775
    {
776
        return $this->getDocumentObjectFromCache($id, $loading);
777
    }
778
779
    /**
780
     * Check the cache for a specific document/resource
781
     *
782
     * @param int $id
783
     * @param bool $loading
784
     * @return string
785
     */
786
    public function getDocumentObjectFromCache($id, $loading = false)
787
    {
788
        $key = ($this->config['cache_type'] == 2) ? $this->makePageCacheKey($id) : $id;
789
        if ($loading) {
790
            $this->cacheKey = $key;
791
        }
792
793
        $cache_path = $this->getHashFile($key);
794
795
        if (!is_file($cache_path)) {
796
            $this->documentGenerated = 1;
797
            return '';
798
        }
799
        $content = file_get_contents($cache_path, false);
800
        if (substr($content, 0, 5) === '<?php') {
801
            $content = substr($content, strpos($content, '?>') + 2);
802
        } // remove php header
803
        $a = explode('<!--__MODxCacheSpliter__-->', $content, 2);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $a. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
804
        if (count($a) == 1) {
805
            $result = $a[0];
806
        } // return only document content
807
        else {
808
            $docObj = unserialize($a[0]); // rebuild document object
809
            // check page security
810
            if ($docObj['privateweb'] && isset ($docObj['__MODxDocGroups__'])) {
811
                $pass = false;
812
                $usrGrps = $this->getUserDocGroups();
813
                $docGrps = explode(',', $docObj['__MODxDocGroups__']);
814
                // check is user has access to doc groups
815
                if (is_array($usrGrps)) {
816
                    foreach ($usrGrps as $k => $v) {
817
                        if (!in_array($v, $docGrps)) {
818
                            continue;
819
                        }
820
                        $pass = true;
821
                        break;
822
                    }
823
                }
824
                // diplay error pages if user has no access to cached doc
825
                if (!$pass) {
826
                    if ($this->config['unauthorized_page']) {
827
                        // check if file is not public
828
                        $rs = $this->db->select('count(id)', '[+prefix+]document_groups', "document='{$id}'", '', '1');
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
829
                        $total = $this->db->getValue($rs);
830
                    } else {
831
                        $total = 0;
832
                    }
833
834
                    if ($total > 0) {
835
                        $this->sendUnauthorizedPage();
836
                    } else {
837
                        $this->sendErrorPage();
838
                    }
839
840
                    exit; // stop here
0 ignored issues
show
Coding Style Compatibility introduced by
The method getDocumentObjectFromCache() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
841
                }
842
            }
843
            // Grab the Scripts
844
            if (isset($docObj['__MODxSJScripts__'])) {
845
                $this->sjscripts = $docObj['__MODxSJScripts__'];
846
            }
847
            if (isset($docObj['__MODxJScripts__'])) {
848
                $this->jscripts = $docObj['__MODxJScripts__'];
849
            }
850
851
            // Remove intermediate variables
852
            unset($docObj['__MODxDocGroups__'], $docObj['__MODxSJScripts__'], $docObj['__MODxJScripts__']);
853
854
            $this->documentObject = $docObj;
855
856
            $result = $a[1]; // return document content
857
        }
858
859
        $this->documentGenerated = 0;
860
        // invoke OnLoadWebPageCache  event
861
        $this->documentContent = $result;
862
        $this->invokeEvent('OnLoadWebPageCache');
863
        return $result;
864
    }
865
866
    /**
867
     * Final processing and output of the document/resource.
868
     *
869
     * - runs uncached snippets
870
     * - add javascript to <head>
871
     * - removes unused placeholders
872
     * - converts URL tags [~...~] to URLs
873
     *
874
     * @param boolean $noEvent Default: false
875
     */
876
    public function outputContent($noEvent = false)
0 ignored issues
show
Coding Style introduced by
outputContent uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
877
    {
878
        $this->documentOutput = $this->documentContent;
879
880
        if ($this->documentGenerated == 1 && $this->documentObject['cacheable'] == 1 && $this->documentObject['type'] == 'document' && $this->documentObject['published'] == 1) {
881
            if (!empty($this->sjscripts)) {
882
                $this->documentObject['__MODxSJScripts__'] = $this->sjscripts;
883
            }
884
            if (!empty($this->jscripts)) {
885
                $this->documentObject['__MODxJScripts__'] = $this->jscripts;
886
            }
887
        }
888
889
        // check for non-cached snippet output
890
        if (strpos($this->documentOutput, '[!') > -1) {
891
            $this->recentUpdate = $_SERVER['REQUEST_TIME'] + $this->config['server_offset_time'];
892
893
            $this->documentOutput = str_replace('[!', '[[', $this->documentOutput);
894
            $this->documentOutput = str_replace('!]', ']]', $this->documentOutput);
895
896
            // Parse document source
897
            $this->documentOutput = $this->parseDocumentSource($this->documentOutput);
898
        }
899
900
        // Moved from prepareResponse() by sirlancelot
901
        // Insert Startup jscripts & CSS scripts into template - template must have a <head> tag
902
        if ($js = $this->getRegisteredClientStartupScripts()) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $js. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
903
            // change to just before closing </head>
904
            // $this->documentContent = preg_replace("/(<head[^>]*>)/i", "\\1\n".$js, $this->documentContent);
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
905
            $this->documentOutput = preg_replace("/(<\/head>)/i", $js . "\n\\1", $this->documentOutput);
906
        }
907
908
        // Insert jscripts & html block into template - template must have a </body> tag
909
        if ($js = $this->getRegisteredClientScripts()) {
910
            $this->documentOutput = preg_replace("/(<\/body>)/i", $js . "\n\\1", $this->documentOutput);
911
        }
912
        // End fix by sirlancelot
913
914
        $this->documentOutput = $this->cleanUpMODXTags($this->documentOutput);
915
916
        $this->documentOutput = $this->rewriteUrls($this->documentOutput);
917
918
        // send out content-type and content-disposition headers
919
        if (IN_PARSER_MODE == "true") {
920
            $type = !empty ($this->contentTypes[$this->documentIdentifier]) ? $this->contentTypes[$this->documentIdentifier] : "text/html";
921
            header('Content-Type: ' . $type . '; charset=' . $this->config['modx_charset']);
922
            //            if (($this->documentIdentifier == $this->config['error_page']) || $redirect_error)
0 ignored issues
show
Unused Code Comprehensibility introduced by
61% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
923
            //                header('HTTP/1.0 404 Not Found');
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
924
            if (!$this->checkPreview() && $this->documentObject['content_dispo'] == 1) {
925
                if ($this->documentObject['alias']) {
926
                    $name = $this->documentObject['alias'];
927
                } else {
928
                    // strip title of special characters
929
                    $name = $this->documentObject['pagetitle'];
930
                    $name = strip_tags($name);
931
                    $name = $this->cleanUpMODXTags($name);
932
                    $name = strtolower($name);
933
                    $name = preg_replace('/&.+?;/', '', $name); // kill entities
934
                    $name = preg_replace('/[^\.%a-z0-9 _-]/', '', $name);
935
                    $name = preg_replace('/\s+/', '-', $name);
936
                    $name = preg_replace('|-+|', '-', $name);
937
                    $name = trim($name, '-');
938
                }
939
                $header = 'Content-Disposition: attachment; filename=' . $name;
940
                header($header);
941
            }
942
        }
943
        $this->setConditional();
944
945
        $stats = $this->getTimerStats($this->tstart);
946
947
        $out =& $this->documentOutput;
948
        $out = str_replace("[^q^]", $stats['queries'], $out);
949
        $out = str_replace("[^qt^]", $stats['queryTime'], $out);
950
        $out = str_replace("[^p^]", $stats['phpTime'], $out);
951
        $out = str_replace("[^t^]", $stats['totalTime'], $out);
952
        $out = str_replace("[^s^]", $stats['source'], $out);
953
        $out = str_replace("[^m^]", $stats['phpMemory'], $out);
954
        //$this->documentOutput= $out;
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
955
956
        // invoke OnWebPagePrerender event
957
        if (!$noEvent) {
958
            $evtOut = $this->invokeEvent('OnWebPagePrerender', array('documentOutput' => $this->documentOutput));
959
            if (is_array($evtOut) && count($evtOut) > 0) {
960
                $this->documentOutput = $evtOut['0'];
961
            }
962
        }
963
964
        $this->documentOutput = $this->removeSanitizeSeed($this->documentOutput);
965
966
        if (strpos($this->documentOutput, '\{') !== false) {
967
            $this->documentOutput = $this->RecoveryEscapedTags($this->documentOutput);
968
        } elseif (strpos($this->documentOutput, '\[') !== false) {
969
            $this->documentOutput = $this->RecoveryEscapedTags($this->documentOutput);
970
        }
971
972
        echo $this->documentOutput;
973
974
        if ($this->dumpSQL) {
975
            echo $this->queryCode;
976
        }
977
        if ($this->dumpSnippets) {
978
            $sc = "";
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $sc. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
979
            $tt = 0;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $tt. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
980
            foreach ($this->snippetsTime as $s => $v) {
981
                $t = $v['time'];
982
                $sname = $v['sname'];
983
                $sc .= sprintf("%s. %s (%s)<br>", $s, $sname, sprintf("%2.2f ms", $t)); // currentSnippet
984
                $tt += $t;
985
            }
986
            echo "<fieldset><legend><b>Snippets</b> (" . count($this->snippetsTime) . " / " . sprintf("%2.2f ms", $tt) . ")</legend>{$sc}</fieldset><br />";
987
            echo $this->snippetsCode;
988
        }
989
        if ($this->dumpPlugins) {
990
            $ps = "";
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ps. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
991
            $tt = 0;
992
            foreach ($this->pluginsTime as $s => $t) {
993
                $ps .= "$s (" . sprintf("%2.2f ms", $t * 1000) . ")<br>";
994
                $tt += $t;
995
            }
996
            echo "<fieldset><legend><b>Plugins</b> (" . count($this->pluginsTime) . " / " . sprintf("%2.2f ms", $tt * 1000) . ")</legend>{$ps}</fieldset><br />";
997
            echo $this->pluginsCode;
998
        }
999
1000
        ob_end_flush();
1001
    }
1002
1003
    /**
1004
     * @param $contents
1005
     * @return mixed
1006
     */
1007
    public function RecoveryEscapedTags($contents)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1008
    {
1009
        list($sTags, $rTags) = $this->getTagsForEscape();
1010
        return str_replace($rTags, $sTags, $contents);
1011
    }
1012
1013
    /**
1014
     * @param string $tags
1015
     * @return array[]
1016
     */
1017
    public function getTagsForEscape($tags = '{{,}},[[,]],[!,!],[*,*],[(,)],[+,+],[~,~],[^,^]')
1018
    {
1019
        $srcTags = explode(',', $tags);
1020
        $repTags = array();
1021
        foreach ($srcTags as $tag) {
1022
            $repTags[] = '\\' . $tag[0] . '\\' . $tag[1];
1023
        }
1024
        return array($srcTags, $repTags);
1025
    }
1026
1027
    /**
1028
     * @param $tstart
1029
     * @return array
1030
     */
1031
    public function getTimerStats($tstart)
1032
    {
1033
        $stats = array();
1034
1035
        $stats['totalTime'] = ($this->getMicroTime() - $tstart);
1036
        $stats['queryTime'] = $this->queryTime;
1037
        $stats['phpTime'] = $stats['totalTime'] - $stats['queryTime'];
1038
1039
        $stats['queryTime'] = sprintf("%2.4f s", $stats['queryTime']);
1040
        $stats['totalTime'] = sprintf("%2.4f s", $stats['totalTime']);
1041
        $stats['phpTime'] = sprintf("%2.4f s", $stats['phpTime']);
1042
        $stats['source'] = $this->documentGenerated == 1 ? "database" : "cache";
1043
        $stats['queries'] = isset ($this->executedQueries) ? $this->executedQueries : 0;
1044
        $stats['phpMemory'] = (memory_get_peak_usage(true) / 1024 / 1024) . " mb";
1045
1046
        return $stats;
1047
    }
1048
1049
    public function setConditional()
0 ignored issues
show
Coding Style introduced by
setConditional uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
setConditional uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1050
    {
1051
        if (!empty($_POST) || (defined('MODX_API_MODE') && MODX_API_MODE) || $this->getLoginUserID('mgr') || !$this->useConditional || empty($this->recentUpdate)) {
1052
            return;
1053
        }
1054
        $last_modified = gmdate('D, d M Y H:i:s T', $this->recentUpdate);
1055
        $etag = md5($last_modified);
1056
        $HTTP_IF_MODIFIED_SINCE = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false;
1057
        $HTTP_IF_NONE_MATCH = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? $_SERVER['HTTP_IF_NONE_MATCH'] : false;
1058
        header('Pragma: no-cache');
1059
1060
        if ($HTTP_IF_MODIFIED_SINCE == $last_modified || strpos($HTTP_IF_NONE_MATCH, $etag) !== false) {
1061
            header('HTTP/1.1 304 Not Modified');
1062
            header('Content-Length: 0');
1063
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method setConditional() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1064
        } else {
1065
            header("Last-Modified: {$last_modified}");
1066
            header("ETag: '{$etag}'");
1067
        }
1068
    }
1069
1070
    /**
1071
     * Checks the publish state of page
1072
     */
1073
    public function updatePubStatus()
0 ignored issues
show
Coding Style introduced by
updatePubStatus uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1074
    {
1075
        $cacheRefreshTime = 0;
1076
        $recent_update = 0;
1077
        @include(MODX_BASE_PATH . $this->getCacheFolder() . 'sitePublishing.idx.php');
1078
        $this->recentUpdate = $recent_update;
1079
1080
        $timeNow = $_SERVER['REQUEST_TIME'] + $this->config['server_offset_time'];
1081
        if ($timeNow < $cacheRefreshTime || $cacheRefreshTime == 0) {
1082
            return;
1083
        }
1084
1085
        // now, check for documents that need publishing
1086
        $field = array('published' => 1, 'publishedon' => $timeNow);
1087
        $where = "pub_date <= {$timeNow} AND pub_date!=0 AND published=0";
1088
        $result_pub = $this->db->select( 'id', '[+prefix+]site_content',  $where);
1089
        $this->db->update($field, '[+prefix+]site_content', $where);
1090 View Code Duplication
        if( $this->db->getRecordCount( $result_pub ) >= 1 ) { //Event Published doc
1091
            while ($row_unpub = $this->db->getRow($result_pub)) {
1092
                $this->invokeEvent("OnDocPublished", array (
1093
                    "docid" => $row_unpub['id']
1094
                ));
1095
            }
1096
        }
1097
1098
        // now, check for documents that need un-publishing
1099
        $field = array('published' => 0, 'publishedon' => 0);
1100
        $where = "unpub_date <= {$timeNow} AND unpub_date!=0 AND published=1";
1101
        $result_unpub = $this->db->select( 'id', '[+prefix+]site_content',  $where);
1102
        $this->db->update($field, '[+prefix+]site_content', $where);
1103 View Code Duplication
        if( $this->db->getRecordCount( $result_unpub ) >= 1 ) { //Event unPublished doc
1104
            while ($row_unpub = $this->db->getRow($result_unpub)) {
1105
                $this->invokeEvent("OnDocUnPublished", array (
1106
                    "docid" => $row_unpub['id']
1107
                ));
1108
            }
1109
        }
1110
1111
        $this->recentUpdate = $timeNow;
1112
1113
        // clear the cache
1114
        $this->clearCache('full');
1115
    }
1116
1117
    public function checkPublishStatus()
1118
    {
1119
        $this->updatePubStatus();
1120
    }
1121
1122
    /**
1123
     * Final jobs.
1124
     *
1125
     * - cache page
1126
     */
1127
    public function postProcess()
1128
    {
1129
        // if the current document was generated, cache it!
1130
        $cacheable = ($this->config['enable_cache'] && $this->documentObject['cacheable']) ? 1 : 0;
1131
        if ($cacheable && $this->documentGenerated && $this->documentObject['type'] == 'document' && $this->documentObject['published']) {
1132
            // invoke OnBeforeSaveWebPageCache event
1133
            $this->invokeEvent("OnBeforeSaveWebPageCache");
1134
1135
            if (!empty($this->cacheKey) && is_scalar($this->cacheKey)) {
1136
                // get and store document groups inside document object. Document groups will be used to check security on cache pages
1137
                $where = "document='{$this->documentIdentifier}'";
1138
                $rs = $this->db->select('document_group', '[+prefix+]document_groups', $where);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1139
                $docGroups = $this->db->getColumn('document_group', $rs);
1140
1141
                // Attach Document Groups and Scripts
1142
                if (is_array($docGroups)) {
1143
                    $this->documentObject['__MODxDocGroups__'] = implode(",", $docGroups);
1144
                }
1145
1146
                $docObjSerial = serialize($this->documentObject);
1147
                $cacheContent = $docObjSerial . "<!--__MODxCacheSpliter__-->" . $this->documentContent;
1148
                $page_cache_path = MODX_BASE_PATH . $this->getHashFile($this->cacheKey);
1149
                file_put_contents($page_cache_path, "<?php die('Unauthorized access.'); ?>$cacheContent");
1150
            }
1151
        }
1152
1153
        // Useful for example to external page counters/stats packages
1154
        $this->invokeEvent('OnWebPageComplete');
1155
1156
        // end post processing
1157
    }
1158
1159
    /**
1160
     * @param $content
1161
     * @param string $left
1162
     * @param string $right
1163
     * @return array
1164
     */
1165
    public function getTagsFromContent($content, $left = '[+', $right = '+]')
1166
    {
1167
        $_ = $this->_getTagsFromContent($content, $left, $right);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $_. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1168
        if (empty($_)) {
1169
            return array();
1170
        }
1171
        foreach ($_ as $v) {
1172
            $tags[0][] = "{$left}{$v}{$right}";
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...
1173
            $tags[1][] = $v;
1174
        }
1175
        return $tags;
0 ignored issues
show
Bug introduced by
The variable $tags 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...
1176
    }
1177
1178
    /**
1179
     * @param $content
1180
     * @param string $left
1181
     * @param string $right
1182
     * @return array
1183
     */
1184
    public function _getTagsFromContent($content, $left = '[+', $right = '+]')
1185
    {
1186
        if (strpos($content, $left) === false) {
1187
            return array();
1188
        }
1189
        $spacer = md5('<<<EVO>>>');
1190
        if($left==='{{' && strpos($content,';}}')!==false)  $content = str_replace(';}}', sprintf(';}%s}',   $spacer),$content);
1191
        if($left==='{{' && strpos($content,'{{}}')!==false) $content = str_replace('{{}}',sprintf('{%$1s{}%$1s}',$spacer),$content);
1192
        if($left==='[[' && strpos($content,']]]]')!==false) $content = str_replace(']]]]',sprintf(']]%s]]',  $spacer),$content);
1193
        if($left==='[[' && strpos($content,']]]')!==false)  $content = str_replace(']]]', sprintf(']%s]]',   $spacer),$content);
1194
1195
        $pos['<![CDATA['] = strpos($content, '<![CDATA[');
0 ignored issues
show
Coding Style Comprehensibility introduced by
$pos was never initialized. Although not strictly required by PHP, it is generally a good practice to add $pos = 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...
1196
        $pos[']]>'] = strpos($content, ']]>');
1197
1198
        if ($pos['<![CDATA['] !== false && $pos[']]>'] !== false) {
1199
            $content = substr($content, 0, $pos['<![CDATA[']) . substr($content, $pos[']]>'] + 3);
1200
        }
1201
1202
        $lp = explode($left, $content);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $lp. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1203
        $piece = array();
1204
        foreach ($lp as $lc => $lv) {
1205
            if ($lc !== 0) {
1206
                $piece[] = $left;
1207
            }
1208
            if (strpos($lv, $right) === false) {
1209
                $piece[] = $lv;
1210
            } else {
1211
                $rp = explode($right, $lv);
1212
                foreach ($rp as $rc => $rv) {
1213
                    if ($rc !== 0) {
1214
                        $piece[] = $right;
1215
                    }
1216
                    $piece[] = $rv;
1217
                }
1218
            }
1219
        }
1220
        $lc = 0;
1221
        $rc = 0;
1222
        $fetch = '';
1223
        $tags = array();
1224
        foreach ($piece as $v) {
1225
            if ($v === $left) {
1226
                if (0 < $lc) {
1227
                    $fetch .= $left;
1228
                }
1229
                $lc++;
1230
            } elseif ($v === $right) {
1231
                if ($lc === 0) {
1232
                    continue;
1233
                }
1234
                $rc++;
1235
                if ($lc === $rc) {
1236
                    // #1200 Enable modifiers in Wayfinder - add nested placeholders to $tags like for $fetch = "phx:input=`[+wf.linktext+]`:test"
1237
                    if (strpos($fetch, $left) !== false) {
1238
                        $nested = $this->_getTagsFromContent($fetch, $left, $right);
1239
                        foreach ($nested as $tag) {
1240
                            if (!in_array($tag, $tags)) {
1241
                                $tags[] = $tag;
1242
                            }
1243
                        }
1244
                    }
1245
1246
                    if (!in_array($fetch, $tags)) {  // Avoid double Matches
1247
                        $tags[] = $fetch; // Fetch
1248
                    };
1249
                    $fetch = ''; // and reset
1250
                    $lc = 0;
1251
                    $rc = 0;
1252
                } else {
1253
                    $fetch .= $right;
1254
                }
1255
            } else {
1256
                if (0 < $lc) {
1257
                    $fetch .= $v;
1258
                } else {
1259
                    continue;
1260
                }
1261
            }
1262
        }
1263
        foreach($tags as $i=>$tag) {
1264
            if(strpos($tag,$spacer)!==false) $tags[$i] = str_replace($spacer, '', $tag);
1265
        }
1266
        return $tags;
1267
    }
1268
1269
    /**
1270
     * Merge content fields and TVs
1271
     *
1272
     * @param $content
1273
     * @param bool $ph
1274
     * @return string
1275
     * @internal param string $template
1276
     */
1277
    public function mergeDocumentContent($content, $ph = false)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ph. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Coding Style introduced by
mergeDocumentContent uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1278
    {
1279 View Code Duplication
        if ($this->config['enable_at_syntax']) {
1280
            if (stripos($content, '<@LITERAL>') !== false) {
1281
                $content = $this->escapeLiteralTagsContent($content);
1282
            }
1283
        }
1284
        if (strpos($content, '[*') === false) {
1285
            return $content;
1286
        }
1287
        if (!isset($this->documentIdentifier)) {
1288
            return $content;
1289
        }
1290
        if (!isset($this->documentObject) || empty($this->documentObject)) {
1291
            return $content;
1292
        }
1293
1294
        if (!$ph) {
1295
            $ph = $this->documentObject;
1296
        }
1297
1298
        $matches = $this->getTagsFromContent($content, '[*', '*]');
1299
        if (!$matches) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matches of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1300
            return $content;
1301
        }
1302
1303
        foreach ($matches[1] as $i => $key) {
1304
            if(strpos($key,'[+')!==false) continue; // Allow chunk {{chunk?&param=`xxx`}} with [*tv_name_[+param+]*] as content
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1305
            if (substr($key, 0, 1) == '#') {
1306
                $key = substr($key, 1);
1307
            } // remove # for QuickEdit format
1308
1309
            list($key, $modifiers) = $this->splitKeyAndFilter($key);
1310
            if (strpos($key, '@') !== false) {
1311
                list($key, $context) = explode('@', $key, 2);
1312
            } else {
1313
                $context = false;
1314
            }
1315
1316
            // if(!isset($ph[$key]) && !$context) continue; // #1218 TVs/PHs will not be rendered if custom_meta_title is not assigned to template like [*custom_meta_title:ne:then=`[*custom_meta_title*]`:else=`[*pagetitle*]`*]
0 ignored issues
show
Unused Code Comprehensibility introduced by
77% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1317
            if ($context) {
1318
                $value = $this->_contextValue("{$key}@{$context}", $this->documentObject['parent']);
0 ignored issues
show
Documentation introduced by
$this->documentObject['parent'] is of type array|string, but the function expects a boolean|integer.

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...
1319
            } else {
1320
                $value = isset($ph[$key]) ? $ph[$key] : '';
1321
            }
1322
1323
            if (is_array($value)) {
1324
                include_once(MODX_MANAGER_PATH . 'includes/tmplvars.format.inc.php');
1325
                include_once(MODX_MANAGER_PATH . 'includes/tmplvars.commands.inc.php');
1326
                $value = getTVDisplayFormat($value[0], $value[1], $value[2], $value[3], $value[4]);
1327
            }
1328
1329
            $s = &$matches[0][$i];
1330
            if ($modifiers !== false) {
1331
                $value = $this->applyFilter($value, $modifiers, $key);
1332
            }
1333
1334 View Code Duplication
            if (strpos($content, $s) !== false) {
1335
                $content = str_replace($s, $value, $content);
1336
            } elseif($this->debug) {
1337
                $this->addLog('mergeDocumentContent parse error', $_SERVER['REQUEST_URI'] . $s, 2);
1338
            }
1339
        }
1340
1341
        return $content;
1342
    }
1343
1344
    /**
1345
     * @param $key
1346
     * @param bool|int $parent
1347
     * @return bool|mixed|string
1348
     */
1349
    public function _contextValue($key, $parent = false)
1350
    {
1351
        if (preg_match('/@\d+\/u/', $key)) {
1352
            $key = str_replace(array('@', '/u'), array('@u(', ')'), $key);
1353
        }
1354
        list($key, $str) = explode('@', $key, 2);
1355
1356
        if (strpos($str, '(')) {
1357
            list($context, $option) = explode('(', $str, 2);
1358
        } else {
1359
            list($context, $option) = array($str, false);
1360
        }
1361
1362
        if ($option) {
1363
            $option = trim($option, ')(\'"`');
1364
        }
1365
1366
        switch (strtolower($context)) {
1367
            case 'site_start':
1368
                $docid = $this->config['site_start'];
1369
                break;
1370
            case 'parent':
1371
            case 'p':
1372
                $docid = $parent;
1373
                if ($docid == 0) {
1374
                    $docid = $this->config['site_start'];
1375
                }
1376
                break;
1377
            case 'ultimateparent':
1378
            case 'uparent':
1379
            case 'up':
1380
            case 'u':
1381 View Code Duplication
                if (strpos($str, '(') !== false) {
1382
                    $top = substr($str, strpos($str, '('));
1383
                    $top = trim($top, '()"\'');
1384
                } else {
1385
                    $top = 0;
1386
                }
1387
                $docid = $this->getUltimateParentId($this->documentIdentifier, $top);
1388
                break;
1389
            case 'alias':
1390
                $str = substr($str, strpos($str, '('));
1391
                $str = trim($str, '()"\'');
1392
                $docid = $this->getIdFromAlias($str);
1393
                break;
1394 View Code Duplication
            case 'prev':
1395
                if (!$option) {
1396
                    $option = 'menuindex,ASC';
1397
                } elseif (strpos($option, ',') === false) {
1398
                    $option .= ',ASC';
1399
                }
1400
                list($by, $dir) = explode(',', $option, 2);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $by. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1401
                $children = $this->getActiveChildren($parent, $by, $dir);
1402
                $find = false;
1403
                $prev = false;
1404
                foreach ($children as $row) {
1405
                    if ($row['id'] == $this->documentIdentifier) {
1406
                        $find = true;
1407
                        break;
1408
                    }
1409
                    $prev = $row;
1410
                }
1411
                if ($find) {
1412
                    if (isset($prev[$key])) {
1413
                        return $prev[$key];
1414
                    } else {
1415
                        $docid = $prev['id'];
1416
                    }
1417
                } else {
1418
                    $docid = '';
1419
                }
1420
                break;
1421 View Code Duplication
            case 'next':
1422
                if (!$option) {
1423
                    $option = 'menuindex,ASC';
1424
                } elseif (strpos($option, ',') === false) {
1425
                    $option .= ',ASC';
1426
                }
1427
                list($by, $dir) = explode(',', $option, 2);
1428
                $children = $this->getActiveChildren($parent, $by, $dir);
1429
                $find = false;
1430
                $next = false;
1431
                foreach ($children as $row) {
1432
                    if ($find) {
1433
                        $next = $row;
1434
                        break;
1435
                    }
1436
                    if ($row['id'] == $this->documentIdentifier) {
1437
                        $find = true;
1438
                    }
1439
                }
1440
                if ($find) {
1441
                    if (isset($next[$key])) {
1442
                        return $next[$key];
1443
                    } else {
1444
                        $docid = $next['id'];
1445
                    }
1446
                } else {
1447
                    $docid = '';
1448
                }
1449
                break;
1450
            default:
1451
                $docid = $str;
1452
        }
1453
        if (preg_match('@^[1-9][0-9]*$@', $docid)) {
1454
            $value = $this->getField($key, $docid);
1455
        } else {
1456
            $value = '';
1457
        }
1458
        return $value;
1459
    }
1460
1461
    /**
1462
     * Merge system settings
1463
     *
1464
     * @param $content
1465
     * @param bool|array $ph
1466
     * @return string
1467
     * @internal param string $template
1468
     */
1469
    public function mergeSettingsContent($content, $ph = false)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ph. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Coding Style introduced by
mergeSettingsContent uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1470
    {
1471 View Code Duplication
        if ($this->config['enable_at_syntax']) {
1472
            if (stripos($content, '<@LITERAL>') !== false) {
1473
                $content = $this->escapeLiteralTagsContent($content);
1474
            }
1475
        }
1476
        if (strpos($content, '[(') === false) {
1477
            return $content;
1478
        }
1479
1480
        if (empty($ph)) {
1481
            $ph = $this->config;
1482
        }
1483
1484
        $matches = $this->getTagsFromContent($content, '[(', ')]');
1485
        if (empty($matches)) {
1486
            return $content;
1487
        }
1488
1489
        foreach ($matches[1] as $i => $key) {
1490
            list($key, $modifiers) = $this->splitKeyAndFilter($key);
1491
1492
            if (isset($ph[$key])) {
1493
                $value = $ph[$key];
1494
            } else {
1495
                continue;
1496
            }
1497
1498
            if ($modifiers !== false) {
1499
                $value = $this->applyFilter($value, $modifiers, $key);
1500
            }
1501
            $s = &$matches[0][$i];
1502 View Code Duplication
            if (strpos($content, $s) !== false) {
1503
                $content = str_replace($s, $value, $content);
1504
            } elseif($this->debug) {
1505
                $this->addLog('mergeSettingsContent parse error', $_SERVER['REQUEST_URI'] . $s, 2);
1506
            }
1507
        }
1508
        return $content;
1509
    }
1510
1511
    /**
1512
     * Merge chunks
1513
     *
1514
     * @param string $content
1515
     * @param bool|array $ph
1516
     * @return string
1517
     */
1518
    public function mergeChunkContent($content, $ph = false)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ph. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Coding Style introduced by
mergeChunkContent uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1519
    {
1520
        if ($this->config['enable_at_syntax']) {
1521
            if (strpos($content, '{{ ') !== false) {
1522
                $content = str_replace(array('{{ ', ' }}'), array('\{\{ ', ' \}\}'), $content);
1523
            }
1524
            if (stripos($content, '<@LITERAL>') !== false) {
1525
                $content = $this->escapeLiteralTagsContent($content);
1526
            }
1527
        }
1528
        if (strpos($content, '{{') === false) {
1529
            return $content;
1530
        }
1531
1532
        if (empty($ph)) {
1533
            $ph = $this->chunkCache;
1534
        }
1535
1536
        $matches = $this->getTagsFromContent($content, '{{', '}}');
1537
        if (empty($matches)) {
1538
            return $content;
1539
        }
1540
1541
        foreach ($matches[1] as $i => $key) {
1542
            $snip_call = $this->_split_snip_call($key);
1543
            $key = $snip_call['name'];
1544
            $params = $this->getParamsFromString($snip_call['params']);
1545
1546
            list($key, $modifiers) = $this->splitKeyAndFilter($key);
1547
1548
            if (!isset($ph[$key])) {
1549
                $ph[$key] = $this->getChunk($key);
1550
            }
1551
            $value = $ph[$key];
1552
1553
            if (is_null($value)) {
1554
                continue;
1555
            }
1556
1557
            $value = $this->parseText($value, $params); // parse local scope placeholers for ConditionalTags
0 ignored issues
show
Bug introduced by
It seems like $params defined by $this->getParamsFromString($snip_call['params']) on line 1544 can also be of type null; however, DocumentParser::parseText() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1558
            $value = $this->mergePlaceholderContent($value, $params);  // parse page global placeholers
0 ignored issues
show
Bug introduced by
It seems like $params defined by $this->getParamsFromString($snip_call['params']) on line 1544 can also be of type null; however, DocumentParser::mergePlaceholderContent() does only seem to accept boolean|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1559
            if ($this->config['enable_at_syntax']) {
1560
                $value = $this->mergeConditionalTagsContent($value);
1561
            }
1562
            $value = $this->mergeDocumentContent($value);
1563
            $value = $this->mergeSettingsContent($value);
1564
            $value = $this->mergeChunkContent($value);
1565
1566
            if ($modifiers !== false) {
1567
                $value = $this->applyFilter($value, $modifiers, $key);
1568
            }
1569
1570
            $s = &$matches[0][$i];
1571 View Code Duplication
            if (strpos($content, $s) !== false) {
1572
                $content = str_replace($s, $value, $content);
1573
            } elseif($this->debug) {
1574
                $this->addLog('mergeChunkContent parse error', $_SERVER['REQUEST_URI'] . $s, 2);
1575
            }
1576
        }
1577
        return $content;
1578
    }
1579
1580
    /**
1581
     * Merge placeholder values
1582
     *
1583
     * @param string $content
1584
     * @param bool|array $ph
1585
     * @return string
1586
     */
1587
    public function mergePlaceholderContent($content, $ph = false)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ph. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Coding Style introduced by
mergePlaceholderContent uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1588
    {
1589
1590 View Code Duplication
        if ($this->config['enable_at_syntax']) {
1591
            if (stripos($content, '<@LITERAL>') !== false) {
1592
                $content = $this->escapeLiteralTagsContent($content);
1593
            }
1594
        }
1595
        if (strpos($content, '[+') === false) {
1596
            return $content;
1597
        }
1598
1599
        if (empty($ph)) {
1600
            $ph = $this->placeholders;
1601
        }
1602
1603
        if ($this->config['enable_at_syntax']) {
1604
            $content = $this->mergeConditionalTagsContent($content);
1605
        }
1606
1607
        $content = $this->mergeDocumentContent($content);
1608
        $content = $this->mergeSettingsContent($content);
1609
        $matches = $this->getTagsFromContent($content, '[+', '+]');
1610
        if (empty($matches)) {
1611
            return $content;
1612
        }
1613
        foreach ($matches[1] as $i => $key) {
1614
1615
            list($key, $modifiers) = $this->splitKeyAndFilter($key);
1616
1617
            if (isset($ph[$key])) {
1618
                $value = $ph[$key];
1619
            } elseif ($key === 'phx') {
1620
                $value = '';
1621
            } else {
1622
                continue;
1623
            }
1624
1625
            if ($modifiers !== false) {
1626
                $modifiers = $this->mergePlaceholderContent($modifiers);
1627
                $value = $this->applyFilter($value, $modifiers, $key);
0 ignored issues
show
Documentation introduced by
$modifiers is of type string, but the function expects a boolean.

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...
1628
            }
1629
            $s = &$matches[0][$i];
1630 View Code Duplication
            if (strpos($content, $s) !== false) {
1631
                $content = str_replace($s, $value, $content);
1632
            } elseif($this->debug) {
1633
                $this->addLog('mergePlaceholderContent parse error', $_SERVER['REQUEST_URI'] . $s, 2);
1634
            }
1635
        }
1636
        return $content;
1637
    }
1638
1639
    /**
1640
     * @param $content
1641
     * @param string $iftag
1642
     * @param string $elseiftag
1643
     * @param string $elsetag
1644
     * @param string $endiftag
1645
     * @return mixed|string
1646
     */
1647
    public function mergeConditionalTagsContent($content, $iftag = '<@IF:', $elseiftag = '<@ELSEIF:', $elsetag = '<@ELSE>', $endiftag = '<@ENDIF>')
0 ignored issues
show
Coding Style introduced by
mergeConditionalTagsContent uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1648
    {
1649
        if (strpos($content, '@IF') !== false) {
1650
            $content = $this->_prepareCTag($content, $iftag, $elseiftag, $elsetag, $endiftag);
1651
        }
1652
1653
        if (strpos($content, $iftag) === false) {
1654
            return $content;
1655
        }
1656
1657
        $sp = '#' . md5('ConditionalTags' . $_SERVER['REQUEST_TIME']) . '#';
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $sp. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1658
        $content = str_replace(array('<?php', '<?=', '<?', '?>'), array("{$sp}b", "{$sp}p", "{$sp}s", "{$sp}e"), $content);
1659
1660
        $pieces = explode('<@IF:', $content);
1661 View Code Duplication
        foreach ($pieces as $i => $split) {
1662
            if ($i === 0) {
1663
                $content = $split;
1664
                continue;
1665
            }
1666
            list($cmd, $text) = explode('>', $split, 2);
1667
            $cmd = str_replace("'", "\'", $cmd);
1668
            $content .= "<?php if(\$this->_parseCTagCMD('" . $cmd . "')): ?>";
1669
            $content .= $text;
1670
        }
1671
        $pieces = explode('<@ELSEIF:', $content);
1672 View Code Duplication
        foreach ($pieces as $i => $split) {
1673
            if ($i === 0) {
1674
                $content = $split;
1675
                continue;
1676
            }
1677
            list($cmd, $text) = explode('>', $split, 2);
1678
            $cmd = str_replace("'", "\'", $cmd);
1679
            $content .= "<?php elseif(\$this->_parseCTagCMD('" . $cmd . "')): ?>";
1680
            $content .= $text;
1681
        }
1682
1683
        $content = str_replace(array('<@ELSE>', '<@ENDIF>'), array('<?php else:?>', '<?php endif;?>'), $content);
1684
        ob_start();
1685
        $content = eval('?>' . $content);
0 ignored issues
show
Unused Code introduced by
$content 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...
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
1686
        $content = ob_get_clean();
1687
        $content = str_replace(array("{$sp}b", "{$sp}p", "{$sp}s", "{$sp}e"), array('<?php', '<?=', '<?', '?>'), $content);
1688
1689
        return $content;
1690
    }
1691
1692
    /**
1693
     * @param $content
1694
     * @param string $iftag
1695
     * @param string $elseiftag
1696
     * @param string $elsetag
1697
     * @param string $endiftag
1698
     * @return mixed
1699
     */
1700
    private function _prepareCTag($content, $iftag = '<@IF:', $elseiftag = '<@ELSEIF:', $elsetag = '<@ELSE>', $endiftag = '<@ENDIF>')
1701
    {
1702
        if (strpos($content, '<!--@IF ') !== false) {
1703
            $content = str_replace('<!--@IF ', $iftag, $content);
1704
        } // for jp
1705
        if (strpos($content, '<!--@IF:') !== false) {
1706
            $content = str_replace('<!--@IF:', $iftag, $content);
1707
        }
1708
        if (strpos($content, $iftag) === false) {
1709
            return $content;
1710
        }
1711
        if (strpos($content, '<!--@ELSEIF:') !== false) {
1712
            $content = str_replace('<!--@ELSEIF:', $elseiftag, $content);
1713
        } // for jp
1714
        if (strpos($content, '<!--@ELSE-->') !== false) {
1715
            $content = str_replace('<!--@ELSE-->', $elsetag, $content);
1716
        }  // for jp
1717
        if (strpos($content, '<!--@ENDIF-->') !== false) {
1718
            $content = str_replace('<!--@ENDIF-->', $endiftag, $content);
1719
        }    // for jp
1720
        if (strpos($content, '<@ENDIF-->') !== false) {
1721
            $content = str_replace('<@ENDIF-->', $endiftag, $content);
1722
        }
1723
        $tags = array($iftag, $elseiftag, $elsetag, $endiftag);
1724
        $content = str_ireplace($tags, $tags, $content); // Change to capital letters
1725
        return $content;
1726
    }
1727
1728
    /**
1729
     * @param $cmd
1730
     * @return mixed|string
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use integer|string|boolean.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
1731
     */
1732
    private function _parseCTagCMD($cmd)
1733
    {
1734
        $cmd = trim($cmd);
1735
        $reverse = substr($cmd, 0, 1) === '!' ? true : false;
1736
        if ($reverse) {
1737
            $cmd = ltrim($cmd, '!');
1738
        }
1739
        if (strpos($cmd, '[!') !== false) {
1740
            $cmd = str_replace(array('[!', '!]'), array('[[', ']]'), $cmd);
1741
        }
1742
        $safe = 0;
1743
        while ($safe < 20) {
1744
            $bt = md5($cmd);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $bt. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1745
            if (strpos($cmd, '[*') !== false) {
1746
                $cmd = $this->mergeDocumentContent($cmd);
1747
            }
1748
            if (strpos($cmd, '[(') !== false) {
1749
                $cmd = $this->mergeSettingsContent($cmd);
1750
            }
1751
            if (strpos($cmd, '{{') !== false) {
1752
                $cmd = $this->mergeChunkContent($cmd);
1753
            }
1754
            if (strpos($cmd, '[[') !== false) {
1755
                $cmd = $this->evalSnippets($cmd);
1756
            }
1757
            if (strpos($cmd, '[+') !== false && strpos($cmd, '[[') === false) {
1758
                $cmd = $this->mergePlaceholderContent($cmd);
1759
            }
1760
            if ($bt === md5($cmd)) {
1761
                break;
1762
            }
1763
            $safe++;
1764
        }
1765
        $cmd = ltrim($cmd);
1766
        $cmd = rtrim($cmd, '-');
1767
        $cmd = str_ireplace(array(' and ', ' or '), array('&&', '||'), $cmd);
1768
1769
        if (!preg_match('@^[0-9]*$@', $cmd) && preg_match('@^[0-9<= \-\+\*/\(\)%!&|]*$@', $cmd)) {
1770
            $cmd = eval("return {$cmd};");
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
1771
        } else {
1772
            $_ = explode(',', '[*,[(,{{,[[,[!,[+');
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $_. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1773
            foreach ($_ as $left) {
1774
                if (strpos($cmd, $left) !== false) {
1775
                    $cmd = 0;
1776
                    break;
1777
                }
1778
            }
1779
        }
1780
        $cmd = trim($cmd);
1781
        if (!preg_match('@^[0-9]+$@', $cmd)) {
1782
            $cmd = empty($cmd) ? 0 : 1;
1783
        } elseif ($cmd <= 0) {
1784
            $cmd = 0;
1785
        }
1786
1787
        if ($reverse) {
1788
            $cmd = !$cmd;
1789
        }
1790
1791
        return $cmd;
1792
    }
1793
1794
    /**
1795
     * Remove Comment-Tags from output like <!--@- Comment -@-->
1796
     * @param $content
1797
     * @param string $left
1798
     * @param string $right
1799
     * @return mixed
1800
     */
1801
    function ignoreCommentedTagsContent($content, $left = '<!--@-', $right = '-@-->')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1802
    {
1803
        if (strpos($content, $left) === false) {
1804
            return $content;
1805
        }
1806
1807
        $matches = $this->getTagsFromContent($content, $left, $right);
1808
        if (!empty($matches)) {
1809
            foreach ($matches[0] as $i => $v) {
1810
                $addBreakMatches[$i] = $v . "\n";
0 ignored issues
show
Coding Style Comprehensibility introduced by
$addBreakMatches was never initialized. Although not strictly required by PHP, it is generally a good practice to add $addBreakMatches = 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...
1811
            }
1812
            $content = str_replace($addBreakMatches, '', $content);
0 ignored issues
show
Bug introduced by
The variable $addBreakMatches 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...
1813
            if (strpos($content, $left) !== false) {
1814
                $content = str_replace($matches[0], '', $content);
1815
            }
1816
        }
1817
        return $content;
1818
    }
1819
1820
    /**
1821
     * @param $content
1822
     * @param string $left
1823
     * @param string $right
1824
     * @return mixed
1825
     */
1826
    public function escapeLiteralTagsContent($content, $left = '<@LITERAL>', $right = '<@ENDLITERAL>')
0 ignored issues
show
Coding Style introduced by
escapeLiteralTagsContent uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1827
    {
1828
        if (stripos($content, $left) === false) {
1829
            return $content;
1830
        }
1831
1832
        $matches = $this->getTagsFromContent($content, $left, $right);
1833
        if (empty($matches)) {
1834
            return $content;
1835
        }
1836
1837
        list($sTags, $rTags) = $this->getTagsForEscape();
1838
        foreach ($matches[1] as $i => $v) {
1839
            $v = str_ireplace($sTags, $rTags, $v);
1840
            $s = &$matches[0][$i];
1841 View Code Duplication
            if (strpos($content, $s) !== false) {
1842
                $content = str_replace($s, $v, $content);
1843
            } elseif($this->debug) {
1844
                $this->addLog('ignoreCommentedTagsContent parse error', $_SERVER['REQUEST_URI'] . $s, 2);
1845
            }
1846
        }
1847
        return $content;
1848
    }
1849
1850
    /**
1851
     * Detect PHP error according to MODX error level
1852
     *
1853
     * @param integer $error PHP error level
1854
     * @return boolean Error detected
1855
     */
1856
1857
    public function detectError($error)
1858
    {
1859
        $detected = false;
1860
        if ($this->config['error_reporting'] == 99 && $error) {
1861
            $detected = true;
1862
        } elseif ($this->config['error_reporting'] == 2 && ($error & ~E_NOTICE)) {
1863
            $detected = true;
1864
        } elseif ($this->config['error_reporting'] == 1 && ($error & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT)) {
1865
            $detected = true;
1866
        }
1867
        return $detected;
1868
    }
1869
1870
    /**
1871
     * Run a plugin
1872
     *
1873
     * @param string $pluginCode Code to run
1874
     * @param array $params
1875
     */
1876
    public function evalPlugin($pluginCode, $params)
1877
    {
1878
        $modx = &$this;
1879
        $modx->event->params = &$params; // store params inside event object
1880
        if (is_array($params)) {
1881
            extract($params, EXTR_SKIP);
1882
        }
1883
        /* if uncomment incorrect work plugin, cant understend where use this code and for what?
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1884
        // This code will avoid further execution of plugins in case they cause a fatal-error. clearCache() will delete those locks to allow execution of locked plugins again.
1885
        // Related to https://github.com/modxcms/evolution/issues/1130
1886
        $lock_file_path = MODX_BASE_PATH . 'assets/cache/lock_' . str_replace(' ','-',strtolower($this->event->activePlugin)) . '.pageCache.php';
1887
        if($this->isBackend()) {
1888
            if(is_file($lock_file_path)) {
1889
                $msg = sprintf("Plugin parse error, Temporarily disabled '%s'.", $this->event->activePlugin);
1890
                $this->logEvent(0, 3, $msg, $msg);
1891
                return;
1892
            }
1893
            elseif(stripos($this->event->activePlugin,'ElementsInTree')===false) touch($lock_file_path);
1894
        }*/
1895
        ob_start();
1896
        eval($pluginCode);
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
1897
        $msg = ob_get_contents();
1898
        ob_end_clean();
1899
        // When reached here, no fatal error occured so the lock should be removed.
1900
        /*if(is_file($lock_file_path)) unlink($lock_file_path);*/
0 ignored issues
show
Unused Code Comprehensibility introduced by
77% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1901
1902 View Code Duplication
        if ((0 < $this->config['error_reporting']) && $msg && isset($php_errormsg)) {
1903
            $error_info = error_get_last();
1904
            if ($this->detectError($error_info['type'])) {
1905
                $msg = ($msg === false) ? 'ob_get_contents() error' : $msg;
1906
                $this->messageQuit('PHP Parse Error', '', true, $error_info['type'], $error_info['file'], 'Plugin', $error_info['message'], $error_info['line'], $msg);
1907
                if ($this->isBackend()) {
1908
                    $this->event->alert('An error occurred while loading. Please see the event log for more information.<p>' . $msg . '</p>');
1909
                }
1910
            }
1911
        } else {
1912
            echo $msg;
1913
        }
1914
        unset($modx->event->params);
1915
    }
1916
1917
    /**
1918
     * Run a snippet
1919
     *
1920
     * @param $phpcode
1921
     * @param array $params
1922
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|object|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
1923
     * @internal param string $snippet Code to run
1924
     */
1925
    public function evalSnippet($phpcode, $params)
1926
    {
1927
        $modx = &$this;
1928
        /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1929
        if(isset($params) && is_array($params)) {
1930
            foreach($params as $k=>$v) {
1931
                $v = strtolower($v);
1932
                if($v==='false')    $params[$k] = false;
1933
                elseif($v==='true') $params[$k] = true;
1934
            }
1935
        }*/
1936
        $modx->event->params = &$params; // store params inside event object
1937
        if (is_array($params)) {
1938
            extract($params, EXTR_SKIP);
1939
        }
1940
        ob_start();
1941
        if (strpos($phpcode, ';') !== false) {
1942
            $return = eval($phpcode);
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
1943
        } else {
1944
            $return = call_user_func_array($phpcode, array($params));
1945
        }
1946
        $echo = ob_get_contents();
1947
        ob_end_clean();
1948 View Code Duplication
        if ((0 < $this->config['error_reporting']) && isset($php_errormsg)) {
1949
            $error_info = error_get_last();
1950
            if ($this->detectError($error_info['type'])) {
1951
                $echo = ($echo === false) ? 'ob_get_contents() error' : $echo;
1952
                $this->messageQuit('PHP Parse Error', '', true, $error_info['type'], $error_info['file'], 'Snippet', $error_info['message'], $error_info['line'], $echo);
1953
                if ($this->isBackend()) {
1954
                    $this->event->alert('An error occurred while loading. Please see the event log for more information<p>' . $echo . $return . '</p>');
1955
                }
1956
            }
1957
        }
1958
        unset($modx->event->params);
1959
        if (is_array($return) || is_object($return)) {
1960
            return $return;
1961
        } else {
1962
            return $echo . $return;
1963
        }
1964
    }
1965
1966
    /**
1967
     * Run snippets as per the tags in $documentSource and replace the tags with the returned values.
1968
     *
1969
     * @param $content
1970
     * @return string
1971
     * @internal param string $documentSource
1972
     */
1973
    public function evalSnippets($content)
0 ignored issues
show
Coding Style introduced by
evalSnippets uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1974
    {
1975
        if (strpos($content, '[[') === false) {
1976
            return $content;
1977
        }
1978
1979
        $matches = $this->getTagsFromContent($content, '[[', ']]');
1980
1981
        if (empty($matches)) {
1982
            return $content;
1983
        }
1984
1985
        $this->snipLapCount++;
1986
        if ($this->dumpSnippets) {
1987
            $this->snippetsCode .= sprintf('<fieldset><legend><b style="color: #821517;">PARSE PASS %s</b></legend><p>The following snippets (if any) were parsed during this pass.</p>', $this->snipLapCount);
1988
        }
1989
1990
        foreach ($matches[1] as $i => $call) {
1991
            $s = &$matches[0][$i];
1992
            if (substr($call, 0, 2) === '$_') {
1993
                if (strpos($content, '_PHX_INTERNAL_') === false) {
1994
                    $value = $this->_getSGVar($call);
1995
                } else {
1996
                    $value = $s;
1997
                }
1998 View Code Duplication
                if (strpos($content, $s) !== false) {
1999
                    $content = str_replace($s, $value, $content);
2000
                } elseif($this->debug) {
2001
                    $this->addLog('evalSnippetsSGVar parse error', $_SERVER['REQUEST_URI'] . $s, 2);
2002
                }
2003
                continue;
2004
            }
2005
            $value = $this->_get_snip_result($call);
2006
            if (is_null($value)) {
2007
                continue;
2008
            }
2009
2010 View Code Duplication
            if (strpos($content, $s) !== false) {
2011
                $content = str_replace($s, $value, $content);
2012
            } elseif($this->debug) {
2013
                $this->addLog('evalSnippets parse error', $_SERVER['REQUEST_URI'] . $s, 2);
2014
            }
2015
        }
2016
2017
        if ($this->dumpSnippets) {
2018
            $this->snippetsCode .= '</fieldset><br />';
2019
        }
2020
2021
        return $content;
2022
    }
2023
2024
    /**
2025
     * @param $value
2026
     * @return mixed|string
2027
     */
2028
    public function _getSGVar($value)
0 ignored issues
show
Coding Style introduced by
_getSGVar uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
2029
    { // Get super globals
2030
        $key = $value;
2031
        $_ = $this->config['enable_filter'];
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $_. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2032
        $this->config['enable_filter'] = 1;
2033
        list($key, $modifiers) = $this->splitKeyAndFilter($key);
2034
        $this->config['enable_filter'] = $_;
2035
        $key = str_replace(array('(', ')'), array("['", "']"), $key);
2036
        $key = rtrim($key, ';');
2037
        if (strpos($key, '$_SESSION') !== false) {
2038
            $_ = $_SESSION;
2039
            $key = str_replace('$_SESSION', '$_', $key);
2040
            if (isset($_['mgrFormValues'])) {
2041
                unset($_['mgrFormValues']);
2042
            }
2043
            if (isset($_['token'])) {
2044
                unset($_['token']);
2045
            }
2046
        }
2047
        if (strpos($key, '[') !== false) {
2048
            $value = $key ? eval("return {$key};") : '';
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
2049
        } elseif (0 < eval("return count({$key});")) {
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
2050
            $value = eval("return print_r({$key},true);");
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
2051
        } else {
2052
            $value = '';
2053
        }
2054
        if ($modifiers !== false) {
2055
            $value = $this->applyFilter($value, $modifiers, $key);
2056
        }
2057
        return $value;
2058
    }
2059
2060
    /**
2061
     * @param $piece
2062
     * @return null|string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null|array|object? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
2063
     */
2064
    private function _get_snip_result($piece)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
2065
    {
2066
        if (ltrim($piece) !== $piece) {
2067
            return '';
2068
        }
2069
2070
        $eventtime = $this->dumpSnippets ? $this->getMicroTime() : 0;
2071
2072
        $snip_call = $this->_split_snip_call($piece);
2073
        $key = $snip_call['name'];
2074
2075
        list($key, $modifiers) = $this->splitKeyAndFilter($key);
2076
        $snip_call['name'] = $key;
2077
        $snippetObject = $this->_getSnippetObject($key);
2078
        if (is_null($snippetObject['content'])) {
2079
            return null;
2080
        }
2081
2082
        $this->currentSnippet = $snippetObject['name'];
2083
2084
        // current params
2085
        $params = $this->getParamsFromString($snip_call['params']);
2086
2087
        if (!isset($snippetObject['properties'])) {
2088
            $snippetObject['properties'] = '';
2089
        }
2090
        $default_params = $this->parseProperties($snippetObject['properties'], $this->currentSnippet, 'snippet');
2091
        $params = array_merge($default_params, $params);
2092
2093
        $value = $this->evalSnippet($snippetObject['content'], $params);
2094
        $this->currentSnippet = '';
2095
        if ($modifiers !== false) {
2096
            $value = $this->applyFilter($value, $modifiers, $key);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type array or object; however, DocumentParser::applyFilter() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
2097
        }
2098
2099
        if ($this->dumpSnippets) {
2100
            $eventtime = $this->getMicroTime() - $eventtime;
2101
            $eventtime = sprintf('%2.2f ms', $eventtime * 1000);
2102
            $code = str_replace("\t", '  ', $this->htmlspecialchars($value));
2103
            $piece = str_replace("\t", '  ', $this->htmlspecialchars($piece));
2104
            $print_r_params = str_replace("\t", '  ', $this->htmlspecialchars('$modx->event->params = ' . print_r($params, true)));
2105
            $this->snippetsCode .= sprintf('<fieldset style="margin:1em;"><legend><b>%s</b>(%s)</legend><pre style="white-space: pre-wrap;background-color:#fff;width:90%%;">[[%s]]</pre><pre style="white-space: pre-wrap;background-color:#fff;width:90%%;">%s</pre><pre style="white-space: pre-wrap;background-color:#fff;width:90%%;">%s</pre></fieldset>', $snippetObject['name'], $eventtime, $piece, $print_r_params, $code);
2106
            $this->snippetsTime[] = array('sname' => $key, 'time' => $eventtime);
2107
        }
2108
        return $value;
2109
    }
2110
2111
    /**
2112
     * @param string $string
2113
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
2114
     */
2115
    public function getParamsFromString($string = '')
2116
    {
2117
        if (empty($string)) {
2118
            return array();
2119
        }
2120
2121
        if (strpos($string, '&_PHX_INTERNAL_') !== false) {
2122
            $string = str_replace(array('&_PHX_INTERNAL_091_&', '&_PHX_INTERNAL_093_&'), array('[', ']'), $string);
2123
        }
2124
2125
        $_ = $this->documentOutput;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $_. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2126
        $this->documentOutput = $string;
2127
        $this->invokeEvent('OnBeforeParseParams');
2128
        $string = $this->documentOutput;
2129
        $this->documentOutput = $_;
2130
2131
        $_tmp = $string;
2132
        $_tmp = ltrim($_tmp, '?&');
2133
        $temp_params = array();
2134
        $key = '';
2135
        $value = null;
2136
        while ($_tmp !== '') {
2137
            $bt = $_tmp;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $bt. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2138
            $char = substr($_tmp, 0, 1);
2139
            $_tmp = substr($_tmp, 1);
2140
2141
            if ($char === '=') {
2142
                $_tmp = trim($_tmp);
2143
                $delim = substr($_tmp, 0, 1);
2144
                if (in_array($delim, array('"', "'", '`'))) {
2145
                    $null = null;
2146
                    //list(, $value, $_tmp)
0 ignored issues
show
Unused Code Comprehensibility introduced by
78% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2147
                    list($null, $value, $_tmp) = explode($delim, $_tmp, 3);
2148
                    unset($null);
2149
2150
                    if (substr(trim($_tmp), 0, 2) === '//') {
2151
                        $_tmp = strstr(trim($_tmp), "\n");
2152
                    }
2153
                    $i = 0;
2154
                    while ($delim === '`' && substr(trim($_tmp), 0, 1) !== '&' && 1 < substr_count($_tmp, '`')) {
2155
                        list($inner, $outer, $_tmp) = explode('`', $_tmp, 3);
2156
                        $value .= "`{$inner}`{$outer}";
2157
                        $i++;
2158
                        if (100 < $i) {
2159
                            exit('The nest of values are hard to read. Please use three different quotes.');
0 ignored issues
show
Coding Style Compatibility introduced by
The method getParamsFromString() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2160
                        }
2161
                    }
2162
                    if ($i && $delim === '`') {
2163
                        $value = rtrim($value, '`');
2164
                    }
2165
                } elseif (strpos($_tmp, '&') !== false) {
2166
                    list($value, $_tmp) = explode('&', $_tmp, 2);
2167
                    $value = trim($value);
2168
                } else {
2169
                    $value = $_tmp;
2170
                    $_tmp = '';
2171
                }
2172
            } elseif ($char === '&') {
2173
                if (trim($key) !== '') {
2174
                    $value = '1';
2175
                } else {
2176
                    continue;
2177
                }
2178
            } elseif ($_tmp === '') {
2179
                $key .= $char;
2180
                $value = '1';
2181
            } elseif ($key !== '' || trim($char) !== '') {
2182
                $key .= $char;
2183
            }
2184
2185
            if (isset($value) && !is_null($value)) {
2186
                if (strpos($key, 'amp;') !== false) {
2187
                    $key = str_replace('amp;', '', $key);
2188
                }
2189
                $key = trim($key);
2190 View Code Duplication
                if (strpos($value, '[!') !== false) {
2191
                    $value = str_replace(array('[!', '!]'), array('[[', ']]'), $value);
2192
                }
2193
                $value = $this->mergeDocumentContent($value);
2194
                $value = $this->mergeSettingsContent($value);
2195
                $value = $this->mergeChunkContent($value);
2196
                $value = $this->evalSnippets($value);
2197
                if (substr($value, 0, 6) !== '@CODE:') {
2198
                    $value = $this->mergePlaceholderContent($value);
2199
                }
2200
2201
                $temp_params[][$key] = $value;
2202
2203
                $key = '';
2204
                $value = null;
2205
2206
                $_tmp = ltrim($_tmp, " ,\t");
2207
                if (substr($_tmp, 0, 2) === '//') {
2208
                    $_tmp = strstr($_tmp, "\n");
2209
                }
2210
            }
2211
2212
            if ($_tmp === $bt) {
2213
                $key = trim($key);
2214
                if ($key !== '') {
2215
                    $temp_params[][$key] = '';
2216
                }
2217
                break;
2218
            }
2219
        }
2220
2221
        foreach ($temp_params as $p) {
2222
            $k = key($p);
2223
            if (substr($k, -2) === '[]') {
2224
                $k = substr($k, 0, -2);
2225
                $params[$k][] = current($p);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = 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...
2226
            } elseif (strpos($k, '[') !== false && substr($k, -1) === ']') {
2227
                list($k, $subk) = explode('[', $k, 2);
2228
                $subk = substr($subk, 0, -1);
2229
                $params[$k][$subk] = current($p);
0 ignored issues
show
Bug introduced by
The variable $params 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...
2230
            } else {
2231
                $params[$k] = current($p);
2232
            }
2233
        }
2234
        return $params;
2235
    }
2236
2237
    /**
2238
     * @param $str
2239
     * @return bool|int
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use integer|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
2240
     */
2241
    public function _getSplitPosition($str)
2242
    {
2243
        $closeOpt = false;
2244
        $maybePos = false;
2245
        $inFilter = false;
2246
        $total = strlen($str);
2247
        for ($i = 0; $i < $total; $i++) {
2248
            $c = substr($str, $i, 1);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $c. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2249
            $cc = substr($str, $i, 2);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $cc. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2250
            if (!$inFilter) {
2251
                if ($c === ':') {
2252
                    $inFilter = true;
2253
                } elseif ($c === '?') {
2254
                    $pos = $i;
2255
                } elseif ($c === ' ') {
2256
                    $maybePos = $i;
2257
                } elseif ($c === '&' && $maybePos) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $maybePos of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2258
                    $pos = $maybePos;
2259
                } elseif ($c === "\n") {
2260
                    $pos = $i;
2261
                } else {
2262
                    $pos = false;
2263
                }
2264
            } else {
2265
                if ($cc == $closeOpt) {
2266
                    $closeOpt = false;
2267
                } elseif ($c == $closeOpt) {
2268
                    $closeOpt = false;
2269
                } elseif ($closeOpt) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $closeOpt of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2270
                    continue;
2271
                } elseif ($cc === "('") {
2272
                    $closeOpt = "')";
2273
                } elseif ($cc === '("') {
2274
                    $closeOpt = '")';
2275
                } elseif ($cc === '(`') {
2276
                    $closeOpt = '`)';
2277
                } elseif ($c === '(') {
2278
                    $closeOpt = ')';
2279
                } elseif ($c === '?') {
2280
                    $pos = $i;
2281
                } elseif ($c === ' ' && strpos($str, '?') === false) {
2282
                    $pos = $i;
2283
                } else {
2284
                    $pos = false;
2285
                }
2286
            }
2287
            if ($pos) {
0 ignored issues
show
Bug introduced by
The variable $pos 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...
Bug Best Practice introduced by
The expression $pos of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2288
                break;
2289
            }
2290
        }
2291
        return $pos;
2292
    }
2293
2294
    /**
2295
     * @param $call
2296
     * @return mixed
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
2297
     */
2298
    private function _split_snip_call($call)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
2299
    {
2300
        $spacer = md5('dummy');
2301 View Code Duplication
        if (strpos($call, ']]>') !== false) {
2302
            $call = str_replace(']]>', "]{$spacer}]>", $call);
2303
        }
2304
2305
        $splitPosition = $this->_getSplitPosition($call);
2306
2307
        if ($splitPosition !== false) {
2308
            $name = substr($call, 0, $splitPosition);
2309
            $params = substr($call, $splitPosition + 1);
2310
        } else {
2311
            $name = $call;
2312
            $params = '';
2313
        }
2314
2315
        $snip['name'] = trim($name);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$snip was never initialized. Although not strictly required by PHP, it is generally a good practice to add $snip = 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...
2316 View Code Duplication
        if (strpos($params, $spacer) !== false) {
2317
            $params = str_replace("]{$spacer}]>", ']]>', $params);
2318
        }
2319
        $snip['params'] = ltrim($params, "?& \t\n");
2320
2321
        return $snip;
2322
    }
2323
2324
    /**
2325
     * @param $snip_name
2326
     * @return mixed
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use null|array.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
2327
     */
2328
    private function _getSnippetObject($snip_name)
0 ignored issues
show
Coding Style Naming introduced by
The parameter $snip_name is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
2329
    {
2330
        if (isset($this->snippetCache[$snip_name])) {
2331
            $snippetObject['name'] = $snip_name;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$snippetObject was never initialized. Although not strictly required by PHP, it is generally a good practice to add $snippetObject = 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...
2332
            $snippetObject['content'] = $this->snippetCache[$snip_name];
2333
            if (isset($this->snippetCache["{$snip_name}Props"])) {
2334
                if (!isset($this->snippetCache["{$snip_name}Props"])) {
2335
                    $this->snippetCache["{$snip_name}Props"] = '';
2336
                }
2337
                $snippetObject['properties'] = $this->snippetCache["{$snip_name}Props"];
2338
            }
2339
        } elseif (substr($snip_name, 0, 1) === '@' && isset($this->pluginEvent[trim($snip_name, '@')])) {
2340
            $snippetObject['name'] = trim($snip_name, '@');
0 ignored issues
show
Coding Style Comprehensibility introduced by
$snippetObject was never initialized. Although not strictly required by PHP, it is generally a good practice to add $snippetObject = 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...
2341
            $snippetObject['content'] = sprintf('$rs=$this->invokeEvent("%s",$params);echo trim(implode("",$rs));', trim($snip_name, '@'));
2342
            $snippetObject['properties'] = '';
2343
        } else {
2344
            $where = sprintf("name='%s' AND disabled=0", $this->db->escape($snip_name));
2345
            $rs = $this->db->select('name,snippet,properties', '[+prefix+]site_snippets', $where);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2346
            $count = $this->db->getRecordCount($rs);
2347
            if (1 < $count) {
2348
                exit('Error $modx->_getSnippetObject()' . $snip_name);
0 ignored issues
show
Coding Style Compatibility introduced by
The method _getSnippetObject() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2349
            }
2350
            if ($count) {
2351
                $row = $this->db->getRow($rs);
2352
                $snip_content = $row['snippet'];
2353
                $snip_prop = $row['properties'];
2354
            } else {
2355
                $snip_content = null;
2356
                $snip_prop = '';
2357
            }
2358
            $snippetObject['name'] = $snip_name;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$snippetObject was never initialized. Although not strictly required by PHP, it is generally a good practice to add $snippetObject = 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...
2359
            $snippetObject['content'] = $snip_content;
2360
            $snippetObject['properties'] = $snip_prop;
2361
            $this->snippetCache[$snip_name] = $snip_content;
2362
            $this->snippetCache["{$snip_name}Props"] = $snip_prop;
2363
        }
2364
        return $snippetObject;
2365
    }
2366
2367
    /**
2368
     * @param $text
2369
     * @return mixed
2370
     */
2371
    public function toAlias($text)
2372
    {
2373
        $suff = $this->config['friendly_url_suffix'];
2374
        return str_replace(array('.xml' . $suff, '.rss' . $suff, '.js' . $suff, '.css' . $suff, '.txt' . $suff, '.json' . $suff, '.pdf' . $suff), array('.xml', '.rss', '.js', '.css', '.txt', '.json', '.pdf'), $text);
2375
    }
2376
2377
    /**
2378
     * makeFriendlyURL
2379
     *
2380
     * @desc Create an URL.
2381
     *
2382
     * @param $pre {string} - Friendly URL Prefix. @required
2383
     * @param $suff {string} - Friendly URL Suffix. @required
2384
     * @param $alias {string} - Full document path. @required
2385
     * @param int $isfolder {0; 1}
2386
     * - Is it a folder? Default: 0.
2387
     * @param int $id {integer}
2388
     * - Document id. Default: 0.
2389
     * @return mixed|string {string} - Result URL.
2390
     * - Result URL.
2391
     */
2392
    public function makeFriendlyURL($pre, $suff, $alias, $isfolder = 0, $id = 0)
2393
    {
2394
        if ($id == $this->config['site_start'] && $this->config['seostrict'] === '1') {
2395
            $url = $this->config['base_url'];
2396
        } else {
2397
            $Alias = explode('/', $alias);
2398
            $alias = array_pop($Alias);
2399
            $dir = implode('/', $Alias);
2400
            unset($Alias);
2401
2402
            if ($this->config['make_folders'] === '1' && $isfolder == 1) {
2403
                $suff = '/';
2404
            }
2405
2406
            $url = ($dir != '' ? $dir . '/' : '') . $pre . $alias . $suff;
2407
        }
2408
2409
        $evtOut = $this->invokeEvent('OnMakeDocUrl', array(
2410
            'id' => $id,
2411
            'url' => $url
2412
        ));
2413
2414
        if (is_array($evtOut) && count($evtOut) > 0) {
2415
            $url = array_pop($evtOut);
2416
        }
2417
2418
        return $url;
2419
    }
2420
2421
    /**
2422
     * Convert URL tags [~...~] to URLs
2423
     *
2424
     * @param string $documentSource
2425
     * @return string
2426
     */
2427
    public function rewriteUrls($documentSource)
2428
    {
2429
        // rewrite the urls
2430
        if ($this->config['friendly_urls'] == 1) {
2431
            $aliases = array();
2432
            if (is_array($this->documentListing)) {
2433
                foreach ($this->documentListing as $path => $docid) { // This is big Loop on large site!
2434
                    $aliases[$docid] = $path;
2435
                    $isfolder[$docid] = $this->aliasListing[$docid]['isfolder'];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$isfolder was never initialized. Although not strictly required by PHP, it is generally a good practice to add $isfolder = 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...
2436
                }
2437
            }
2438
2439
            if ($this->config['aliaslistingfolder'] == 1) {
2440
                preg_match_all('!\[\~([0-9]+)\~\]!ise', $documentSource, $match);
2441
                $ids = implode(',', array_unique($match['1']));
2442
                if ($ids) {
2443
                    $res = $this->db->select("id,alias,isfolder,parent,alias_visible", $this->getFullTableName('site_content'), "id IN (" . $ids . ") AND isfolder = '0'");
2444
                    while ($row = $this->db->getRow($res)) {
2445
                        if ($this->config['use_alias_path'] == '1' && $row['parent'] != 0) {
2446
                            $parent = $row['parent'];
2447
                            $path = $aliases[$parent];
2448
2449
                            while (isset($this->aliasListing[$parent]) && $this->aliasListing[$parent]['alias_visible'] == 0) {
2450
                                $path = $this->aliasListing[$parent]['path'];
2451
                                $parent = $this->aliasListing[$parent]['parent'];
2452
                            }
2453
2454
                            $aliases[$row['id']] = $path . '/' . $row['alias'];
2455
                        } else {
2456
                            $aliases[$row['id']] = $row['alias'];
2457
                        }
2458
                        $isfolder[$row['id']] = '0';
0 ignored issues
show
Bug introduced by
The variable $isfolder 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...
2459
                    }
2460
                }
2461
            }
2462
            $in = '!\[\~([0-9]+)\~\]!is';
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $in. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2463
            $isfriendly = ($this->config['friendly_alias_urls'] == 1 ? 1 : 0);
2464
            $pref = $this->config['friendly_url_prefix'];
2465
            $suff = $this->config['friendly_url_suffix'];
2466
            $documentSource = preg_replace_callback($in, function ($m) use ($aliases, $isfolder, $isfriendly, $pref, $suff) {
0 ignored issues
show
Bug introduced by
The variable $isfolder 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...
Comprehensibility introduced by
Avoid variables with short names like $m. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2467
                global $modx;
2468
                $thealias = $aliases[$m[1]];
2469
                $thefolder = $isfolder[$m[1]];
2470
                if ($isfriendly && isset($thealias)) {
2471
                    //found friendly url
2472
                    $out = ($modx->config['seostrict'] == '1' ? $modx->toAlias($modx->makeFriendlyURL($pref, $suff, $thealias, $thefolder, $m[1])) : $modx->makeFriendlyURL($pref, $suff, $thealias, $thefolder, $m[1]));
2473
                } else {
2474
                    //not found friendly url
2475
                    $out = $modx->makeFriendlyURL($pref, $suff, $m[1]);
2476
                }
2477
                return $out;
2478
            }, $documentSource);
2479
2480
        } else {
2481
            $in = '!\[\~([0-9]+)\~\]!is';
2482
            $out = "index.php?id=" . '\1';
2483
            $documentSource = preg_replace($in, $out, $documentSource);
2484
        }
2485
2486
        return $documentSource;
2487
    }
2488
2489
    public function sendStrictURI()
0 ignored issues
show
Coding Style introduced by
sendStrictURI uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
sendStrictURI uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
2490
    {
2491
        $q = $this->q;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $q. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2492
        // FIX URLs
2493
        if (empty($this->documentIdentifier) || $this->config['seostrict'] == '0' || $this->config['friendly_urls'] == '0') {
2494
            return;
2495
        }
2496
        if ($this->config['site_status'] == 0) {
2497
            return;
2498
        }
2499
2500
        $scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
2501
        $len_base_url = strlen($this->config['base_url']);
2502
2503
        $url_path = $q;//LANG
2504
2505 View Code Duplication
        if (substr($url_path, 0, $len_base_url) === $this->config['base_url']) {
2506
            $url_path = substr($url_path, $len_base_url);
2507
        }
2508
2509
        $strictURL = $this->toAlias($this->makeUrl($this->documentIdentifier));
2510
2511 View Code Duplication
        if (substr($strictURL, 0, $len_base_url) === $this->config['base_url']) {
2512
            $strictURL = substr($strictURL, $len_base_url);
2513
        }
2514
        $http_host = $_SERVER['HTTP_HOST'];
2515
        $requestedURL = "{$scheme}://{$http_host}" . '/' . $q; //LANG
2516
2517
        $site_url = $this->config['site_url'];
2518
        $url_query_string = explode('?', $_SERVER['REQUEST_URI']);
2519
        // Strip conflicting id/q from query string
2520
        $qstring = !empty($url_query_string[1]) ? preg_replace("#(^|&)(q|id)=[^&]+#", '', $url_query_string[1]) : '';
2521
2522
        if ($this->documentIdentifier == $this->config['site_start']) {
2523
            if ($requestedURL != $this->config['site_url']) {
2524
                // Force redirect of site start
2525
                // $this->sendErrorPage();
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2526
                if ($qstring) {
2527
                    $url = "{$site_url}?{$qstring}";
2528
                } else {
2529
                    $url = $site_url;
2530
                }
2531
                if ($this->config['base_url'] != $_SERVER['REQUEST_URI']) {
2532
                    if (empty($_POST)) {
2533
                        if (($this->config['base_url'] . '?' . $qstring) != $_SERVER['REQUEST_URI']) {
2534
                            $this->sendRedirect($url, 0, 'REDIRECT_HEADER', 'HTTP/1.0 301 Moved Permanently');
2535
                            exit(0);
0 ignored issues
show
Coding Style Compatibility introduced by
The method sendStrictURI() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2536
                        }
2537
                    }
2538
                }
2539
            }
2540
        } elseif ($url_path != $strictURL && $this->documentIdentifier != $this->config['error_page']) {
2541
            // Force page redirect
2542
            //$strictURL = ltrim($strictURL,'/');
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2543
            if (!empty($qstring)) {
2544
                $url = "{$site_url}{$strictURL}?{$qstring}";
2545
            } else {
2546
                $url = "{$site_url}{$strictURL}";
2547
            }
2548
            $this->sendRedirect($url, 0, 'REDIRECT_HEADER', 'HTTP/1.0 301 Moved Permanently');
2549
            exit(0);
0 ignored issues
show
Coding Style Compatibility introduced by
The method sendStrictURI() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2550
        }
2551
        return;
2552
    }
2553
2554
    /**
2555
     * Get all db fields and TVs for a document/resource
2556
     *
2557
     * @param string $method
2558
     * @param mixed $identifier
2559
     * @param bool $isPrepareResponse
2560
     * @return array
2561
     */
2562
    public function getDocumentObject($method, $identifier, $isPrepareResponse = false)
0 ignored issues
show
Coding Style introduced by
getDocumentObject uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
2563
    {
2564
2565
        $cacheKey = md5(print_r(func_get_args(), true));
2566
        if (isset($this->tmpCache[__FUNCTION__][$cacheKey])) {
2567
            return $this->tmpCache[__FUNCTION__][$cacheKey];
2568
        }
2569
2570
        $tblsc = $this->getFullTableName("site_content");
2571
        $tbldg = $this->getFullTableName("document_groups");
2572
2573
        // allow alias to be full path
2574
        if ($method == 'alias') {
2575
            $identifier = $this->cleanDocumentIdentifier($identifier);
2576
            $method = $this->documentMethod;
2577
        }
2578
        if ($method == 'alias' && $this->config['use_alias_path'] && array_key_exists($identifier, $this->documentListing)) {
2579
            $method = 'id';
2580
            $identifier = $this->documentListing[$identifier];
2581
        }
2582
2583
        $out = $this->invokeEvent('OnBeforeLoadDocumentObject', compact('method', 'identifier'));
2584
        if (is_array($out) && is_array($out[0])) {
2585
            $documentObject = $out[0];
2586
        } else {
2587
            // get document groups for current user
2588
            if ($docgrp = $this->getUserDocGroups()) {
2589
                $docgrp = implode(",", $docgrp);
2590
            }
2591
            // get document
2592
            $access = ($this->isFrontend() ? "sc.privateweb=0" : "1='" . $_SESSION['mgrRole'] . "' OR sc.privatemgr=0") . (!$docgrp ? "" : " OR dg.document_group IN ($docgrp)");
2593
            $rs = $this->db->select('sc.*', "{$tblsc} sc
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2594
                LEFT JOIN {$tbldg} dg ON dg.document = sc.id", "sc.{$method} = '{$identifier}' AND ({$access})", "", 1);
2595
            if ($this->db->getRecordCount($rs) < 1) {
2596
                $seclimit = 0;
2597
                if ($this->config['unauthorized_page']) {
2598
                    // method may still be alias, while identifier is not full path alias, e.g. id not found above
2599
                    if ($method === 'alias') {
2600
                        $secrs = $this->db->select('count(dg.id)', "{$tbldg} as dg, {$tblsc} as sc", "dg.document = sc.id AND sc.alias = '{$identifier}'", '', 1);
2601
                    } else {
2602
                        $secrs = $this->db->select('count(id)', $tbldg, "document = '{$identifier}'", '', 1);
2603
                    }
2604
                    // check if file is not public
2605
                    $seclimit = $this->db->getValue($secrs);
2606
                }
2607
                if ($seclimit > 0) {
2608
                    // match found but not publicly accessible, send the visitor to the unauthorized_page
2609
                    $this->sendUnauthorizedPage();
2610
                    exit; // stop here
0 ignored issues
show
Coding Style Compatibility introduced by
The method getDocumentObject() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2611
                } else {
2612
                    $this->sendErrorPage();
2613
                    exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method getDocumentObject() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2614
                }
2615
            }
2616
            # this is now the document :) #
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
2617
            $documentObject = $this->db->getRow($rs);
2618
2619
            if ($isPrepareResponse === 'prepareResponse') {
2620
                $this->documentObject = &$documentObject;
2621
            }
2622
            $out = $this->invokeEvent('OnLoadDocumentObject', compact('method', 'identifier', 'documentObject'));
2623
            if (is_array($out) && is_array($out[0])) {
2624
                $documentObject = $out[0];
2625
            }
2626
            if ($documentObject['template']) {
2627
                // load TVs and merge with document - Orig by Apodigm - Docvars
2628
                $rs = $this->db->select("tv.*, IF(tvc.value!='',tvc.value,tv.default_text) as value", $this->getFullTableName("site_tmplvars") . " tv
2629
                INNER JOIN " . $this->getFullTableName("site_tmplvar_templates") . " tvtpl ON tvtpl.tmplvarid = tv.id
2630
                LEFT JOIN " . $this->getFullTableName("site_tmplvar_contentvalues") . " tvc ON tvc.tmplvarid=tv.id AND tvc.contentid = '{$documentObject['id']}'", "tvtpl.templateid = '{$documentObject['template']}'");
2631
                $tmplvars = array();
2632
                while ($row = $this->db->getRow($rs)) {
2633
                    $tmplvars[$row['name']] = array(
2634
                        $row['name'],
2635
                        $row['value'],
2636
                        $row['display'],
2637
                        $row['display_params'],
2638
                        $row['type']
2639
                    );
2640
                }
2641
                $documentObject = array_merge($documentObject, $tmplvars);
2642
            }
2643
            $out = $this->invokeEvent('OnAfterLoadDocumentObject', compact('method', 'identifier', 'documentObject'));
2644
            if (is_array($out) && array_key_exists(0, $out) !== false && is_array($out[0])) {
2645
                $documentObject = $out[0];
2646
            }
2647
        }
2648
2649
        $this->tmpCache[__FUNCTION__][$cacheKey] = $documentObject;
2650
2651
        return $documentObject;
2652
    }
2653
2654
    /**
2655
     * Parse a source string.
2656
     *
2657
     * Handles most MODX tags. Exceptions include:
2658
     *   - uncached snippet tags [!...!]
2659
     *   - URL tags [~...~]
2660
     *
2661
     * @param string $source
2662
     * @return string
2663
     */
2664
    public function parseDocumentSource($source)
2665
    {
2666
        // set the number of times we are to parse the document source
2667
        $this->minParserPasses = empty ($this->minParserPasses) ? 2 : $this->minParserPasses;
2668
        $this->maxParserPasses = empty ($this->maxParserPasses) ? 10 : $this->maxParserPasses;
2669
        $passes = $this->minParserPasses;
2670
        for ($i = 0; $i < $passes; $i++) {
2671
            // get source length if this is the final pass
2672
            if ($i == ($passes - 1)) {
2673
                $st = md5($source);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $st. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2674
            }
2675
            if ($this->dumpSnippets == 1) {
2676
                $this->snippetsCode .= "<fieldset><legend><b style='color: #821517;'>PARSE PASS " . ($i + 1) . "</b></legend><p>The following snippets (if any) were parsed during this pass.</p>";
2677
            }
2678
2679
            // invoke OnParseDocument event
2680
            $this->documentOutput = $source; // store source code so plugins can
2681
            $this->invokeEvent("OnParseDocument"); // work on it via $modx->documentOutput
2682
            $source = $this->documentOutput;
2683
2684
            if ($this->config['enable_at_syntax']) {
2685
                $source = $this->ignoreCommentedTagsContent($source);
2686
                $source = $this->mergeConditionalTagsContent($source);
2687
            }
2688
2689
            $source = $this->mergeSettingsContent($source);
2690
            $source = $this->mergeDocumentContent($source);
2691
            $source = $this->mergeChunkContent($source);
2692
            $source = $this->evalSnippets($source);
2693
            $source = $this->mergePlaceholderContent($source);
2694
2695
            if ($this->dumpSnippets == 1) {
2696
                $this->snippetsCode .= "</fieldset><br />";
2697
            }
2698
            if ($i == ($passes - 1) && $i < ($this->maxParserPasses - 1)) {
2699
                // check if source content was changed
2700
                if ($st != md5($source)) {
0 ignored issues
show
Bug introduced by
The variable $st 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...
2701
                    $passes++;
2702
                } // if content change then increase passes because
2703
            } // we have not yet reached maxParserPasses
2704
        }
2705
        return $source;
2706
    }
2707
2708
    /**
2709
     * Starts the parsing operations.
2710
     *
2711
     * - connects to the db
2712
     * - gets the settings (including system_settings)
2713
     * - gets the document/resource identifier as in the query string
2714
     * - finally calls prepareResponse()
2715
     */
2716
    public function executeParser()
2717
    {
2718
        if(MODX_CLI) {
2719
            throw new RuntimeException('Call DocumentParser::executeParser on CLI mode');
2720
        }
2721
2722
        //error_reporting(0);
2723
        set_error_handler(array(
2724
            & $this,
2725
            "phpError"
2726
        ), E_ALL);
2727
        $this->db->connect();
2728
2729
        // get the settings
2730
        if (empty ($this->config)) {
2731
            $this->getSettings();
2732
        }
2733
2734
        $this->_IIS_furl_fix(); // IIS friendly url fix
2735
2736
        // check site settings
2737
        if ($this->checkSiteStatus()) {
2738
            // make sure the cache doesn't need updating
2739
            $this->updatePubStatus();
2740
2741
            // find out which document we need to display
2742
            $this->documentMethod = filter_input(INPUT_GET, 'q') ? 'alias' : 'id';
2743
            $this->documentIdentifier = $this->getDocumentIdentifier($this->documentMethod);
2744
        } else {
2745
            header('HTTP/1.0 503 Service Unavailable');
2746
            $this->systemCacheKey = 'unavailable';
2747
            if (!$this->config['site_unavailable_page']) {
2748
                // display offline message
2749
                $this->documentContent = $this->config['site_unavailable_message'];
2750
                $this->outputContent();
2751
                exit; // stop processing here, as the site's offline
0 ignored issues
show
Coding Style Compatibility introduced by
The method executeParser() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2752
            } else {
2753
                // setup offline page document settings
2754
                $this->documentMethod = 'id';
2755
                $this->documentIdentifier = $this->config['site_unavailable_page'];
2756
            }
2757
        }
2758
2759
        if ($this->documentMethod == "alias") {
2760
            $this->documentIdentifier = $this->cleanDocumentIdentifier($this->documentIdentifier);
2761
2762
            // Check use_alias_path and check if $this->virtualDir is set to anything, then parse the path
2763
            if ($this->config['use_alias_path'] == 1) {
2764
                $alias = (strlen($this->virtualDir) > 0 ? $this->virtualDir . '/' : '') . $this->documentIdentifier;
2765
                if (isset($this->documentListing[$alias])) {
2766
                    $this->documentIdentifier = $this->documentListing[$alias];
2767
                } else {
2768
                    //@TODO: check new $alias;
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2769
                    if ($this->config['aliaslistingfolder'] == 1) {
2770
                        $tbl_site_content = $this->getFullTableName('site_content');
2771
2772
                        $parentId = $this->getIdFromAlias($this->virtualDir);
2773
                        $parentId = ($parentId > 0) ? $parentId : '0';
2774
2775
                        $docAlias = $this->db->escape($this->documentIdentifier);
2776
2777
                        $rs = $this->db->select('id', $tbl_site_content, "deleted=0 and parent='{$parentId}' and alias='{$docAlias}'");
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2778
                        if ($this->db->getRecordCount($rs) == 0) {
2779
                            $this->sendErrorPage();
2780
                        }
2781
                        $docId = $this->db->getValue($rs);
2782
2783
                        if (!$docId) {
2784
                            $alias = $this->q;
2785
                            if (!empty($this->config['friendly_url_suffix'])) {
2786
                                $pos = strrpos($alias, $this->config['friendly_url_suffix']);
2787
2788
                                if ($pos !== false) {
2789
                                    $alias = substr($alias, 0, $pos);
2790
                                }
2791
                            }
2792
                            $docId = $this->getIdFromAlias($alias);
2793
                        }
2794
2795
                        if ($docId > 0) {
2796
                            $this->documentIdentifier = $docId;
2797
                        } else {
2798
                            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2799
                            $rs  = $this->db->select('id', $tbl_site_content, "deleted=0 and alias='{$docAlias}'");
2800
                            if($this->db->getRecordCount($rs)==0)
2801
                            {
2802
                                $rs  = $this->db->select('id', $tbl_site_content, "deleted=0 and id='{$docAlias}'");
2803
                            }
2804
                            $docId = $this->db->getValue($rs);
2805
2806
                            if ($docId > 0)
2807
                            {
2808
                                $this->documentIdentifier = $docId;
2809
2810
                            }else{
2811
                            */
2812
                            $this->sendErrorPage();
2813
                            //}
2814
                        }
2815
                    } else {
2816
                        $this->sendErrorPage();
2817
                    }
2818
                }
2819
            } else {
2820
                if (isset($this->documentListing[$this->documentIdentifier])) {
2821
                    $this->documentIdentifier = $this->documentListing[$this->documentIdentifier];
2822
                } else {
2823
                    $docAlias = $this->db->escape($this->documentIdentifier);
2824
                    $rs = $this->db->select('id', $this->getFullTableName('site_content'), "deleted=0 and alias='{$docAlias}'");
2825
                    $this->documentIdentifier = (int)$this->db->getValue($rs);
2826
                }
2827
            }
2828
            $this->documentMethod = 'id';
2829
        }
2830
2831
        //$this->_fixURI();
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2832
        // invoke OnWebPageInit event
2833
        $this->invokeEvent("OnWebPageInit");
2834
        // invoke OnLogPageView event
2835
        if ($this->config['track_visitors'] == 1) {
2836
            $this->invokeEvent("OnLogPageHit");
2837
        }
2838
        if ($this->config['seostrict'] === '1') {
2839
            $this->sendStrictURI();
2840
        }
2841
        $this->prepareResponse();
2842
    }
2843
2844
    /**
2845
     * @param $path
2846
     * @param null $suffix
2847
     * @return mixed
2848
     */
2849
    public function mb_basename($path, $suffix = null)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
2850
    {
2851
        $exp = explode('/', $path);
2852
        return str_replace($suffix, '', end($exp));
2853
    }
2854
2855
    public function _IIS_furl_fix()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
Coding Style introduced by
_IIS_furl_fix uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
_IIS_furl_fix uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
_IIS_furl_fix uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
2856
    {
2857
        if ($this->config['friendly_urls'] != 1) {
2858
            return;
2859
        }
2860
2861
        if (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') === false) {
2862
            return;
2863
        }
2864
2865
        $url = $_SERVER['QUERY_STRING'];
2866
        $err = substr($url, 0, 3);
2867
        if ($err !== '404' && $err !== '405') {
2868
            return;
2869
        }
2870
2871
        $k = array_keys($_GET);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $k. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2872
        unset ($_GET[$k[0]]);
2873
        unset ($_REQUEST[$k[0]]); // remove 404,405 entry
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2874
        $qp = parse_url(str_replace($this->config['site_url'], '', substr($url, 4)));
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $qp. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2875
        $_SERVER['QUERY_STRING'] = $qp['query'];
2876
        if (!empty ($qp['query'])) {
2877
            parse_str($qp['query'], $qv);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $qv. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
2878
            foreach ($qv as $n => $v) {
0 ignored issues
show
Bug introduced by
The expression $qv of type null|array 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...
2879
                $_REQUEST[$n] = $_GET[$n] = $v;
2880
            }
2881
        }
2882
        $_SERVER['PHP_SELF'] = $this->config['base_url'] . $qp['path'];
2883
        $this->q = $qp['path'];
2884
        return $qp['path'];
2885
    }
2886
2887
    /**
2888
     * The next step called at the end of executeParser()
2889
     *
2890
     * - checks cache
2891
     * - checks if document/resource is deleted/unpublished
2892
     * - checks if resource is a weblink and redirects if so
2893
     * - gets template and parses it
2894
     * - ensures that postProcess is called when PHP is finished
2895
     */
2896
    public function prepareResponse()
2897
    {
2898
        // we now know the method and identifier, let's check the cache
2899
2900
        if ($this->config['enable_cache'] == 2 && $this->isLoggedIn()) {
2901
            $this->config['enable_cache'] = 0;
2902
        }
2903
2904
        if ($this->config['enable_cache']) {
2905
            $this->documentContent = $this->getDocumentObjectFromCache($this->documentIdentifier, true);
2906
        } else {
2907
            $this->documentContent = '';
2908
        }
2909
2910
        if ($this->documentContent == '') {
2911
            // get document object from DB
2912
            $this->documentObject = $this->getDocumentObject($this->documentMethod, $this->documentIdentifier, 'prepareResponse');
2913
2914
            // write the documentName to the object
2915
            $this->documentName = &$this->documentObject['pagetitle'];
2916
2917
            // check if we should not hit this document
2918
            if ($this->documentObject['donthit'] == 1) {
2919
                $this->config['track_visitors'] = 0;
2920
            }
2921
2922
            if ($this->documentObject['deleted'] == 1) {
2923
                $this->sendErrorPage();
2924
            } // validation routines
2925
            elseif ($this->documentObject['published'] == 0) {
2926
                $this->_sendErrorForUnpubPage();
2927
            } elseif ($this->documentObject['type'] == 'reference') {
2928
                $this->_sendRedirectForRefPage($this->documentObject['content']);
2929
            }
2930
2931
            // get the template and start parsing!
2932
            if (!$this->documentObject['template']) {
2933
                $templateCode = '[*content*]';
2934
            } // use blank template
2935
            else {
2936
                $templateCode = $this->_getTemplateCodeFromDB($this->documentObject['template']);
2937
            }
2938
2939
            if (substr($templateCode, 0, 8) === '@INCLUDE') {
2940
                $templateCode = $this->atBindInclude($templateCode);
2941
            }
2942
2943
2944
            $this->documentContent = &$templateCode;
2945
2946
            // invoke OnLoadWebDocument event
2947
            $this->invokeEvent('OnLoadWebDocument');
2948
2949
            // Parse document source
2950
            $this->documentContent = $this->parseDocumentSource($templateCode);
2951
2952
            $this->documentGenerated = 1;
2953
        } else {
2954
            $this->documentGenerated = 0;
2955
        }
2956
2957
        if ($this->config['error_page'] == $this->documentIdentifier && $this->config['error_page'] != $this->config['site_start']) {
2958
            header('HTTP/1.0 404 Not Found');
2959
        }
2960
2961
        register_shutdown_function(array(
2962
            &$this,
2963
            'postProcess'
2964
        )); // tell PHP to call postProcess when it shuts down
2965
        $this->outputContent();
2966
        //$this->postProcess();
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2967
    }
2968
2969
    public function _sendErrorForUnpubPage()
0 ignored issues
show
Coding Style introduced by
_sendErrorForUnpubPage uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
2970
    {
2971
        // Can't view unpublished pages !$this->checkPreview()
2972
        if (!$this->hasPermission('view_unpublished')) {
2973
            $this->sendErrorPage();
2974
        } else {
2975
            // Inculde the necessary files to check document permissions
2976
            include_once(MODX_MANAGER_PATH . 'processors/user_documents_permissions.class.php');
2977
            $udperms = new udperms();
2978
            $udperms->user = $this->getLoginUserID();
0 ignored issues
show
Documentation Bug introduced by
The property $user was declared of type integer, but $this->getLoginUserID() is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
2979
            $udperms->document = $this->documentIdentifier;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->documentIdentifier can also be of type string or boolean. However, the property $document is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2980
            $udperms->role = $_SESSION['mgrRole'];
2981
            // Doesn't have access to this document
2982
            if (!$udperms->checkPermissions()) {
2983
                $this->sendErrorPage();
2984
            }
2985
        }
2986
    }
2987
2988
    /**
2989
     * @param $url
2990
     */
2991
    public function _sendRedirectForRefPage($url)
2992
    {
2993
        // check whether it's a reference
2994
        if (preg_match('@^[1-9][0-9]*$@', $url)) {
2995
            $url = $this->makeUrl($url); // if it's a bare document id
2996
        } elseif (strpos($url, '[~') !== false) {
2997
            $url = $this->rewriteUrls($url); // if it's an internal docid tag, process it
2998
        }
2999
        $this->sendRedirect($url, 0, '', 'HTTP/1.0 301 Moved Permanently');
3000
        exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method _sendRedirectForRefPage() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3001
    }
3002
3003
    /**
3004
     * @param $templateID
3005
     * @return mixed
3006
     */
3007
    public function _getTemplateCodeFromDB($templateID)
3008
    {
3009
        $rs = $this->db->select('content', '[+prefix+]site_templates', "id = '{$templateID}'");
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
3010
        if ($this->db->getRecordCount($rs) == 1) {
3011
            return $this->db->getValue($rs);
3012
        } else {
3013
            $this->messageQuit('Incorrect number of templates returned from database');
3014
        }
3015
    }
3016
3017
    /**
3018
     * Returns an array of all parent record IDs for the id passed.
3019
     *
3020
     * @param int $id Docid to get parents for.
3021
     * @param int $height The maximum number of levels to go up, default 10.
3022
     * @return array
3023
     */
3024
    public function getParentIds($id, $height = 10)
3025
    {
3026
        $parents = array();
3027
        while ($id && $height--) {
3028
            $thisid = $id;
3029
            if ($this->config['aliaslistingfolder'] == 1) {
3030
                $id = isset($this->aliasListing[$id]['parent']) ? $this->aliasListing[$id]['parent'] : $this->db->getValue("SELECT `parent` FROM " . $this->getFullTableName("site_content") . " WHERE `id` = '{$id}' LIMIT 0,1");
3031
                if (!$id || $id == '0') {
3032
                    break;
3033
                }
3034
            } else {
3035
                $id = $this->aliasListing[$id]['parent'];
3036
                if (!$id) {
3037
                    break;
3038
                }
3039
            }
3040
            $parents[$thisid] = $id;
3041
        }
3042
        return $parents;
3043
    }
3044
3045
    /**
3046
     * @param $id
3047
     * @param int $top
3048
     * @return mixed
3049
     */
3050
    public function getUltimateParentId($id, $top = 0)
3051
    {
3052
        $i = 0;
3053
        while ($id && $i < 20) {
3054
            if ($top == $this->aliasListing[$id]['parent']) {
3055
                break;
3056
            }
3057
            $id = $this->aliasListing[$id]['parent'];
3058
            $i++;
3059
        }
3060
        return $id;
3061
    }
3062
3063
    /**
3064
     * Returns an array of child IDs belonging to the specified parent.
3065
     *
3066
     * @param int $id The parent resource/document to start from
3067
     * @param int $depth How many levels deep to search for children, default: 10
3068
     * @param array $children Optional array of docids to merge with the result.
3069
     * @return array Contains the document Listing (tree) like the sitemap
3070
     */
3071
    public function getChildIds($id, $depth = 10, $children = array())
3072
    {
3073
3074
        $cacheKey = md5(print_r(func_get_args(), true));
3075
        if (isset($this->tmpCache[__FUNCTION__][$cacheKey])) {
3076
            return $this->tmpCache[__FUNCTION__][$cacheKey];
3077
        }
3078
3079
        if ($this->config['aliaslistingfolder'] == 1) {
3080
3081
            $res = $this->db->select("id,alias,isfolder,parent", $this->getFullTableName('site_content'), "parent IN (" . $id . ") AND deleted = '0'");
3082
            $idx = array();
3083
            while ($row = $this->db->getRow($res)) {
3084
                $pAlias = '';
3085
                if (isset($this->aliasListing[$row['parent']])) {
3086
                    $pAlias .= !empty($this->aliasListing[$row['parent']]['path']) ? $this->aliasListing[$row['parent']]['path'] . '/' : '';
3087
                    $pAlias .= !empty($this->aliasListing[$row['parent']]['alias']) ? $this->aliasListing[$row['parent']]['alias'] . '/' : '';
3088
                };
3089
                $children[$pAlias . $row['alias']] = $row['id'];
3090
                if ($row['isfolder'] == 1) {
3091
                    $idx[] = $row['id'];
3092
                }
3093
            }
3094
            $depth--;
3095
            $idx = implode(',', $idx);
3096
            if (!empty($idx)) {
3097
                if ($depth) {
3098
                    $children = $this->getChildIds($idx, $depth, $children);
3099
                }
3100
            }
3101
            $this->tmpCache[__FUNCTION__][$cacheKey] = $children;
3102
            return $children;
3103
3104
        } else {
3105
3106
            // Initialise a static array to index parents->children
3107
            static $documentMap_cache = array();
3108
            if (!count($documentMap_cache)) {
3109
                foreach ($this->documentMap as $document) {
3110
                    foreach ($document as $p => $c) {
3111
                        $documentMap_cache[$p][] = $c;
3112
                    }
3113
                }
3114
            }
3115
3116
            // Get all the children for this parent node
3117
            if (isset($documentMap_cache[$id])) {
3118
                $depth--;
3119
3120
                foreach ($documentMap_cache[$id] as $childId) {
3121
                    $pkey = (strlen($this->aliasListing[$childId]['path']) ? "{$this->aliasListing[$childId]['path']}/" : '') . $this->aliasListing[$childId]['alias'];
3122
                    if (!strlen($pkey)) {
3123
                        $pkey = "{$childId}";
3124
                    }
3125
                    $children[$pkey] = $childId;
3126
3127
                    if ($depth && isset($documentMap_cache[$childId])) {
3128
                        $children += $this->getChildIds($childId, $depth);
3129
                    }
3130
                }
3131
            }
3132
            $this->tmpCache[__FUNCTION__][$cacheKey] = $children;
3133
            return $children;
3134
3135
        }
3136
    }
3137
3138
    /**
3139
     * Displays a javascript alert message in the web browser and quit
3140
     *
3141
     * @param string $msg Message to show
3142
     * @param string $url URL to redirect to
3143
     */
3144
    public function webAlertAndQuit($msg, $url = "")
3145
    {
3146
        global $modx_manager_charset;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
3147
        if (substr(strtolower($url), 0, 11) == "javascript:") {
3148
            $fnc = substr($url, 11);
3149
        } elseif ($url) {
3150
            $fnc = "window.location.href='" . addslashes($url) . "';";
3151
        } else {
3152
            $fnc = "history.back(-1);";
3153
        }
3154
        echo "<html><head>
3155
            <title>MODX :: Alert</title>
3156
            <meta http-equiv=\"Content-Type\" content=\"text/html; charset={$modx_manager_charset};\">
3157
            <script>
3158
                function __alertQuit() {
3159
                    alert('" . addslashes($msg) . "');
3160
                    {$fnc}
3161
                }
3162
                window.setTimeout('__alertQuit();',100);
3163
            </script>
3164
            </head><body>
3165
            <p>{$msg}</p>
3166
            </body></html>";
3167
        exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method webAlertAndQuit() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
3168
    }
3169
3170
    /**
3171
     * Returns 1 if user has the currect permission
3172
     *
3173
     * @param string $pm Permission name
3174
     * @return int Why not bool?
3175
     */
3176
    public function hasPermission($pm)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $pm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Coding Style introduced by
hasPermission uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3177
    {
3178
        $state = 0;
3179
        $pms = $_SESSION['mgrPermissions'];
3180
        if ($pms) {
3181
            $state = ((bool)$pms[$pm] === true);
3182
        }
3183
        return (int)$state;
3184
    }
3185
3186
    /**
3187
     * Returns true if element is locked
3188
     *
3189
     * @param int $type Types: 1=template, 2=tv, 3=chunk, 4=snippet, 5=plugin, 6=module, 7=resource, 8=role
3190
     * @param int $id Element- / Resource-id
3191
     * @param bool $includeThisUser true = Return also info about actual user
3192
     * @return array lock-details or null
3193
     */
3194
    public function elementIsLocked($type, $id, $includeThisUser = false)
3195
    {
3196
        $id = (int)$id;
3197
        $type = (int)$type;
3198
        if (!$type || !$id) {
3199
            return null;
3200
        }
3201
3202
        // Build lockedElements-Cache at first call
3203
        $this->buildLockedElementsCache();
3204
3205
        if (!$includeThisUser && $this->lockedElements[$type][$id]['sid'] == $this->sid) {
3206
            return null;
3207
        }
3208
3209
        if (isset($this->lockedElements[$type][$id])) {
3210
            return $this->lockedElements[$type][$id];
3211
        } else {
3212
            return null;
3213
        }
3214
    }
3215
3216
    /**
3217
     * Returns Locked Elements as Array
3218
     *
3219
     * @param int $type Types: 0=all, 1=template, 2=tv, 3=chunk, 4=snippet, 5=plugin, 6=module, 7=resource, 8=role
3220
     * @param bool $minimumDetails true =
3221
     * @return array|mixed|null
3222
     */
3223
    public function getLockedElements($type = 0, $minimumDetails = false)
3224
    {
3225
        $this->buildLockedElementsCache();
3226
3227
        if (!$minimumDetails) {
3228
            $lockedElements = $this->lockedElements;
3229
        } else {
3230
            // Minimum details for HTML / Ajax-requests
3231
            $lockedElements = array();
3232
            foreach ($this->lockedElements as $elType => $elements) {
3233
                foreach ($elements as $elId => $el) {
3234
                    $lockedElements[$elType][$elId] = array(
3235
                        'username' => $el['username'],
3236
                        'lasthit_df' => $el['lasthit_df'],
3237
                        'state' => $this->determineLockState($el['internalKey'])
3238
                    );
3239
                }
3240
            }
3241
        }
3242
3243
        if ($type == 0) {
3244
            return $lockedElements;
3245
        }
3246
3247
        $type = (int)$type;
3248
        if (isset($lockedElements[$type])) {
3249
            return $lockedElements[$type];
3250
        } else {
3251
            return array();
3252
        }
3253
    }
3254
3255
    /**
3256
     * Builds the Locked Elements Cache once
3257
     */
3258
    public function buildLockedElementsCache()
3259
    {
3260
        if (is_null($this->lockedElements)) {
3261
            $this->lockedElements = array();
3262
            $this->cleanupExpiredLocks();
3263
3264
            $rs = $this->db->select('sid,internalKey,elementType,elementId,lasthit,username', $this->getFullTableName('active_user_locks') . " ul
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
3265
                LEFT JOIN {$this->getFullTableName('manager_users')} mu on ul.internalKey = mu.id");
3266
            while ($row = $this->db->getRow($rs)) {
3267
                $this->lockedElements[$row['elementType']][$row['elementId']] = array(
3268
                    'sid' => $row['sid'],
3269
                    'internalKey' => $row['internalKey'],
3270
                    'username' => $row['username'],
3271
                    'elementType' => $row['elementType'],
3272
                    'elementId' => $row['elementId'],
3273
                    'lasthit' => $row['lasthit'],
3274
                    'lasthit_df' => $this->toDateFormat($row['lasthit']),
3275
                    'state' => $this->determineLockState($row['sid'])
3276
                );
3277
            }
3278
        }
3279
    }
3280
3281
    /**
3282
     * Cleans up the active user locks table
3283
     */
3284
    public function cleanupExpiredLocks()
3285
    {
3286
        // Clean-up active_user_sessions first
3287
        $timeout = (int)$this->config['session_timeout'] < 2 ? 120 : $this->config['session_timeout'] * 60; // session.js pings every 10min, updateMail() in mainMenu pings every minute, so 2min is minimum
3288
        $validSessionTimeLimit = $this->time - $timeout;
3289
        $this->db->delete($this->getFullTableName('active_user_sessions'), "lasthit < {$validSessionTimeLimit}");
3290
3291
        // Clean-up active_user_locks
3292
        $rs = $this->db->select('sid,internalKey', $this->getFullTableName('active_user_sessions'));
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
3293
        $count = $this->db->getRecordCount($rs);
3294
        if ($count) {
3295
            $rs = $this->db->makeArray($rs);
3296
            $userSids = array();
3297
            foreach ($rs as $row) {
0 ignored issues
show
Bug introduced by
The expression $rs of type false|array 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...
3298
                $userSids[] = $row['sid'];
3299
            }
3300
            $userSids = "'" . implode("','", $userSids) . "'";
3301
            $this->db->delete($this->getFullTableName('active_user_locks'), "sid NOT IN({$userSids})");
3302
        } else {
3303
            $this->db->delete($this->getFullTableName('active_user_locks'));
3304
        }
3305
3306
    }
3307
3308
    /**
3309
     * Cleans up the active users table
3310
     */
3311
    public function cleanupMultipleActiveUsers()
3312
    {
3313
        $timeout = 20 * 60; // Delete multiple user-sessions after 20min
3314
        $validSessionTimeLimit = $this->time - $timeout;
3315
3316
        $activeUserSids = array();
3317
        $rs = $this->db->select('sid', $this->getFullTableName('active_user_sessions'));
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
3318
        $count = $this->db->getRecordCount($rs);
3319
        if ($count) {
3320
            $rs = $this->db->makeArray($rs);
3321
            foreach ($rs as $row) {
0 ignored issues
show
Bug introduced by
The expression $rs of type false|array 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...
3322
                $activeUserSids[] = $row['sid'];
3323
            }
3324
        }
3325
3326
        $rs = $this->db->select("sid,internalKey,lasthit", "{$this->getFullTableName('active_users')}", "", "lasthit DESC");
3327
        if ($this->db->getRecordCount($rs)) {
3328
            $rs = $this->db->makeArray($rs);
3329
            $internalKeyCount = array();
3330
            $deleteSids = '';
3331
            foreach ($rs as $row) {
0 ignored issues
show
Bug introduced by
The expression $rs of type false|array 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...
3332
                if (!isset($internalKeyCount[$row['internalKey']])) {
3333
                    $internalKeyCount[$row['internalKey']] = 0;
3334
                }
3335
                $internalKeyCount[$row['internalKey']]++;
3336
3337
                if ($internalKeyCount[$row['internalKey']] > 1 && !in_array($row['sid'], $activeUserSids) && $row['lasthit'] < $validSessionTimeLimit) {
3338
                    $deleteSids .= $deleteSids == '' ? '' : ' OR ';
3339
                    $deleteSids .= "sid='{$row['sid']}'";
3340
                };
3341
3342
            }
3343
            if ($deleteSids) {
3344
                $this->db->delete($this->getFullTableName('active_users'), $deleteSids);
3345
            }
3346
        }
3347
3348
    }
3349
3350
    /**
3351
     * Determines state of a locked element acc. to user-permissions
3352
     *
3353
     * @param $sid
3354
     * @return int $state States: 0=No display, 1=viewing this element, 2=locked, 3=show unlock-button
3355
     * @internal param int $internalKey : ID of User who locked actual element
3356
     */
3357
    public function determineLockState($sid)
3358
    {
3359
        $state = 0;
3360
        if ($this->hasPermission('display_locks')) {
3361
            if ($sid == $this->sid) {
3362
                $state = 1;
3363
            } else {
3364
                if ($this->hasPermission('remove_locks')) {
3365
                    $state = 3;
3366
                } else {
3367
                    $state = 2;
3368
                }
3369
            }
3370
        }
3371
        return $state;
3372
    }
3373
3374
    /**
3375
     * Locks an element
3376
     *
3377
     * @param int $type Types: 1=template, 2=tv, 3=chunk, 4=snippet, 5=plugin, 6=module, 7=resource, 8=role
3378
     * @param int $id Element- / Resource-id
3379
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be false|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
3380
     */
3381
    public function lockElement($type, $id)
0 ignored issues
show
Coding Style introduced by
lockElement uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3382
    {
3383
        $userId = $this->isBackend() && $_SESSION['mgrInternalKey'] ? $_SESSION['mgrInternalKey'] : 0;
3384
        $type = (int)$type;
3385
        $id = (int)$id;
3386
        if (!$type || !$id || !$userId) {
3387
            return false;
3388
        }
3389
3390
        $sql = sprintf('REPLACE INTO %s (internalKey, elementType, elementId, lasthit, sid)
3391
                VALUES (%d, %d, %d, %d, \'%s\')', $this->getFullTableName('active_user_locks'), $userId, $type, $id, $this->time, $this->sid);
3392
        $this->db->query($sql);
3393
    }
3394
3395
    /**
3396
     * Unlocks an element
3397
     *
3398
     * @param int $type Types: 1=template, 2=tv, 3=chunk, 4=snippet, 5=plugin, 6=module, 7=resource, 8=role
3399
     * @param int $id Element- / Resource-id
3400
     * @param bool $includeAllUsers true = Deletes not only own user-locks
3401
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be false|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
3402
     */
3403
    public function unlockElement($type, $id, $includeAllUsers = false)
0 ignored issues
show
Coding Style introduced by
unlockElement uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3404
    {
3405
        $userId = $this->isBackend() && $_SESSION['mgrInternalKey'] ? $_SESSION['mgrInternalKey'] : 0;
3406
        $type = (int)$type;
3407
        $id = (int)$id;
3408
        if (!$type || !$id) {
3409
            return false;
3410
        }
3411
3412
        if (!$includeAllUsers) {
3413
            $sql = sprintf('DELETE FROM %s WHERE internalKey = %d AND elementType = %d AND elementId = %d;', $this->getFullTableName('active_user_locks'), $userId, $type, $id);
3414
        } else {
3415
            $sql = sprintf('DELETE FROM %s WHERE elementType = %d AND elementId = %d;', $this->getFullTableName('active_user_locks'), $type, $id);
3416
        }
3417
        $this->db->query($sql);
3418
    }
3419
3420
    /**
3421
     * Updates table "active_user_sessions" with userid, lasthit, IP
3422
     */
3423
    public function updateValidatedUserSession()
0 ignored issues
show
Coding Style introduced by
updateValidatedUserSession uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3424
    {
3425
        if (!$this->sid) {
3426
            return;
3427
        }
3428
3429
        // web users are stored with negative keys
3430
        $userId = $this->getLoginUserType() == 'manager' ? $this->getLoginUserID() : -$this->getLoginUserID();
3431
3432
        // Get user IP
3433
        if ($cip = getenv("HTTP_CLIENT_IP")) {
3434
            $ip = $cip;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ip. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
3435
        } elseif ($cip = getenv("HTTP_X_FORWARDED_FOR")) {
3436
            $ip = $cip;
3437
        } elseif ($cip = getenv("REMOTE_ADDR")) {
3438
            $ip = $cip;
3439
        } else {
3440
            $ip = "UNKNOWN";
3441
        }
3442
        $_SESSION['ip'] = $ip;
3443
3444
        $sql = sprintf('REPLACE INTO %s (internalKey, lasthit, ip, sid)
3445
            VALUES (%d, %d, \'%s\', \'%s\')', $this->getFullTableName('active_user_sessions'), $userId, $this->time, $ip, $this->sid);
3446
        $this->db->query($sql);
3447
    }
3448
3449
    /**
3450
     * Add an a alert message to the system event log
3451
     *
3452
     * @param int $evtid Event ID
3453
     * @param int $type Types: 1 = information, 2 = warning, 3 = error
3454
     * @param string $msg Message to be logged
3455
     * @param string $source source of the event (module, snippet name, etc.)
3456
     *                       Default: Parser
3457
     */
3458
    public function logEvent($evtid, $type, $msg, $source = 'Parser')
0 ignored issues
show
Coding Style introduced by
logEvent uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
logEvent uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3459
    {
3460
        $msg = $this->db->escape($msg);
3461
        if (strpos($GLOBALS['database_connection_charset'], 'utf8') === 0 && extension_loaded('mbstring')) {
3462
            $esc_source = mb_substr($source, 0, 50, "UTF-8");
3463
        } else {
3464
            $esc_source = substr($source, 0, 50);
3465
        }
3466
        $esc_source = $this->db->escape($esc_source);
3467
3468
        $LoginUserID = $this->getLoginUserID();
3469
        if ($LoginUserID == '') {
3470
            $LoginUserID = 0;
3471
        }
3472
3473
        $usertype = $this->isFrontend() ? 1 : 0;
3474
        $evtid = (int)$evtid;
3475
        $type = (int)$type;
3476
3477
        // Types: 1 = information, 2 = warning, 3 = error
3478
        if ($type < 1) {
3479
            $type = 1;
3480
        } elseif ($type > 3) {
3481
            $type = 3;
3482
        }
3483
3484
        $this->db->insert(array(
3485
            'eventid' => $evtid,
3486
            'type' => $type,
3487
            'createdon' => $_SERVER['REQUEST_TIME'] + $this->config['server_offset_time'],
3488
            'source' => $esc_source,
3489
            'description' => $msg,
3490
            'user' => $LoginUserID,
3491
            'usertype' => $usertype
3492
        ), $this->getFullTableName("event_log"));
3493
3494
        if (isset($this->config['send_errormail']) && $this->config['send_errormail'] !== '0') {
3495
            if ($this->config['send_errormail'] <= $type) {
3496
                $this->sendmail(array(
3497
                    'subject' => 'MODX System Error on ' . $this->config['site_name'],
3498
                    'body' => 'Source: ' . $source . ' - The details of the error could be seen in the MODX system events log.',
3499
                    'type' => 'text'
3500
                ));
3501
            }
3502
        }
3503
    }
3504
3505
    /**
3506
     * @param array $params
3507
     * @param string $msg
3508
     * @param array $files
3509
     * @return mixed
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use boolean.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
3510
     */
3511
    public function sendmail($params = array(), $msg = '', $files = array())
0 ignored issues
show
Coding Style introduced by
sendmail uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3512
    {
3513
        if (isset($params) && is_string($params)) {
3514
            if (strpos($params, '=') === false) {
3515
                if (strpos($params, '@') !== false) {
3516
                    $p['to'] = $params;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$p was never initialized. Although not strictly required by PHP, it is generally a good practice to add $p = 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...
Comprehensibility introduced by
Avoid variables with short names like $p. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
3517
                } else {
3518
                    $p['subject'] = $params;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$p was never initialized. Although not strictly required by PHP, it is generally a good practice to add $p = 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...
3519
                }
3520
            } else {
3521
                $params_array = explode(',', $params);
3522
                foreach ($params_array as $k => $v) {
3523
                    $k = trim($k);
3524
                    $v = trim($v);
3525
                    $p[$k] = $v;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$p was never initialized. Although not strictly required by PHP, it is generally a good practice to add $p = 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...
3526
                }
3527
            }
3528
        } else {
3529
            $p = $params;
3530
            unset($params);
3531
        }
3532
        if (isset($p['sendto'])) {
3533
            $p['to'] = $p['sendto'];
3534
        }
3535
3536
        if (isset($p['to']) && preg_match('@^[0-9]+$@', $p['to'])) {
3537
            $userinfo = $this->getUserInfo($p['to']);
3538
            $p['to'] = $userinfo['email'];
3539
        }
3540
        if (isset($p['from']) && preg_match('@^[0-9]+$@', $p['from'])) {
3541
            $userinfo = $this->getUserInfo($p['from']);
3542
            $p['from'] = $userinfo['email'];
3543
            $p['fromname'] = $userinfo['username'];
3544
        }
3545
        if ($msg === '' && !isset($p['body'])) {
3546
            $p['body'] = $_SERVER['REQUEST_URI'] . "\n" . $_SERVER['HTTP_USER_AGENT'] . "\n" . $_SERVER['HTTP_REFERER'];
0 ignored issues
show
Bug introduced by
The variable $p 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...
3547
        } elseif (is_string($msg) && 0 < strlen($msg)) {
3548
            $p['body'] = $msg;
3549
        }
3550
3551
        $this->loadExtension('MODxMailer');
3552
        $sendto = (!isset($p['to'])) ? $this->config['emailsender'] : $p['to'];
3553
        $sendto = explode(',', $sendto);
3554
        foreach ($sendto as $address) {
3555
            list($name, $address) = $this->mail->address_split($address);
3556
            $this->mail->AddAddress($address, $name);
3557
        }
3558 View Code Duplication
        if (isset($p['cc'])) {
3559
            $p['cc'] = explode(',', $p['cc']);
3560
            foreach ($p['cc'] as $address) {
3561
                list($name, $address) = $this->mail->address_split($address);
3562
                $this->mail->AddCC($address, $name);
3563
            }
3564
        }
3565 View Code Duplication
        if (isset($p['bcc'])) {
3566
            $p['bcc'] = explode(',', $p['bcc']);
3567
            foreach ($p['bcc'] as $address) {
3568
                list($name, $address) = $this->mail->address_split($address);
3569
                $this->mail->AddBCC($address, $name);
3570
            }
3571
        }
3572
        if (isset($p['from']) && strpos($p['from'], '<') !== false && substr($p['from'], -1) === '>') {
3573
            list($p['fromname'], $p['from']) = $this->mail->address_split($p['from']);
3574
        }
3575
        $this->mail->From = (!isset($p['from'])) ? $this->config['emailsender'] : $p['from'];
3576
        $this->mail->FromName = (!isset($p['fromname'])) ? $this->config['site_name'] : $p['fromname'];
3577
        $this->mail->Subject = (!isset($p['subject'])) ? $this->config['emailsubject'] : $p['subject'];
3578
        $this->mail->Body = $p['body'];
3579
        if (isset($p['type']) && $p['type'] == 'text') {
3580
            $this->mail->IsHTML(false);
3581
        }
3582
        if (!is_array($files)) {
3583
            $files = array();
3584
        }
3585
        foreach ($files as $f) {
3586
            if (file_exists(MODX_BASE_PATH . $f) && is_file(MODX_BASE_PATH . $f) && is_readable(MODX_BASE_PATH . $f)) {
3587
                $this->mail->AddAttachment(MODX_BASE_PATH . $f);
3588
            }
3589
        }
3590
        $rs = $this->mail->send();
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
3591
        return $rs;
3592
    }
3593
3594
    /**
3595
     * @param string $target
3596
     * @param int $limit
3597
     * @param int $trim
3598
     */
3599
    public function rotate_log($target = 'event_log', $limit = 3000, $trim = 100)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
3600
    {
3601
        if ($limit < $trim) {
3602
            $trim = $limit;
3603
        }
3604
3605
        $table_name = $this->getFullTableName($target);
3606
        $count = $this->db->getValue($this->db->select('COUNT(id)', $table_name));
3607
        $over = $count - $limit;
3608
        if (0 < $over) {
3609
            $trim = ($over + $trim);
3610
            $this->db->delete($table_name, '', '', $trim);
3611
        }
3612
        $this->db->optimize($table_name);
3613
    }
3614
3615
    /**
3616
     * Returns true if we are currently in the manager backend
3617
     *
3618
     * @return boolean
3619
     */
3620
    public function isBackend()
3621
    {
3622
        return (defined('IN_MANAGER_MODE') && IN_MANAGER_MODE === true);
3623
    }
3624
3625
    /**
3626
     * Returns true if we are currently in the frontend
3627
     *
3628
     * @return boolean
3629
     */
3630
    public function isFrontend()
3631
    {
3632
        return ! $this->isBackend();
3633
    }
3634
3635
    /**
3636
     * Gets all child documents of the specified document, including those which are unpublished or deleted.
3637
     *
3638
     * @param int $id The Document identifier to start with
3639
     * @param string $sort Sort field
3640
     *                     Default: menuindex
3641
     * @param string $dir Sort direction, ASC and DESC is possible
3642
     *                    Default: ASC
3643
     * @param string $fields Default: id, pagetitle, description, parent, alias, menutitle
3644
     * @return array
3645
     */
3646 View Code Duplication
    public function getAllChildren($id = 0, $sort = 'menuindex', $dir = 'ASC', $fields = 'id, pagetitle, description, parent, alias, menutitle')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
getAllChildren uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3647
    {
3648
3649
        $cacheKey = md5(print_r(func_get_args(), true));
3650
        if (isset($this->tmpCache[__FUNCTION__][$cacheKey])) {
3651
            return $this->tmpCache[__FUNCTION__][$cacheKey];
3652
        }
3653
3654
        $tblsc = $this->getFullTableName("site_content");
3655
        $tbldg = $this->getFullTableName("document_groups");
3656
        // modify field names to use sc. table reference
3657
        $fields = 'sc.' . implode(',sc.', array_filter(array_map('trim', explode(',', $fields))));
3658
        $sort = 'sc.' . implode(',sc.', array_filter(array_map('trim', explode(',', $sort))));
3659
        // get document groups for current user
3660
        if ($docgrp = $this->getUserDocGroups()) {
3661
            $docgrp = implode(",", $docgrp);
3662
        }
3663
        // build query
3664
        $access = ($this->isFrontend() ? "sc.privateweb=0" : "1='" . $_SESSION['mgrRole'] . "' OR sc.privatemgr=0") . (!$docgrp ? "" : " OR dg.document_group IN ($docgrp)");
3665
        $result = $this->db->select("DISTINCT {$fields}", "{$tblsc} sc
3666
                LEFT JOIN {$tbldg} dg on dg.document = sc.id", "sc.parent = '{$id}' AND ({$access}) GROUP BY sc.id", "{$sort} {$dir}");
3667
        $resourceArray = $this->db->makeArray($result);
3668
        $this->tmpCache[__FUNCTION__][$cacheKey] = $resourceArray;
3669
        return $resourceArray;
3670
    }
3671
3672
    /**
3673
     * Gets all active child documents of the specified document, i.e. those which published and not deleted.
3674
     *
3675
     * @param int $id The Document identifier to start with
3676
     * @param string $sort Sort field
3677
     *                     Default: menuindex
3678
     * @param string $dir Sort direction, ASC and DESC is possible
3679
     *                    Default: ASC
3680
     * @param string $fields Default: id, pagetitle, description, parent, alias, menutitle
3681
     * @return array
3682
     */
3683 View Code Duplication
    public function getActiveChildren($id = 0, $sort = 'menuindex', $dir = 'ASC', $fields = 'id, pagetitle, description, parent, alias, menutitle')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
getActiveChildren uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3684
    {
3685
        $cacheKey = md5(print_r(func_get_args(), true));
3686
        if (isset($this->tmpCache[__FUNCTION__][$cacheKey])) {
3687
            return $this->tmpCache[__FUNCTION__][$cacheKey];
3688
        }
3689
3690
        $tblsc = $this->getFullTableName("site_content");
3691
        $tbldg = $this->getFullTableName("document_groups");
3692
3693
        // modify field names to use sc. table reference
3694
        $fields = 'sc.' . implode(',sc.', array_filter(array_map('trim', explode(',', $fields))));
3695
        $sort = 'sc.' . implode(',sc.', array_filter(array_map('trim', explode(',', $sort))));
3696
        // get document groups for current user
3697
        if ($docgrp = $this->getUserDocGroups()) {
3698
            $docgrp = implode(",", $docgrp);
3699
        }
3700
        // build query
3701
        $access = ($this->isFrontend() ? "sc.privateweb=0" : "1='" . $_SESSION['mgrRole'] . "' OR sc.privatemgr=0") . (!$docgrp ? "" : " OR dg.document_group IN ($docgrp)");
3702
        $result = $this->db->select("DISTINCT {$fields}", "{$tblsc} sc
3703
                LEFT JOIN {$tbldg} dg on dg.document = sc.id", "sc.parent = '{$id}' AND sc.published=1 AND sc.deleted=0 AND ({$access}) GROUP BY sc.id", "{$sort} {$dir}");
3704
        $resourceArray = $this->db->makeArray($result);
3705
3706
        $this->tmpCache[__FUNCTION__][$cacheKey] = $resourceArray;
3707
3708
        return $resourceArray;
3709
    }
3710
3711
    /**
3712
     * getDocumentChildren
3713
     * @version 1.1.1 (2014-02-19)
3714
     *
3715
     * @desc Returns the children of the selected document/folder as an associative array.
3716
     *
3717
     * @param $parentid {integer} - The parent document identifier. Default: 0 (site root).
3718
     * @param $published {0; 1; 'all'} - Document publication status. Once the parameter equals 'all', the result will be returned regardless of whether the ducuments are published or they are not. Default: 1.
3719
     * @param $deleted {0; 1; 'all'} - Document removal status. Once the parameter equals 'all', the result will be returned regardless of whether the ducuments are deleted or they are not. Default: 0.
3720
     * @param $fields {comma separated string; '*'} - Comma separated list of document fields to get. Default: '*' (all fields).
3721
     * @param $where {string} - Where condition in SQL style. Should include a leading 'AND '. Default: ''.
3722
     * @param $sort {comma separated string} - Should be a comma-separated list of field names on which to sort. Default: 'menuindex'.
3723
     * @param $dir {'ASC'; 'DESC'} - Sort direction, ASC and DESC is possible. Default: 'ASC'.
3724
     * @param $limit {string} - Should be a valid SQL LIMIT clause without the 'LIMIT ' i.e. just include the numbers as a string. Default: Empty string (no limit).
3725
     *
3726
     * @return {array; false} - Result array, or false.
0 ignored issues
show
Documentation introduced by
The doc-type {array; could not be parsed: Unknown type name "{array" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
3727
     */
3728
    public function getDocumentChildren($parentid = 0, $published = 1, $deleted = 0, $fields = '*', $where = '', $sort = 'menuindex', $dir = 'ASC', $limit = '')
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
Coding Style introduced by
getDocumentChildren uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3729
    {
3730
3731
        $cacheKey = md5(print_r(func_get_args(), true));
3732
        if (isset($this->tmpCache[__FUNCTION__][$cacheKey])) {
3733
            return $this->tmpCache[__FUNCTION__][$cacheKey];
3734
        }
3735
3736
        $published = ($published !== 'all') ? 'AND sc.published = ' . $published : '';
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $published (integer) and 'all' (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
3737
        $deleted = ($deleted !== 'all') ? 'AND sc.deleted = ' . $deleted : '';
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $deleted (integer) and 'all' (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
3738
3739
        if ($where != '') {
3740
            $where = 'AND ' . $where;
3741
        }
3742
3743
        // modify field names to use sc. table reference
3744
        $fields = 'sc.' . implode(',sc.', array_filter(array_map('trim', explode(',', $fields))));
3745
        $sort = ($sort == '') ? '' : 'sc.' . implode(',sc.', array_filter(array_map('trim', explode(',', $sort))));
3746
3747
        // get document groups for current user
3748
        if ($docgrp = $this->getUserDocGroups()) {
3749
            $docgrp = implode(',', $docgrp);
3750
        }
3751
3752
        // build query
3753
        $access = ($this->isFrontend() ? 'sc.privateweb=0' : '1="' . $_SESSION['mgrRole'] . '" OR sc.privatemgr=0') . (!$docgrp ? '' : ' OR dg.document_group IN (' . $docgrp . ')');
3754
3755
        $tblsc = $this->getFullTableName('site_content');
3756
        $tbldg = $this->getFullTableName('document_groups');
3757
3758
        $result = $this->db->select("DISTINCT {$fields}", "{$tblsc} sc
3759
                LEFT JOIN {$tbldg} dg on dg.document = sc.id", "sc.parent = '{$parentid}' {$published} {$deleted} {$where} AND ({$access}) GROUP BY sc.id", ($sort ? "{$sort} {$dir}" : ""), $limit);
3760
3761
        $resourceArray = $this->db->makeArray($result);
3762
3763
        $this->tmpCache[__FUNCTION__][$cacheKey] = $resourceArray;
3764
3765
        return $resourceArray;
3766
    }
3767
3768
    /**
3769
     * getDocuments
3770
     * @version 1.1.1 (2013-02-19)
3771
     *
3772
     * @desc Returns required documents (their fields).
3773
     *
3774
     * @param $ids {array; comma separated string} - Documents Ids to get. @required
3775
     * @param $published {0; 1; 'all'} - Documents publication status. Once the parameter equals 'all', the result will be returned regardless of whether the documents are published or they are not. Default: 1.
3776
     * @param $deleted {0; 1; 'all'} - Documents removal status. Once the parameter equals 'all', the result will be returned regardless of whether the documents are deleted or they are not. Default: 0.
3777
     * @param $fields {comma separated string; '*'} - Documents fields to get. Default: '*'.
3778
     * @param $where {string} - SQL WHERE clause. Default: ''.
3779
     * @param $sort {comma separated string} - A comma-separated list of field names to sort by. Default: 'menuindex'.
3780
     * @param $dir {'ASC'; 'DESC'} - Sorting direction. Default: 'ASC'.
3781
     * @param $limit {string} - SQL LIMIT (without 'LIMIT '). An empty string means no limit. Default: ''.
3782
     *
3783
     * @return {array; false} - Result array with documents, or false.
0 ignored issues
show
Documentation introduced by
The doc-type {array; could not be parsed: Unknown type name "{array" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
3784
     */
3785
    public function getDocuments($ids = array(), $published = 1, $deleted = 0, $fields = '*', $where = '', $sort = 'menuindex', $dir = 'ASC', $limit = '')
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
Coding Style introduced by
getDocuments uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3786
    {
3787
3788
        $cacheKey = md5(print_r(func_get_args(), true));
3789
        if (isset($this->tmpCache[__FUNCTION__][$cacheKey])) {
3790
            return $this->tmpCache[__FUNCTION__][$cacheKey];
3791
        }
3792
3793
        if (is_string($ids)) {
3794
            if (strpos($ids, ',') !== false) {
3795
                $ids = array_filter(array_map('intval', explode(',', $ids)));
3796
            } else {
3797
                $ids = array($ids);
3798
            }
3799
        }
3800
        if (count($ids) == 0) {
3801
            $this->tmpCache[__FUNCTION__][$cacheKey] = false;
3802
            return false;
3803
        } else {
3804
            // modify field names to use sc. table reference
3805
            $fields = 'sc.' . implode(',sc.', array_filter(array_map('trim', explode(',', $fields))));
3806
            $sort = ($sort == '') ? '' : 'sc.' . implode(',sc.', array_filter(array_map('trim', explode(',', $sort))));
3807
            if ($where != '') {
3808
                $where = 'AND ' . $where;
3809
            }
3810
3811
            $published = ($published !== 'all') ? "AND sc.published = '{$published}'" : '';
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $published (integer) and 'all' (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
3812
            $deleted = ($deleted !== 'all') ? "AND sc.deleted = '{$deleted}'" : '';
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $deleted (integer) and 'all' (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
3813
3814
            // get document groups for current user
3815
            if ($docgrp = $this->getUserDocGroups()) {
3816
                $docgrp = implode(',', $docgrp);
3817
            }
3818
3819
            $access = ($this->isFrontend() ? 'sc.privateweb=0' : '1="' . $_SESSION['mgrRole'] . '" OR sc.privatemgr=0') . (!$docgrp ? '' : ' OR dg.document_group IN (' . $docgrp . ')');
3820
3821
            $tblsc = $this->getFullTableName('site_content');
3822
            $tbldg = $this->getFullTableName('document_groups');
3823
3824
            $result = $this->db->select("DISTINCT {$fields}", "{$tblsc} sc
3825
                    LEFT JOIN {$tbldg} dg on dg.document = sc.id", "(sc.id IN (" . implode(',', $ids) . ") {$published} {$deleted} {$where}) AND ({$access}) GROUP BY sc.id", ($sort ? "{$sort} {$dir}" : ""), $limit);
3826
3827
            $resourceArray = $this->db->makeArray($result);
3828
3829
            $this->tmpCache[__FUNCTION__][$cacheKey] = $resourceArray;
3830
3831
            return $resourceArray;
3832
        }
3833
    }
3834
3835
    /**
3836
     * getDocument
3837
     * @version 1.0.1 (2014-02-19)
3838
     *
3839
     * @desc Returns required fields of a document.
3840
     *
3841
     * @param int $id {integer}
3842
     * - Id of a document which data has to be gained. @required
3843
     * @param string $fields {comma separated string; '*'}
3844
     * - Comma separated list of document fields to get. Default: '*'.
3845
     * @param int $published {0; 1; 'all'}
3846
     * - Document publication status. Once the parameter equals 'all', the result will be returned regardless of whether the documents are published or they are not. Default: false.
3847
     * @param int $deleted {0; 1; 'all'}
3848
     * - Document removal status. Once the parameter equals 'all', the result will be returned regardless of whether the documents are deleted or they are not. Default: 0.
3849
     * @return bool {array; false} - Result array with fields or false.
3850
     * - Result array with fields or false.
3851
     */
3852 View Code Duplication
    public function getDocument($id = 0, $fields = '*', $published = 1, $deleted = 0)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3853
    {
3854
        if ($id == 0) {
3855
            return false;
3856
        } else {
3857
            $docs = $this->getDocuments(array($id), $published, $deleted, $fields, '', '', '', 1);
3858
3859
            if ($docs != false) {
3860
                return $docs[0];
3861
            } else {
3862
                return false;
3863
            }
3864
        }
3865
    }
3866
3867
    /**
3868
     * @param string $field
3869
     * @param string $docid
3870
     * @return bool|mixed
3871
     */
3872
    public function getField($field = 'content', $docid = '')
3873
    {
3874
        if (empty($docid) && isset($this->documentIdentifier)) {
3875
            $docid = $this->documentIdentifier;
3876
        } elseif (!preg_match('@^[0-9]+$@', $docid)) {
3877
            $docid = $this->getIdFromAlias($docid);
3878
        }
3879
3880
        if (empty($docid)) {
3881
            return false;
3882
        }
3883
3884
        $cacheKey = md5(print_r(func_get_args(), true));
3885
        if (isset($this->tmpCache[__FUNCTION__][$cacheKey])) {
3886
            return $this->tmpCache[__FUNCTION__][$cacheKey];
3887
        }
3888
3889
        $doc = $this->getDocumentObject('id', $docid);
3890
        if (is_array($doc[$field])) {
3891
            $tvs = $this->getTemplateVarOutput($field, $docid, 1);
0 ignored issues
show
Documentation introduced by
$field is of type string, but the function expects a array.

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...
3892
            $content = $tvs[$field];
3893
        } else {
3894
            $content = $doc[$field];
3895
        }
3896
3897
        $this->tmpCache[__FUNCTION__][$cacheKey] = $content;
3898
3899
        return $content;
3900
    }
3901
3902
    /**
3903
     * Returns the page information as database row, the type of result is
3904
     * defined with the parameter $rowMode
3905
     *
3906
     * @param int $pageid The parent document identifier
3907
     *                    Default: -1 (no result)
3908
     * @param int $active Should we fetch only published and undeleted documents/resources?
3909
     *                     1 = yes, 0 = no
3910
     *                     Default: 1
3911
     * @param string $fields List of fields
3912
     *                       Default: id, pagetitle, description, alias
3913
     * @return boolean|array
3914
     */
3915
    public function getPageInfo($pageid = -1, $active = 1, $fields = 'id, pagetitle, description, alias')
0 ignored issues
show
Coding Style introduced by
getPageInfo uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3916
    {
3917
3918
        $cacheKey = md5(print_r(func_get_args(), true));
3919
        if (isset($this->tmpCache[__FUNCTION__][$cacheKey])) {
3920
            return $this->tmpCache[__FUNCTION__][$cacheKey];
3921
        }
3922
3923
        if ($pageid == 0) {
3924
            return false;
3925
        } else {
3926
            $tblsc = $this->getFullTableName("site_content");
3927
            $tbldg = $this->getFullTableName("document_groups");
3928
            $activeSql = $active == 1 ? "AND sc.published=1 AND sc.deleted=0" : "";
3929
            // modify field names to use sc. table reference
3930
            $fields = 'sc.' . implode(',sc.', array_filter(array_map('trim', explode(',', $fields))));
3931
            // get document groups for current user
3932
            if ($docgrp = $this->getUserDocGroups()) {
3933
                $docgrp = implode(",", $docgrp);
3934
            }
3935
            $access = ($this->isFrontend() ? "sc.privateweb=0" : "1='" . $_SESSION['mgrRole'] . "' OR sc.privatemgr=0") . (!$docgrp ? "" : " OR dg.document_group IN ($docgrp)");
3936
            $result = $this->db->select($fields, "{$tblsc} sc LEFT JOIN {$tbldg} dg on dg.document = sc.id", "(sc.id='{$pageid}' {$activeSql}) AND ({$access})", "", 1);
3937
            $pageInfo = $this->db->getRow($result);
3938
3939
            $this->tmpCache[__FUNCTION__][$cacheKey] = $pageInfo;
3940
3941
            return $pageInfo;
3942
        }
3943
    }
3944
3945
    /**
3946
     * Returns the parent document/resource of the given docid
3947
     *
3948
     * @param int $pid The parent docid. If -1, then fetch the current document/resource's parent
3949
     *                 Default: -1
3950
     * @param int $active Should we fetch only published and undeleted documents/resources?
3951
     *                     1 = yes, 0 = no
3952
     *                     Default: 1
3953
     * @param string $fields List of fields
3954
     *                       Default: id, pagetitle, description, alias
3955
     * @return boolean|array
3956
     */
3957
    public function getParent($pid = -1, $active = 1, $fields = 'id, pagetitle, description, alias, parent')
3958
    {
3959
        if ($pid == -1) {
3960
            $pid = $this->documentObject['parent'];
3961
            return ($pid == 0) ? false : $this->getPageInfo($pid, $active, $fields);
3962
        } else if ($pid == 0) {
3963
            return false;
3964
        } else {
3965
            // first get the child document
3966
            $child = $this->getPageInfo($pid, $active, "parent");
3967
            // now return the child's parent
3968
            $pid = ($child['parent']) ? $child['parent'] : 0;
3969
            return ($pid == 0) ? false : $this->getPageInfo($pid, $active, $fields);
3970
        }
3971
    }
3972
3973
    /**
3974
     * Returns the id of the current snippet.
3975
     *
3976
     * @return int
3977
     */
3978
    public function getSnippetId()
3979
    {
3980
        if ($this->currentSnippet) {
3981
            $tbl = $this->getFullTableName("site_snippets");
3982
            $rs = $this->db->select('id', $tbl, "name='" . $this->db->escape($this->currentSnippet) . "'", '', 1);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
3983
            if ($snippetId = $this->db->getValue($rs)) {
3984
                return $snippetId;
3985
            }
3986
        }
3987
        return 0;
3988
    }
3989
3990
    /**
3991
     * Returns the name of the current snippet.
3992
     *
3993
     * @return string
3994
     */
3995
    public function getSnippetName()
3996
    {
3997
        return $this->currentSnippet;
3998
    }
3999
4000
    /**
4001
     * Clear the cache of MODX.
4002
     *
4003
     * @param string $type
4004
     * @param bool $report
4005
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
4006
     */
4007
    public function clearCache($type = '', $report = false)
4008
    {
4009
        $cache_dir = MODX_BASE_PATH . $this->getCacheFolder();
4010
        if (is_array($type)) {
4011
            foreach ($type as $_) {
4012
                $this->clearCache($_, $report);
4013
            }
4014
        } elseif ($type == 'full') {
4015
            include_once(MODX_MANAGER_PATH . 'processors/cache_sync.class.processor.php');
4016
            $sync = new synccache();
4017
            $sync->setCachepath($cache_dir);
4018
            $sync->setReport($report);
4019
            $sync->emptyCache();
4020
        } elseif (preg_match('@^[1-9][0-9]*$@', $type)) {
4021
            $key = ($this->config['cache_type'] == 2) ? $this->makePageCacheKey($type) : $type;
4022
            $file_name = "docid_" . $key . "_*.pageCache.php";
4023
            $cache_path = $cache_dir . $file_name;
4024
            $files = glob($cache_path);
4025
            $files[] = $cache_dir . "docid_" . $key . ".pageCache.php";
4026
            foreach ($files as $file) {
4027
                if (!is_file($file)) {
4028
                    continue;
4029
                }
4030
                unlink($file);
4031
            }
4032
        } else {
4033
            $files = glob($cache_dir . '*');
4034
            foreach ($files as $file) {
4035
                $name = basename($file);
4036
                if (strpos($name, '.pageCache.php') === false) {
4037
                    continue;
4038
                }
4039
                if (!is_file($file)) {
4040
                    continue;
4041
                }
4042
                unlink($file);
4043
            }
4044
        }
4045
    }
4046
4047
    /**
4048
     * makeUrl
4049
     *
4050
     * @desc Create an URL for the given document identifier. The url prefix and postfix are used, when “friendly_url” is active.
4051
     *
4052
     * @param $id {integer} - The document identifier. @required
4053
     * @param string $alias {string}
4054
     * - The alias name for the document. Default: ''.
4055
     * @param string $args {string}
4056
     * - The paramaters to add to the URL. Default: ''.
4057
     * @param string $scheme {string}
4058
     * - With full as valus, the site url configuration is used. Default: ''.
4059
     * @return mixed|string {string} - Result URL.
4060
     * - Result URL.
4061
     */
4062
    public function makeUrl($id, $alias = '', $args = '', $scheme = '')
0 ignored issues
show
Coding Style introduced by
makeUrl uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
4063
    {
4064
        $url = '';
0 ignored issues
show
Unused Code introduced by
$url 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...
4065
        $virtualDir = isset($this->config['virtual_dir']) ? $this->config['virtual_dir'] : '';
4066
        $f_url_prefix = $this->config['friendly_url_prefix'];
4067
        $f_url_suffix = $this->config['friendly_url_suffix'];
4068
4069
        if (!is_numeric($id)) {
4070
            $this->messageQuit("`{$id}` is not numeric and may not be passed to makeUrl()");
4071
        }
4072
4073
        if ($args !== '') {
4074
            // add ? or & to $args if missing
4075
            $args = ltrim($args, '?&');
4076
            $_ = strpos($f_url_prefix, '?');
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $_. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
4077
4078
            if ($_ === false && $this->config['friendly_urls'] == 1) {
4079
                $args = "?{$args}";
4080
            } else {
4081
                $args = "&{$args}";
4082
            }
4083
        }
4084
4085
        if ($id != $this->config['site_start']) {
4086
            if ($this->config['friendly_urls'] == 1 && $alias == '') {
4087
                $alias = $id;
4088
                $alPath = '';
4089
4090
                if ($this->config['friendly_alias_urls'] == 1) {
4091
4092
                    if ($this->config['aliaslistingfolder'] == 1) {
4093
                        $al = $this->getAliasListing($id);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $al. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
4094
                    } else {
4095
                        $al = $this->aliasListing[$id];
4096
                    }
4097
4098
                    if ($al['isfolder'] === 1 && $this->config['make_folders'] === '1') {
4099
                        $f_url_suffix = '/';
4100
                    }
4101
4102
                    $alPath = !empty ($al['path']) ? $al['path'] . '/' : '';
4103
4104
                    if ($al && $al['alias']) {
4105
                        $alias = $al['alias'];
4106
                    }
4107
4108
                }
4109
4110
                $alias = $alPath . $f_url_prefix . $alias . $f_url_suffix;
4111
                $url = "{$alias}{$args}";
4112
            } else {
4113
                $url = "index.php?id={$id}{$args}";
4114
            }
4115
        } else {
4116
            $url = $args;
4117
        }
4118
4119
        $host = $this->config['base_url'];
4120
4121
        // check if scheme argument has been set
4122
        if ($scheme != '') {
4123
            // for backward compatibility - check if the desired scheme is different than the current scheme
4124
            if (is_numeric($scheme) && $scheme != $_SERVER['HTTPS']) {
4125
                $scheme = ($_SERVER['HTTPS'] ? 'http' : 'https');
4126
            }
4127
4128
            //TODO: check to make sure that $site_url incudes the url :port (e.g. :8080)
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
4129
            $host = $scheme == 'full' ? $this->config['site_url'] : $scheme . '://' . $_SERVER['HTTP_HOST'] . $host;
4130
        }
4131
4132
        //fix strictUrl by Bumkaka
4133
        if ($this->config['seostrict'] == '1') {
4134
            $url = $this->toAlias($url);
4135
        }
4136
4137
        if ($this->config['xhtml_urls']) {
4138
            $url = preg_replace("/&(?!amp;)/", "&amp;", $host . $virtualDir . $url);
4139
        } else {
4140
            $url = $host . $virtualDir . $url;
4141
        }
4142
4143
        $evtOut = $this->invokeEvent('OnMakeDocUrl', array(
4144
            'id' => $id,
4145
            'url' => $url
4146
        ));
4147
4148
        if (is_array($evtOut) && count($evtOut) > 0) {
4149
            $url = array_pop($evtOut);
4150
        }
4151
4152
        return $url;
4153
    }
4154
4155
    /**
4156
     * @param $id
4157
     * @return mixed
4158
     */
4159
    public function getAliasListing($id)
4160
    {
4161
        if (isset($this->aliasListing[$id])) {
4162
            $out = $this->aliasListing[$id];
4163
        } else {
4164
            $q = $this->db->query("SELECT id,alias,isfolder,parent FROM " . $this->getFullTableName("site_content") . " WHERE id=" . (int)$id);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $q. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
4165
            if ($this->db->getRecordCount($q) == '1') {
4166
                $q = $this->db->getRow($q);
4167
                $this->aliasListing[$id] = array(
4168
                    'id' => (int)$q['id'],
4169
                    'alias' => $q['alias'] == '' ? $q['id'] : $q['alias'],
4170
                    'parent' => (int)$q['parent'],
4171
                    'isfolder' => (int)$q['isfolder'],
4172
                );
4173
                if ($this->aliasListing[$id]['parent'] > 0) {
4174
                    //fix alias_path_usage
4175
                    if ($this->config['use_alias_path'] == '1') {
4176
                        //&& $tmp['path'] != '' - fix error slash with epty path
4177
                        $tmp = $this->getAliasListing($this->aliasListing[$id]['parent']);
4178
                        $this->aliasListing[$id]['path'] = $tmp['path'] . ($tmp['alias_visible'] ? (($tmp['parent'] > 0 && $tmp['path'] != '') ? '/' : '') . $tmp['alias'] : '');
4179
                    } else {
4180
                        $this->aliasListing[$id]['path'] = '';
4181
                    }
4182
                }
4183
4184
                $out = $this->aliasListing[$id];
4185
            }
4186
        }
4187
        return $out;
0 ignored issues
show
Bug introduced by
The variable $out 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...
4188
    }
4189
4190
    /**
4191
     * Returns an entry from the config
4192
     *
4193
     * Note: most code accesses the config array directly and we will continue to support this.
4194
     *
4195
     * @param string $name
4196
     * @return bool|string
4197
     */
4198
    public function getConfig($name = '')
4199
    {
4200
        if (!empty ($this->config[$name])) {
4201
            return $this->config[$name];
4202
        } else {
4203
            return false;
4204
        }
4205
    }
4206
4207
    /**
4208
     * Returns the MODX version information as version, branch, release date and full application name.
4209
     *
4210
     * @param null $data
4211
     * @return array
4212
     */
4213
4214
    public function getVersionData($data = null)
4215
    {
4216
        $out = array();
4217
        if (empty($this->version) || !is_array($this->version)) {
4218
            //include for compatibility modx version < 1.0.10
4219
            include MODX_MANAGER_PATH . "includes/version.inc.php";
4220
            $this->version = array();
4221
            $this->version['version'] = isset($modx_version) ? $modx_version : '';
0 ignored issues
show
Bug introduced by
The variable $modx_version seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
4222
            $this->version['branch'] = isset($modx_branch) ? $modx_branch : '';
0 ignored issues
show
Bug introduced by
The variable $modx_branch seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
4223
            $this->version['release_date'] = isset($modx_release_date) ? $modx_release_date : '';
0 ignored issues
show
Bug introduced by
The variable $modx_release_date seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
4224
            $this->version['full_appname'] = isset($modx_full_appname) ? $modx_full_appname : '';
0 ignored issues
show
Bug introduced by
The variable $modx_full_appname seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
4225
            $this->version['new_version'] = isset($this->config['newversiontext']) ? $this->config['newversiontext'] : '';
4226
        }
4227
        return (!is_null($data) && is_array($this->version) && isset($this->version[$data])) ? $this->version[$data] : $this->version;
4228
    }
4229
4230
    /**
4231
     * Executes a snippet.
4232
     *
4233
     * @param string $snippetName
4234
     * @param array $params Default: Empty array
4235
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|object|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
4236
     */
4237
    public function runSnippet($snippetName, $params = array())
4238
    {
4239
        if (isset ($this->snippetCache[$snippetName])) {
4240
            $snippet = $this->snippetCache[$snippetName];
4241
            $properties = !empty($this->snippetCache[$snippetName . "Props"]) ? $this->snippetCache[$snippetName . "Props"] : '';
4242
        } else { // not in cache so let's check the db
4243
            $sql = "SELECT ss.`name`, ss.`snippet`, ss.`properties`, sm.properties as `sharedproperties` FROM " . $this->getFullTableName("site_snippets") . " as ss LEFT JOIN " . $this->getFullTableName('site_modules') . " as sm on sm.guid=ss.moduleguid WHERE ss.`name`='" . $this->db->escape($snippetName) . "'  AND ss.disabled=0;";
4244
            $result = $this->db->query($sql);
4245
            if ($this->db->getRecordCount($result) == 1) {
4246
                $row = $this->db->getRow($result);
4247
                $snippet = $this->snippetCache[$snippetName] = $row['snippet'];
4248
                $mergedProperties = array_merge($this->parseProperties($row['properties']), $this->parseProperties($row['sharedproperties']));
4249
                $properties = $this->snippetCache[$snippetName . "Props"] = json_encode($mergedProperties);
4250
            } else {
4251
                $snippet = $this->snippetCache[$snippetName] = "return false;";
4252
                $properties = $this->snippetCache[$snippetName . "Props"] = '';
4253
            }
4254
        }
4255
        // load default params/properties
4256
        $parameters = $this->parseProperties($properties, $snippetName, 'snippet');
4257
        $parameters = array_merge($parameters, $params);
4258
4259
        // run snippet
4260
        return $this->evalSnippet($snippet, $parameters);
4261
    }
4262
4263
    /**
4264
     * Returns the chunk content for the given chunk name
4265
     *
4266
     * @param string $chunkName
4267
     * @return boolean|string
4268
     */
4269
    public function getChunk($chunkName)
4270
    {
4271
        $out = null;
4272
        if (empty($chunkName)) {
4273
            return $out;
4274
        }
4275
        if (isset ($this->chunkCache[$chunkName])) {
4276
            $out = $this->chunkCache[$chunkName];
4277
        } else if (stripos($chunkName, '@FILE') === 0) {
4278
            $out = $this->chunkCache[$chunkName] = $this->atBindFileContent($chunkName);
4279
        } else {
4280
            $where = sprintf("`name`='%s' AND disabled=0", $this->db->escape($chunkName));
4281
            $rs = $this->db->select('snippet', '[+prefix+]site_htmlsnippets', $where);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
4282
            if ($this->db->getRecordCount($rs) == 1) {
4283
                $row = $this->db->getRow($rs);
4284
                $out = $this->chunkCache[$chunkName] = $row['snippet'];
4285
            } else {
4286
                $out = $this->chunkCache[$chunkName] = null;
4287
            }
4288
        }
4289
        return $out;
4290
    }
4291
4292
    /**
4293
     * parseText
4294
     * @version 1.0 (2013-10-17)
4295
     *
4296
     * @desc Replaces placeholders in text with required values.
4297
     *
4298
     * @param string $tpl
4299
     * @param array $ph
4300
     * @param string $left
4301
     * @param string $right
4302
     * @param bool $execModifier
4303
     * @return string {string} - Parsed text.
4304
     * - Parsed text.
4305
     * @internal param $chunk {string} - String to parse. - String to parse. @required
4306
     * @internal param $chunkArr {array} - Array of values. Key — placeholder name, value — value. - Array of values. Key — placeholder name, value — value. @required
4307
     * @internal param $prefix {string} - Placeholders prefix. Default: '[+'. - Placeholders prefix. Default: '[+'.
4308
     * @internal param $suffix {string} - Placeholders suffix. Default: '+]'. - Placeholders suffix. Default: '+]'.
4309
     *
4310
     */
4311
    public function parseText($tpl = '', $ph = array(), $left = '[+', $right = '+]', $execModifier = true)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ph. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Coding Style introduced by
parseText uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
4312
    {
4313
        if (empty($ph) || empty($tpl)) {
4314
            return $tpl;
4315
        }
4316
4317 View Code Duplication
        if ($this->config['enable_at_syntax']) {
4318
            if (stripos($tpl, '<@LITERAL>') !== false) {
4319
                $tpl = $this->escapeLiteralTagsContent($tpl);
4320
            }
4321
        }
4322
4323
        $matches = $this->getTagsFromContent($tpl, $left, $right);
4324
        if (empty($matches)) {
4325
            return $tpl;
4326
        }
4327
4328
        foreach ($matches[1] as $i => $key) {
4329
4330
            if (strpos($key, ':') !== false && $execModifier) {
4331
                list($key, $modifiers) = $this->splitKeyAndFilter($key);
4332
            } else {
4333
                $modifiers = false;
4334
            }
4335
4336
            //          if(!isset($ph[$key])) continue;
0 ignored issues
show
Unused Code Comprehensibility introduced by
87% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4337
            if (!array_key_exists($key, $ph)) {
4338
                continue;
4339
            } //NULL values must be saved in placeholders, if we got them from database string
4340
4341
            $value = $ph[$key];
4342
4343
            $s = &$matches[0][$i];
4344
            if ($modifiers !== false) {
4345
                if (strpos($modifiers, $left) !== false) {
4346
                    $modifiers = $this->parseText($modifiers, $ph, $left, $right);
4347
                }
4348
                $value = $this->applyFilter($value, $modifiers, $key);
4349
            }
4350 View Code Duplication
            if (strpos($tpl, $s) !== false) {
4351
                $tpl = str_replace($s, $value, $tpl);
4352
            } elseif($this->debug) {
4353
                $this->addLog('parseText parse error', $_SERVER['REQUEST_URI'] . $s, 2);
4354
            }
4355
        }
4356
4357
        return $tpl;
4358
    }
4359
4360
    /**
4361
     * parseChunk
4362
     * @version 1.1 (2013-10-17)
4363
     *
4364
     * @desc Replaces placeholders in a chunk with required values.
4365
     *
4366
     * @param $chunkName {string} - Name of chunk to parse. @required
4367
     * @param $chunkArr {array} - Array of values. Key — placeholder name, value — value. @required
4368
     * @param string $prefix {string}
4369
     * - Placeholders prefix. Default: '{'.
4370
     * @param string $suffix {string}
4371
     * - Placeholders suffix. Default: '}'.
4372
     * @return bool|mixed|string {string; false} - Parsed chunk or false if $chunkArr is not array.
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use false|string.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
4373
     * - Parsed chunk or false if $chunkArr is not array.
4374
     */
4375
    public function parseChunk($chunkName, $chunkArr, $prefix = '{', $suffix = '}')
4376
    {
4377
        //TODO: Wouldn't it be more practical to return the contents of a chunk instead of false?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
4378
        if (!is_array($chunkArr)) {
4379
            return false;
4380
        }
4381
4382
        return $this->parseText($this->getChunk($chunkName), $chunkArr, $prefix, $suffix);
0 ignored issues
show
Bug introduced by
It seems like $this->getChunk($chunkName) targeting DocumentParser::getChunk() can also be of type boolean; however, DocumentParser::parseText() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
4383
    }
4384
4385
    /**
4386
     * getTpl
4387
     * get template for snippets
4388
     * @param $tpl {string}
4389
     * @return bool|string {string}
4390
     */
4391
    public function getTpl($tpl)
4392
    {
4393
        $template = $tpl;
4394
        if (preg_match("/^@([^:\s]+)[:\s]+(.+)$/s", trim($tpl), $match)) {
4395
            $command = strtoupper($match[1]);
4396
            $template = $match[2];
4397
        }
4398
        switch ($command) {
0 ignored issues
show
Bug introduced by
The variable $command 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...
4399
            case 'CODE':
4400
                break;
4401
            case 'FILE':
4402
                $template = file_get_contents(MODX_BASE_PATH . $template);
4403
                break;
4404
            case 'CHUNK':
4405
                $template = $this->getChunk($template);
4406
                break;
4407
            case 'DOCUMENT':
4408
                $doc = $this->getDocument($template, 'content', 'all');
4409
                $template = $doc['content'];
4410
                break;
4411
            case 'SELECT':
4412
                $this->db->getValue($this->db->query("SELECT {$template}"));
4413
                break;
4414
            default:
4415
                if (!($template = $this->getChunk($tpl))) {
4416
                    $template = $tpl;
4417
                }
4418
        }
4419
        return $template;
4420
    }
4421
4422
    /**
4423
     * Returns the timestamp in the date format defined in $this->config['datetime_format']
4424
     *
4425
     * @param int $timestamp Default: 0
4426
     * @param string $mode Default: Empty string (adds the time as below). Can also be 'dateOnly' for no time or 'formatOnly' to get the datetime_format string.
4427
     * @return string
4428
     */
4429
    public function toDateFormat($timestamp = 0, $mode = '')
4430
    {
4431
        $timestamp = trim($timestamp);
4432
        if ($mode !== 'formatOnly' && empty($timestamp)) {
4433
            return '-';
4434
        }
4435
        $timestamp = (int)$timestamp;
4436
4437
        switch ($this->config['datetime_format']) {
4438
            case 'YYYY/mm/dd':
4439
                $dateFormat = '%Y/%m/%d';
4440
                break;
4441
            case 'dd-mm-YYYY':
4442
                $dateFormat = '%d-%m-%Y';
4443
                break;
4444
            case 'mm/dd/YYYY':
4445
                $dateFormat = '%m/%d/%Y';
4446
                break;
4447
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4448
            case 'dd-mmm-YYYY':
4449
                $dateFormat = '%e-%b-%Y';
4450
                break;
4451
            */
4452
        }
4453
4454
        if (empty($mode)) {
4455
            $strTime = strftime($dateFormat . " %H:%M:%S", $timestamp);
0 ignored issues
show
Bug introduced by
The variable $dateFormat 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...
4456
        } elseif ($mode == 'dateOnly') {
4457
            $strTime = strftime($dateFormat, $timestamp);
4458
        } elseif ($mode == 'formatOnly') {
4459
            $strTime = $dateFormat;
4460
        }
4461
        return $strTime;
0 ignored issues
show
Bug introduced by
The variable $strTime 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...
4462
    }
4463
4464
    /**
4465
     * Make a timestamp from a string corresponding to the format in $this->config['datetime_format']
4466
     *
4467
     * @param string $str
4468
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
4469
     */
4470
    public function toTimeStamp($str)
4471
    {
4472
        $str = trim($str);
4473
        if (empty($str)) {
4474
            return '';
4475
        }
4476
4477
        switch ($this->config['datetime_format']) {
4478 View Code Duplication
            case 'YYYY/mm/dd':
4479
                if (!preg_match('/^[0-9]{4}\/[0-9]{2}\/[0-9]{2}[0-9 :]*$/', $str)) {
4480
                    return '';
4481
                }
4482
                list ($Y, $m, $d, $H, $M, $S) = sscanf($str, '%4d/%2d/%2d %2d:%2d:%2d');
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $Y. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $m. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $d. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $H. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $M. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $S. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
4483
                break;
4484 View Code Duplication
            case 'dd-mm-YYYY':
4485
                if (!preg_match('/^[0-9]{2}-[0-9]{2}-[0-9]{4}[0-9 :]*$/', $str)) {
4486
                    return '';
4487
                }
4488
                list ($d, $m, $Y, $H, $M, $S) = sscanf($str, '%2d-%2d-%4d %2d:%2d:%2d');
4489
                break;
4490 View Code Duplication
            case 'mm/dd/YYYY':
4491
                if (!preg_match('/^[0-9]{2}\/[0-9]{2}\/[0-9]{4}[0-9 :]*$/', $str)) {
4492
                    return '';
4493
                }
4494
                list ($m, $d, $Y, $H, $M, $S) = sscanf($str, '%2d/%2d/%4d %2d:%2d:%2d');
4495
                break;
4496
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4497
            case 'dd-mmm-YYYY':
4498
                if (!preg_match('/^[0-9]{2}-[0-9a-z]+-[0-9]{4}[0-9 :]*$/i', $str)) {return '';}
4499
                list ($m, $d, $Y, $H, $M, $S) = sscanf($str, '%2d-%3s-%4d %2d:%2d:%2d');
4500
                break;
4501
            */
4502
        }
4503
        if (!$H && !$M && !$S) {
0 ignored issues
show
Bug introduced by
The variable $H 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...
Bug introduced by
The variable $M 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...
Bug introduced by
The variable $S 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...
4504
            $H = 0;
4505
            $M = 0;
4506
            $S = 0;
4507
        }
4508
        $timeStamp = mktime($H, $M, $S, $m, $d, $Y);
0 ignored issues
show
Bug introduced by
The variable $m 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...
Bug introduced by
The variable $d 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...
Bug introduced by
The variable $Y 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...
4509
        $timeStamp = (int)$timeStamp;
4510
        return $timeStamp;
4511
    }
4512
4513
    /**
4514
     * Get the TVs of a document's children. Returns an array where each element represents one child doc.
4515
     *
4516
     * Ignores deleted children. Gets all children - there is no where clause available.
4517
     *
4518
     * @param int $parentid The parent docid
4519
     *                 Default: 0 (site root)
4520
     * @param array $tvidnames . Which TVs to fetch - Can relate to the TV ids in the db (array elements should be numeric only)
4521
     *                                               or the TV names (array elements should be names only)
4522
     *                      Default: Empty array
4523
     * @param int $published Whether published or unpublished documents are in the result
4524
     *                      Default: 1
4525
     * @param string $docsort How to sort the result array (field)
4526
     *                      Default: menuindex
4527
     * @param ASC|string $docsortdir How to sort the result array (direction)
4528
     *                      Default: ASC
4529
     * @param string $tvfields Fields to fetch from site_tmplvars, default '*'
4530
     *                      Default: *
4531
     * @param string $tvsort How to sort each element of the result array i.e. how to sort the TVs (field)
4532
     *                      Default: rank
4533
     * @param string $tvsortdir How to sort each element of the result array i.e. how to sort the TVs (direction)
4534
     *                      Default: ASC
4535
     * @return array|bool
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use false|array.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
4536
     */
4537
    public function getDocumentChildrenTVars($parentid = 0, $tvidnames = array(), $published = 1, $docsort = "menuindex", $docsortdir = "ASC", $tvfields = "*", $tvsort = "rank", $tvsortdir = "ASC")
4538
    {
4539
        $docs = $this->getDocumentChildren($parentid, $published, 0, '*', '', $docsort, $docsortdir);
4540
        if (!$docs) {
4541
            return false;
4542
        } else {
4543
            $result = array();
4544
            // get user defined template variables
4545
            if ($tvfields) {
4546
                $_ = array_filter(array_map('trim', explode(',', $tvfields)));
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $_. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
4547
                foreach ($_ as $i => $v) {
4548
                    if ($v === 'value') {
4549
                        unset($_[$i]);
4550
                    } else {
4551
                        $_[$i] = 'tv.' . $v;
4552
                    }
4553
                }
4554
                $fields = implode(',', $_);
4555
            } else {
4556
                $fields = "tv.*";
4557
            }
4558
4559
            if ($tvsort != '') {
4560
                $tvsort = 'tv.' . implode(',tv.', array_filter(array_map('trim', explode(',', $tvsort))));
4561
            }
4562 View Code Duplication
            if ($tvidnames == "*") {
4563
                $query = "tv.id<>0";
4564
            } else {
4565
                $query = (is_numeric($tvidnames[0]) ? "tv.id" : "tv.name") . " IN ('" . implode("','", $tvidnames) . "')";
4566
            }
4567
4568
            $this->getUserDocGroups();
4569
4570
            foreach ($docs as $doc) {
4571
4572
                $docid = $doc['id'];
4573
4574
                $rs = $this->db->select("{$fields}, IF(tvc.value!='',tvc.value,tv.default_text) as value ", "[+prefix+]site_tmplvars tv
4575
                        INNER JOIN [+prefix+]site_tmplvar_templates tvtpl ON tvtpl.tmplvarid = tv.id
4576
                        LEFT JOIN [+prefix+]site_tmplvar_contentvalues tvc ON tvc.tmplvarid=tv.id AND tvc.contentid='{$docid}'", "{$query} AND tvtpl.templateid = '{$doc['template']}'", ($tvsort ? "{$tvsort} {$tvsortdir}" : ""));
4577
                $tvs = $this->db->makeArray($rs);
4578
4579
                // get default/built-in template variables
4580
                ksort($doc);
4581
                foreach ($doc as $key => $value) {
4582
                    if ($tvidnames == '*' || in_array($key, $tvidnames)) {
4583
                        $tvs[] = array('name' => $key, 'value' => $value);
4584
                    }
4585
                }
4586
                if (is_array($tvs) && count($tvs)) {
4587
                    $result[] = $tvs;
4588
                }
4589
            }
4590
            return $result;
4591
        }
4592
    }
4593
4594
    /**
4595
     * getDocumentChildrenTVarOutput
4596
     * @version 1.1 (2014-02-19)
4597
     *
4598
     * @desc Returns an array where each element represents one child doc and contains the result from getTemplateVarOutput().
4599
     *
4600
     * @param int $parentid {integer}
4601
     * - Id of parent document. Default: 0 (site root).
4602
     * @param array $tvidnames {array; '*'}
4603
     * - Which TVs to fetch. In the form expected by getTemplateVarOutput(). Default: array().
4604
     * @param int $published {0; 1; 'all'}
4605
     * - Document publication status. Once the parameter equals 'all', the result will be returned regardless of whether the ducuments are published or they are not. Default: 1.
4606
     * @param string $sortBy {string}
4607
     * - How to sort the result array (field). Default: 'menuindex'.
4608
     * @param string $sortDir {'ASC'; 'DESC'}
4609
     * - How to sort the result array (direction). Default: 'ASC'.
4610
     * @param string $where {string}
4611
     * - SQL WHERE condition (use only document fields, not TV). Default: ''.
4612
     * @param string $resultKey {string; false}
4613
     * - Field, which values are keys into result array. Use the “false”, that result array keys just will be numbered. Default: 'id'.
4614
     * @return array {array; false} - Result array, or false.
0 ignored issues
show
Documentation introduced by
Should the return type not be false|array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
4615
     * - Result array, or false.
4616
     */
4617
    public function getDocumentChildrenTVarOutput($parentid = 0, $tvidnames = array(), $published = 1, $sortBy = 'menuindex', $sortDir = 'ASC', $where = '', $resultKey = 'id')
4618
    {
4619
        $docs = $this->getDocumentChildren($parentid, $published, 0, 'id', $where, $sortBy, $sortDir);
4620
4621
        if (!$docs) {
4622
            return false;
4623
        } else {
4624
            $result = array();
4625
4626
            $unsetResultKey = false;
4627
4628
            if ($resultKey !== false) {
4629
                if (is_array($tvidnames)) {
4630
                    if (count($tvidnames) != 0 && !in_array($resultKey, $tvidnames)) {
4631
                        $tvidnames[] = $resultKey;
4632
                        $unsetResultKey = true;
4633
                    }
4634
                } else if ($tvidnames != '*' && $tvidnames != $resultKey) {
4635
                    $tvidnames = array($tvidnames, $resultKey);
4636
                    $unsetResultKey = true;
4637
                }
4638
            }
4639
4640
            for ($i = 0; $i < count($docs); $i++) {
4641
                $tvs = $this->getTemplateVarOutput($tvidnames, $docs[$i]['id'], $published);
4642
4643
                if ($tvs) {
4644
                    if ($resultKey !== false && array_key_exists($resultKey, $tvs)) {
4645
                        $result[$tvs[$resultKey]] = $tvs;
4646
4647
                        if ($unsetResultKey) {
4648
                            unset($result[$tvs[$resultKey]][$resultKey]);
4649
                        }
4650
                    } else {
4651
                        $result[] = $tvs;
4652
                    }
4653
                }
4654
            }
4655
4656
            return $result;
4657
        }
4658
    }
4659
4660
    /**
4661
     * Modified by Raymond for TV - Orig Modified by Apodigm - DocVars
4662
     * Returns a single site_content field or TV record from the db.
4663
     *
4664
     * If a site content field the result is an associative array of 'name' and 'value'.
4665
     *
4666
     * If a TV the result is an array representing a db row including the fields specified in $fields.
4667
     *
4668
     * @param string $idname Can be a TV id or name
4669
     * @param string $fields Fields to fetch from site_tmplvars. Default: *
4670
     * @param string|type $docid Docid. Defaults to empty string which indicates the current document.
4671
     * @param int $published Whether published or unpublished documents are in the result
4672
     *                        Default: 1
4673
     * @return bool
4674
     */
4675 View Code Duplication
    public function getTemplateVar($idname = "", $fields = "*", $docid = "", $published = 1)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4676
    {
4677
        if ($idname == "") {
4678
            return false;
4679
        } else {
4680
            $result = $this->getTemplateVars(array($idname), $fields, $docid, $published, "", ""); //remove sorting for speed
4681
            return ($result != false) ? $result[0] : false;
4682
        }
4683
    }
4684
4685
    /**
4686
     * getTemplateVars
4687
     * @version 1.0.1 (2014-02-19)
4688
     *
4689
     * @desc Returns an array of site_content field fields and/or TV records from the db.
4690
     * Elements representing a site content field consist of an associative array of 'name' and 'value'.
4691
     * Elements representing a TV consist of an array representing a db row including the fields specified in $fields.
4692
     *
4693
     * @param $idnames {array; '*'} - Which TVs to fetch. Can relate to the TV ids in the db (array elements should be numeric only) or the TV names (array elements should be names only). @required
4694
     * @param $fields {comma separated string; '*'} - Fields names in the TV table of MODx database. Default: '*'
4695
     * @param $docid {integer; ''} - Id of a document to get. Default: an empty string which indicates the current document.
4696
     * @param $published {0; 1; 'all'} - Document publication status. Once the parameter equals 'all', the result will be returned regardless of whether the ducuments are published or they are not. Default: 1.
4697
     * @param $sort {comma separated string} - Fields of the TV table to sort by. Default: 'rank'.
4698
     * @param $dir {'ASC'; 'DESC'} - How to sort the result array (direction). Default: 'ASC'.
4699
     *
4700
     * @return {array; false} - Result array, or false.
0 ignored issues
show
Documentation introduced by
The doc-type {array; could not be parsed: Unknown type name "{array" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
4701
     */
4702
    public function getTemplateVars($idnames = array(), $fields = '*', $docid = '', $published = 1, $sort = 'rank', $dir = 'ASC')
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
4703
    {
4704
        $cacheKey = md5(print_r(func_get_args(), true));
4705
        if (isset($this->tmpCache[__FUNCTION__][$cacheKey])) {
4706
            return $this->tmpCache[__FUNCTION__][$cacheKey];
4707
        }
4708
4709
        if (($idnames != '*' && !is_array($idnames)) || empty($idnames) ) {
4710
            return false;
4711
        } else {
4712
4713
            // get document record
4714
            if ($docid == '') {
4715
                $docid = $this->documentIdentifier;
4716
                $docRow = $this->documentObject;
4717
            } else {
4718
                $docRow = $this->getDocument($docid, '*', $published);
4719
4720
                if (!$docRow) {
4721
                    $this->tmpCache[__FUNCTION__][$cacheKey] = false;
4722
                    return false;
4723
                }
4724
            }
4725
4726
            // get user defined template variables
4727
            $fields = ($fields == '') ? 'tv.*' : 'tv.' . implode(',tv.', array_filter(array_map('trim', explode(',', $fields))));
4728
            $sort = ($sort == '') ? '' : 'tv.' . implode(',tv.', array_filter(array_map('trim', explode(',', $sort))));
4729
4730 View Code Duplication
            if ($idnames == '*') {
4731
                $query = 'tv.id<>0';
4732
            } else {
4733
                $query = (is_numeric($idnames[0]) ? 'tv.id' : 'tv.name') . " IN ('" . implode("','", $idnames) . "')";
4734
            }
4735
4736
            $rs = $this->db->select("{$fields}, IF(tvc.value != '', tvc.value, tv.default_text) as value", $this->getFullTableName('site_tmplvars') . " tv
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
4737
                    INNER JOIN " . $this->getFullTableName('site_tmplvar_templates') . " tvtpl ON tvtpl.tmplvarid = tv.id
4738
                    LEFT JOIN " . $this->getFullTableName('site_tmplvar_contentvalues') . " tvc ON tvc.tmplvarid=tv.id AND tvc.contentid = '{$docid}'", "{$query} AND tvtpl.templateid = '{$docRow['template']}'", ($sort ? "{$sort} {$dir}" : ""));
4739
4740
            $result = $this->db->makeArray($rs);
4741
4742
            // get default/built-in template variables
4743
            if(is_array($docRow)){
4744
                ksort($docRow);
4745
4746
                foreach ($docRow as $key => $value) {
4747
                    if ($idnames == '*' || in_array($key, $idnames)) {
4748
                        array_push($result, array(
4749
                            'name' => $key,
4750
                            'value' => $value
4751
                        ));
4752
                    }
4753
                }
4754
            }
4755
4756
            $this->tmpCache[__FUNCTION__][$cacheKey] = $result;
4757
4758
            return $result;
4759
        }
4760
    }
4761
4762
    /**
4763
     * getTemplateVarOutput
4764
     * @version 1.0.1 (2014-02-19)
4765
     *
4766
     * @desc Returns an associative array containing TV rendered output values.
4767
     *
4768
     * @param array $idnames {array; '*'}
4769
     * - Which TVs to fetch - Can relate to the TV ids in the db (array elements should be numeric only) or the TV names (array elements should be names only). @required
4770
     * @param string $docid {integer; ''}
4771
     * - Id of a document to get. Default: an empty string which indicates the current document.
4772
     * @param int $published {0; 1; 'all'}
4773
     * - Document publication status. Once the parameter equals 'all', the result will be returned regardless of whether the ducuments are published or they are not. Default: 1.
4774
     * @param string $sep {string}
4775
     * - Separator that is used while concatenating in getTVDisplayFormat(). Default: ''.
4776
     * @return array {array; false} - Result array, or false.
0 ignored issues
show
Documentation introduced by
Should the return type not be false|array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
4777
     * - Result array, or false.
4778
     */
4779
    public function getTemplateVarOutput($idnames = array(), $docid = '', $published = 1, $sep = '')
4780
    {
4781
        if (is_array($idnames) && empty($idnames) ) {
4782
            return false;
4783
        } else {
4784
            $output = array();
4785
            $vars = ($idnames == '*' || is_array($idnames)) ? $idnames : array($idnames);
4786
4787
            $docid = (int)$docid > 0 ? (int)$docid : $this->documentIdentifier;
4788
            // remove sort for speed
4789
            $result = $this->getTemplateVars($vars, '*', $docid, $published, '', '');
4790
4791
            if ($result == false) {
4792
                return false;
4793
            } else {
4794
                $baspath = MODX_MANAGER_PATH . 'includes';
4795
                include_once $baspath . '/tmplvars.format.inc.php';
4796
                include_once $baspath . '/tmplvars.commands.inc.php';
4797
4798
                for ($i = 0; $i < count($result); $i++) {
4799
                    $row = $result[$i];
4800
4801
                    if (!isset($row['id']) or !$row['id']) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
4802
                        $output[$row['name']] = $row['value'];
4803
                    } else {
4804
                        $output[$row['name']] = getTVDisplayFormat($row['name'], $row['value'], $row['display'], $row['display_params'], $row['type'], $docid, $sep);
4805
                    }
4806
                }
4807
4808
                return $output;
4809
            }
4810
        }
4811
    }
4812
4813
    /**
4814
     * Returns the full table name based on db settings
4815
     *
4816
     * @param string $tbl Table name
4817
     * @return string Table name with prefix
4818
     */
4819
    public function getFullTableName($tbl)
4820
    {
4821
        return $this->db->config['dbase'] . ".`" . $this->db->config['table_prefix'] . $tbl . "`";
4822
    }
4823
4824
    /**
4825
     * Returns the placeholder value
4826
     *
4827
     * @param string $name Placeholder name
4828
     * @return string Placeholder value
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
4829
     */
4830
    public function getPlaceholder($name)
4831
    {
4832
        return isset($this->placeholders[$name]) ? $this->placeholders[$name] : null;
4833
    }
4834
4835
    /**
4836
     * Sets a value for a placeholder
4837
     *
4838
     * @param string $name The name of the placeholder
4839
     * @param string $value The value of the placeholder
4840
     */
4841
    public function setPlaceholder($name, $value)
4842
    {
4843
        $this->placeholders[$name] = $value;
4844
    }
4845
4846
    /**
4847
     * Set placeholders en masse via an array or object.
4848
     *
4849
     * @param object|array $subject
4850
     * @param string $prefix
4851
     */
4852
    public function toPlaceholders($subject, $prefix = '')
4853
    {
4854
        if (is_object($subject)) {
4855
            $subject = get_object_vars($subject);
4856
        }
4857
        if (is_array($subject)) {
4858
            foreach ($subject as $key => $value) {
4859
                $this->toPlaceholder($key, $value, $prefix);
4860
            }
4861
        }
4862
    }
4863
4864
    /**
4865
     * For use by toPlaceholders(); For setting an array or object element as placeholder.
4866
     *
4867
     * @param string $key
4868
     * @param object|array $value
4869
     * @param string $prefix
4870
     */
4871
    public function toPlaceholder($key, $value, $prefix = '')
4872
    {
4873
        if (is_array($value) || is_object($value)) {
4874
            $this->toPlaceholders($value, "{$prefix}{$key}.");
4875
        } else {
4876
            $this->setPlaceholder("{$prefix}{$key}", $value);
4877
        }
4878
    }
4879
4880
    /**
4881
     * Returns the manager relative URL/path with respect to the site root.
4882
     *
4883
     * @global string $base_url
4884
     * @return string The complete URL to the manager folder
4885
     */
4886
    public function getManagerPath()
4887
    {
4888
        return MODX_MANAGER_URL;
4889
    }
4890
4891
    /**
4892
     * Returns the cache relative URL/path with respect to the site root.
4893
     *
4894
     * @global string $base_url
4895
     * @return string The complete URL to the cache folder
4896
     */
4897
    public function getCachePath()
4898
    {
4899
        global $base_url;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
4900
        $pth = $base_url . $this->getCacheFolder();
4901
        return $pth;
4902
    }
4903
4904
    /**
4905
     * Sends a message to a user's message box.
4906
     *
4907
     * @param string $type Type of the message
4908
     * @param string $to The recipient of the message
4909
     * @param string $from The sender of the message
4910
     * @param string $subject The subject of the message
4911
     * @param string $msg The message body
4912
     * @param int $private Whether it is a private message, or not
4913
     *                     Default : 0
4914
     */
4915
    public function sendAlert($type, $to, $from, $subject, $msg, $private = 0)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $to. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Coding Style introduced by
sendAlert uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
4916
    {
4917
        $private = ($private) ? 1 : 0;
4918 View Code Duplication
        if (!is_numeric($to)) {
4919
            // Query for the To ID
4920
            $rs = $this->db->select('id', $this->getFullTableName("manager_users"), "username='{$to}'");
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
4921
            $to = $this->db->getValue($rs);
4922
        }
4923 View Code Duplication
        if (!is_numeric($from)) {
4924
            // Query for the From ID
4925
            $rs = $this->db->select('id', $this->getFullTableName("manager_users"), "username='{$from}'");
4926
            $from = $this->db->getValue($rs);
4927
        }
4928
        // insert a new message into user_messages
4929
        $this->db->insert(array(
4930
            'type' => $type,
4931
            'subject' => $subject,
4932
            'message' => $msg,
4933
            'sender' => $from,
4934
            'recipient' => $to,
4935
            'private' => $private,
4936
            'postdate' => $_SERVER['REQUEST_TIME'] + $this->config['server_offset_time'],
4937
            'messageread' => 0,
4938
        ), $this->getFullTableName('user_messages'));
4939
    }
4940
4941
    /**
4942
     * Returns current user id.
4943
     *
4944
     * @param string $context . Default is an empty string which indicates the method should automatically pick 'web (frontend) or 'mgr' (backend)
4945
     * @return string
4946
     */
4947 View Code Duplication
    public function getLoginUserID($context = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
getLoginUserID uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
4948
    {
4949
        $out = false;
4950
4951
        if (!empty($context)) {
4952
            if (is_scalar($context) && isset($_SESSION[$context . 'Validated'])) {
4953
                $out = $_SESSION[$context . 'InternalKey'];
4954
            }
4955
        } else {
4956
            switch (true) {
4957
                case ($this->isFrontend() && isset ($_SESSION['webValidated'])): {
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
4958
                    $out = $_SESSION['webInternalKey'];
4959
                    break;
4960
                }
4961
                case ($this->isBackend() && isset ($_SESSION['mgrValidated'])): {
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
4962
                    $out = $_SESSION['mgrInternalKey'];
4963
                    break;
4964
                }
4965
            }
4966
        }
4967
        return $out;
4968
    }
4969
4970
    /**
4971
     * Returns current user name
4972
     *
4973
     * @param string $context . Default is an empty string which indicates the method should automatically pick 'web (frontend) or 'mgr' (backend)
4974
     * @return string
4975
     */
4976 View Code Duplication
    public function getLoginUserName($context = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
getLoginUserName uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
4977
    {
4978
        $out = false;
4979
4980
        if (!empty($context)) {
4981
            if (is_scalar($context) && isset($_SESSION[$context . 'Validated'])) {
4982
                $out = $_SESSION[$context . 'Shortname'];
4983
            }
4984
        } else {
4985
            switch (true) {
4986
                case ($this->isFrontend() && isset ($_SESSION['webValidated'])): {
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
4987
                    $out = $_SESSION['webShortname'];
4988
                    break;
4989
                }
4990
                case ($this->isBackend() && isset ($_SESSION['mgrValidated'])): {
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
4991
                    $out = $_SESSION['mgrShortname'];
4992
                    break;
4993
                }
4994
            }
4995
        }
4996
        return $out;
4997
    }
4998
4999
    /**
5000
     * Returns current login user type - web or manager
5001
     *
5002
     * @return string
5003
     */
5004
    public function getLoginUserType()
0 ignored issues
show
Coding Style introduced by
getLoginUserType uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
5005
    {
5006
        if ($this->isFrontend() && isset ($_SESSION['webValidated'])) {
5007
            return 'web';
5008
        } elseif ($this->isBackend() && isset ($_SESSION['mgrValidated'])) {
5009
            return 'manager';
5010
        } else {
5011
            return '';
5012
        }
5013
    }
5014
5015
    /**
5016
     * Returns a user info record for the given manager user
5017
     *
5018
     * @param int $uid
5019
     * @return boolean|string
5020
     */
5021
    public function getUserInfo($uid)
5022
    {
5023
        if (isset($this->tmpCache[__FUNCTION__][$uid])) {
5024
            return $this->tmpCache[__FUNCTION__][$uid];
5025
        }
5026
5027
        $from = '[+prefix+]manager_users mu INNER JOIN [+prefix+]user_attributes mua ON mua.internalkey=mu.id';
5028
        $where = sprintf("mu.id='%s'", $this->db->escape($uid));
5029
        $rs = $this->db->select('mu.username, mu.password, mua.*', $from, $where, '', 1);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5030
5031
        if (!$this->db->getRecordCount($rs)) {
5032
            return $this->tmpCache[__FUNCTION__][$uid] = false;
5033
        }
5034
5035
        $row = $this->db->getRow($rs);
5036 View Code Duplication
        if (!isset($row['usertype']) || !$row['usertype']) {
5037
            $row['usertype'] = 'manager';
5038
        }
5039
5040
        $this->tmpCache[__FUNCTION__][$uid] = $row;
5041
5042
        return $row;
5043
    }
5044
5045
    /**
5046
     * Returns a record for the web user
5047
     *
5048
     * @param int $uid
5049
     * @return boolean|string
5050
     */
5051
    public function getWebUserInfo($uid)
5052
    {
5053
        $rs = $this->db->select('wu.username, wu.password, wua.*', $this->getFullTableName("web_users") . " wu
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5054
                INNER JOIN " . $this->getFullTableName("web_user_attributes") . " wua ON wua.internalkey=wu.id", "wu.id='{$uid}'");
5055
        if ($row = $this->db->getRow($rs)) {
5056 View Code Duplication
            if (!isset($row['usertype']) or !$row["usertype"]) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
5057
                $row["usertype"] = "web";
5058
            }
5059
            return $row;
5060
        }
5061
    }
5062
5063
    /**
5064
     * Returns an array of document groups that current user is assigned to.
5065
     * This function will first return the web user doc groups when running from
5066
     * frontend otherwise it will return manager user's docgroup.
5067
     *
5068
     * @param boolean $resolveIds Set to true to return the document group names
5069
     *                            Default: false
5070
     * @return string|array
5071
     */
5072
    public function getUserDocGroups($resolveIds = false)
0 ignored issues
show
Coding Style introduced by
getUserDocGroups uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
5073
    {
5074
        if ($this->isFrontend() && isset($_SESSION['webDocgroups']) && isset($_SESSION['webValidated'])) {
5075
            $dg = $_SESSION['webDocgroups'];
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $dg. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5076
            $dgn = isset($_SESSION['webDocgrpNames']) ? $_SESSION['webDocgrpNames'] : false;
5077
        } else if ($this->isBackend() && isset($_SESSION['mgrDocgroups']) && isset($_SESSION['mgrValidated'])) {
5078
            $dg = $_SESSION['mgrDocgroups'];
5079
            $dgn = isset($_SESSION['mgrDocgrpNames']) ? $_SESSION['mgrDocgrpNames'] : false;
5080
        } else {
5081
            $dg = '';
5082
        }
5083
        if (!$resolveIds) {
5084
            return $dg;
5085
        } else if (is_array($dgn)) {
5086
            return $dgn;
0 ignored issues
show
Bug introduced by
The variable $dgn 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...
5087
        } else if (is_array($dg)) {
5088
            // resolve ids to names
5089
            $dgn = array();
5090
            $ds = $this->db->select('name', $this->getFullTableName("documentgroup_names"), "id IN (" . implode(",", $dg) . ")");
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ds. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5091
            while ($row = $this->db->getRow($ds)) {
5092
                $dgn[] = $row['name'];
5093
            }
5094
            // cache docgroup names to session
5095
            if ($this->isFrontend()) {
5096
                $_SESSION['webDocgrpNames'] = $dgn;
5097
            } else {
5098
                $_SESSION['mgrDocgrpNames'] = $dgn;
5099
            }
5100
            return $dgn;
5101
        }
5102
    }
5103
5104
    /**
5105
     * Change current web user's password
5106
     *
5107
     * @todo Make password length configurable, allow rules for passwords and translation of messages
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
5108
     * @param string $oldPwd
5109
     * @param string $newPwd
5110
     * @return string|boolean Returns true if successful, oterhwise return error
5111
     *                        message
5112
     */
5113
    public function changeWebUserPassword($oldPwd, $newPwd)
0 ignored issues
show
Coding Style introduced by
changeWebUserPassword uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
5114
    {
5115
        $rt = false;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rt. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5116
        if ($_SESSION["webValidated"] == 1) {
5117
            $tbl = $this->getFullTableName("web_users");
5118
            $ds = $this->db->select('id, username, password', $tbl, "id='" . $this->getLoginUserID() . "'");
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ds. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5119
            if ($row = $this->db->getRow($ds)) {
5120
                if ($row["password"] == md5($oldPwd)) {
5121
                    if (strlen($newPwd) < 6) {
5122
                        return "Password is too short!";
5123
                    } elseif ($newPwd == "") {
5124
                        return "You didn't specify a password for this user!";
5125
                    } else {
5126
                        $this->db->update(array(
5127
                            'password' => $this->db->escape($newPwd),
5128
                        ), $tbl, "id='" . $this->getLoginUserID() . "'");
5129
                        // invoke OnWebChangePassword event
5130
                        $this->invokeEvent("OnWebChangePassword", array(
5131
                            "userid" => $row["id"],
5132
                            "username" => $row["username"],
5133
                            "userpassword" => $newPwd
5134
                        ));
5135
                        return true;
5136
                    }
5137
                } else {
5138
                    return "Incorrect password.";
5139
                }
5140
            }
5141
        }
5142
        return $rt;
5143
    }
5144
5145
    /**
5146
     * Returns true if the current web user is a member the specified groups
5147
     *
5148
     * @param array $groupNames
5149
     * @return boolean
5150
     */
5151
    public function isMemberOfWebGroup($groupNames = array())
0 ignored issues
show
Coding Style introduced by
isMemberOfWebGroup uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
5152
    {
5153
        if (!is_array($groupNames)) {
5154
            return false;
5155
        }
5156
        // check cache
5157
        $grpNames = isset ($_SESSION['webUserGroupNames']) ? $_SESSION['webUserGroupNames'] : false;
5158
        if (!is_array($grpNames)) {
5159
            $rs = $this->db->select('wgn.name', $this->getFullTableName("webgroup_names") . " wgn
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5160
                    INNER JOIN " . $this->getFullTableName("web_groups") . " wg ON wg.webgroup=wgn.id AND wg.webuser='" . $this->getLoginUserID() . "'");
5161
            $grpNames = $this->db->getColumn("name", $rs);
5162
            // save to cache
5163
            $_SESSION['webUserGroupNames'] = $grpNames;
5164
        }
5165
        foreach ($groupNames as $k => $v) {
5166
            if (in_array(trim($v), $grpNames)) {
5167
                return true;
5168
            }
5169
        }
5170
        return false;
5171
    }
5172
5173
    /**
5174
     * Registers Client-side CSS scripts - these scripts are loaded at inside
5175
     * the <head> tag
5176
     *
5177
     * @param string $src
5178
     * @param string $media Default: Empty string
5179
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
5180
     */
5181
    public function regClientCSS($src, $media = '')
5182
    {
5183
        if (empty($src) || isset ($this->loadedjscripts[$src])) {
5184
            return '';
5185
        }
5186
        $nextpos = max(array_merge(array(0), array_keys($this->sjscripts))) + 1;
5187
        $this->loadedjscripts[$src]['startup'] = true;
5188
        $this->loadedjscripts[$src]['version'] = '0';
5189
        $this->loadedjscripts[$src]['pos'] = $nextpos;
5190
        if (strpos(strtolower($src), "<style") !== false || strpos(strtolower($src), "<link") !== false) {
5191
            $this->sjscripts[$nextpos] = $src;
5192
        } else {
5193
            $this->sjscripts[$nextpos] = "\t" . '<link rel="stylesheet" type="text/css" href="' . $src . '" ' . ($media ? 'media="' . $media . '" ' : '') . '/>';
5194
        }
5195
    }
5196
5197
    /**
5198
     * Registers Startup Client-side JavaScript - these scripts are loaded at inside the <head> tag
5199
     *
5200
     * @param string $src
5201
     * @param array $options Default: 'name'=>'', 'version'=>'0', 'plaintext'=>false
5202
     */
5203
    public function regClientStartupScript($src, $options = array('name' => '', 'version' => '0', 'plaintext' => false))
5204
    {
5205
        $this->regClientScript($src, $options, true);
5206
    }
5207
5208
    /**
5209
     * Registers Client-side JavaScript these scripts are loaded at the end of the page unless $startup is true
5210
     *
5211
     * @param string $src
5212
     * @param array $options Default: 'name'=>'', 'version'=>'0', 'plaintext'=>false
5213
     * @param boolean $startup Default: false
5214
     * @return string
5215
     */
5216
    public function regClientScript($src, $options = array('name' => '', 'version' => '0', 'plaintext' => false), $startup = false)
5217
    {
5218
        if (empty($src)) {
5219
            return '';
5220
        } // nothing to register
5221
        if (!is_array($options)) {
5222
            if (is_bool($options))  // backward compatibility with old plaintext parameter
5223
            {
5224
                $options = array('plaintext' => $options);
5225
            } elseif (is_string($options)) // Also allow script name as 2nd param
5226
            {
5227
                $options = array('name' => $options);
5228
            } else {
5229
                $options = array();
5230
            }
5231
        }
5232
        $name = isset($options['name']) ? strtolower($options['name']) : '';
5233
        $version = isset($options['version']) ? $options['version'] : '0';
5234
        $plaintext = isset($options['plaintext']) ? $options['plaintext'] : false;
5235
        $key = !empty($name) ? $name : $src;
5236
        unset($overwritepos); // probably unnecessary--just making sure
5237
5238
        $useThisVer = true;
5239
        if (isset($this->loadedjscripts[$key])) { // a matching script was found
5240
            // if existing script is a startup script, make sure the candidate is also a startup script
5241
            if ($this->loadedjscripts[$key]['startup']) {
5242
                $startup = true;
5243
            }
5244
5245
            if (empty($name)) {
5246
                $useThisVer = false; // if the match was based on identical source code, no need to replace the old one
5247
            } else {
5248
                $useThisVer = version_compare($this->loadedjscripts[$key]['version'], $version, '<');
5249
            }
5250
5251
            if ($useThisVer) {
5252
                if ($startup == true && $this->loadedjscripts[$key]['startup'] == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
5253
                    // remove old script from the bottom of the page (new one will be at the top)
5254
                    unset($this->jscripts[$this->loadedjscripts[$key]['pos']]);
5255
                } else {
5256
                    // overwrite the old script (the position may be important for dependent scripts)
5257
                    $overwritepos = $this->loadedjscripts[$key]['pos'];
5258
                }
5259
            } else { // Use the original version
5260
                if ($startup == true && $this->loadedjscripts[$key]['startup'] == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
5261
                    // need to move the exisiting script to the head
5262
                    $version = $this->loadedjscripts[$key][$version];
5263
                    $src = $this->jscripts[$this->loadedjscripts[$key]['pos']];
5264
                    unset($this->jscripts[$this->loadedjscripts[$key]['pos']]);
5265
                } else {
5266
                    return ''; // the script is already in the right place
5267
                }
5268
            }
5269
        }
5270
5271
        if ($useThisVer && $plaintext != true && (strpos(strtolower($src), "<script") === false)) {
5272
            $src = "\t" . '<script type="text/javascript" src="' . $src . '"></script>';
5273
        }
5274
        if ($startup) {
5275
            $pos = isset($overwritepos) ? $overwritepos : max(array_merge(array(0), array_keys($this->sjscripts))) + 1;
5276
            $this->sjscripts[$pos] = $src;
5277
        } else {
5278
            $pos = isset($overwritepos) ? $overwritepos : max(array_merge(array(0), array_keys($this->jscripts))) + 1;
5279
            $this->jscripts[$pos] = $src;
5280
        }
5281
        $this->loadedjscripts[$key]['version'] = $version;
5282
        $this->loadedjscripts[$key]['startup'] = $startup;
5283
        $this->loadedjscripts[$key]['pos'] = $pos;
5284
        return '';
5285
    }
5286
5287
    /**
5288
     * Returns all registered JavaScripts
5289
     *
5290
     * @return string
5291
     */
5292
    public function regClientStartupHTMLBlock($html)
5293
    {
5294
        return $this->regClientScript($html, true, true);
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a array.

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...
5295
    }
5296
5297
    /**
5298
     * Returns all registered startup scripts
5299
     *
5300
     * @return string
5301
     */
5302
    public function regClientHTMLBlock($html)
5303
    {
5304
        return $this->regClientScript($html, true);
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a array.

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...
5305
    }
5306
5307
    /**
5308
     * Remove unwanted html tags and snippet, settings and tags
5309
     *
5310
     * @param string $html
5311
     * @param string $allowed Default: Empty string
5312
     * @return string
5313
     */
5314
    public function stripTags($html, $allowed = "")
5315
    {
5316
        $t = strip_tags($html, $allowed);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $t. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5317
        $t = preg_replace('~\[\*(.*?)\*\]~', "", $t); //tv
5318
        $t = preg_replace('~\[\[(.*?)\]\]~', "", $t); //snippet
5319
        $t = preg_replace('~\[\!(.*?)\!\]~', "", $t); //snippet
5320
        $t = preg_replace('~\[\((.*?)\)\]~', "", $t); //settings
5321
        $t = preg_replace('~\[\+(.*?)\+\]~', "", $t); //placeholders
5322
        $t = preg_replace('~{{(.*?)}}~', "", $t); //chunks
5323
        return $t;
5324
    }
5325
5326
    /**
5327
     * Add an event listener to a plugin - only for use within the current execution cycle
5328
     *
5329
     * @param string $evtName
5330
     * @param string $pluginName
5331
     * @return boolean|int
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use false|integer.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
5332
     */
5333
    public function addEventListener($evtName, $pluginName)
5334
    {
5335
        if (!$evtName || !$pluginName) {
5336
            return false;
5337
        }
5338
        if (!array_key_exists($evtName, $this->pluginEvent)) {
5339
            $this->pluginEvent[$evtName] = array();
5340
        }
5341
        return array_push($this->pluginEvent[$evtName], $pluginName); // return array count
5342
    }
5343
5344
    /**
5345
     * Remove event listener - only for use within the current execution cycle
5346
     *
5347
     * @param string $evtName
5348
     * @return boolean
0 ignored issues
show
Documentation introduced by
Should the return type not be false|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
5349
     */
5350
    public function removeEventListener($evtName)
5351
    {
5352
        if (!$evtName) {
5353
            return false;
5354
        }
5355
        unset ($this->pluginEvent[$evtName]);
5356
    }
5357
5358
    /**
5359
     * Remove all event listeners - only for use within the current execution cycle
5360
     */
5361
    public function removeAllEventListener()
5362
    {
5363
        unset ($this->pluginEvent);
5364
        $this->pluginEvent = array();
5365
    }
5366
5367
    /**
5368
     * Invoke an event.
5369
     *
5370
     * @param string $evtName
5371
     * @param array $extParams Parameters available to plugins. Each array key will be the PHP variable name, and the array value will be the variable value.
5372
     * @return boolean|array
0 ignored issues
show
Documentation introduced by
Should the return type not be false|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
5373
     */
5374
    public function invokeEvent($evtName, $extParams = array())
5375
    {
5376
        if (!$evtName) {
5377
            return false;
5378
        }
5379
        if (!isset ($this->pluginEvent[$evtName])) {
5380
            return false;
5381
        }
5382
5383
        $results = null;
5384
        foreach ($this->pluginEvent[$evtName] as $pluginName) { // start for loop
5385
            if ($this->dumpPlugins) {
5386
                $eventtime = $this->getMicroTime();
5387
            }
5388
            // reset event object
5389
            $e = &$this->event;
5390
            $e->_resetEventObject();
5391
            $e->name = $evtName;
5392
            $e->activePlugin = $pluginName;
5393
5394
            // get plugin code
5395
            $_ = $this->getPluginCode($pluginName);
5396
            $pluginCode = $_['code'];
5397
            $pluginProperties = $_['props'];
5398
5399
            // load default params/properties
5400
            $parameter = $this->parseProperties($pluginProperties);
5401
            if (!is_array($parameter)) {
5402
                $parameter = array();
5403
            }
5404
            if (!empty($extParams)) {
5405
                $parameter = array_merge($parameter, $extParams);
5406
            }
5407
5408
            // eval plugin
5409
            $this->evalPlugin($pluginCode, $parameter);
5410
5411
            if (class_exists('PHxParser')) {
5412
                $this->config['enable_filter'] = 0;
5413
            }
5414
5415
            if ($this->dumpPlugins) {
5416
                $eventtime = $this->getMicroTime() - $eventtime;
0 ignored issues
show
Bug introduced by
The variable $eventtime 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...
5417
                $this->pluginsCode .= sprintf('<fieldset><legend><b>%s / %s</b> (%2.2f ms)</legend>', $evtName, $pluginName, $eventtime * 1000);
5418
                foreach ($parameter as $k => $v) {
5419
                    $this->pluginsCode .= "{$k} => " . print_r($v, true) . '<br>';
5420
                }
5421
                $this->pluginsCode .= '</fieldset><br />';
5422
                $this->pluginsTime["{$evtName} / {$pluginName}"] += $eventtime;
5423
            }
5424
            if ($e->_output != '') {
5425
                $results[] = $e->_output;
5426
            }
5427
            if ($e->_propagate != true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
5428
                break;
5429
            }
5430
        }
5431
5432
        $e->activePlugin = '';
0 ignored issues
show
Bug introduced by
The variable $e 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...
5433
        return $results;
5434
    }
5435
5436
    /**
5437
     * Returns plugin-code and properties
5438
     *
5439
     * @param string $pluginName
5440
     * @return array Associative array consisting of 'code' and 'props'
5441
     */
5442
    public function getPluginCode($pluginName)
5443
    {
5444
        $plugin = array();
5445
        if (isset ($this->pluginCache[$pluginName])) {
5446
            $pluginCode = $this->pluginCache[$pluginName];
5447
            $pluginProperties = isset($this->pluginCache[$pluginName . "Props"]) ? $this->pluginCache[$pluginName . "Props"] : '';
5448
        } else {
5449
            $pluginName = $this->db->escape($pluginName);
5450
            $result = $this->db->select('name, plugincode, properties', $this->getFullTableName("site_plugins"), "name='{$pluginName}' AND disabled=0");
5451
            if ($row = $this->db->getRow($result)) {
5452
                $pluginCode = $this->pluginCache[$row['name']] = $row['plugincode'];
5453
                $pluginProperties = $this->pluginCache[$row['name'] . "Props"] = $row['properties'];
5454
            } else {
5455
                $pluginCode = $this->pluginCache[$pluginName] = "return false;";
5456
                $pluginProperties = '';
5457
            }
5458
        }
5459
        $plugin['code'] = $pluginCode;
5460
        $plugin['props'] = $pluginProperties;
5461
5462
        return $plugin;
5463
    }
5464
5465
    /**
5466
     * Parses a resource property string and returns the result as an array
5467
     *
5468
     * @param string $propertyString
5469
     * @param string|null $elementName
5470
     * @param string|null $elementType
5471
     * @return array Associative array in the form property name => property value
5472
     */
5473
    public function parseProperties($propertyString, $elementName = null, $elementType = null)
5474
    {
5475
        $propertyString = trim($propertyString);
5476
        $propertyString = str_replace('{}', '', $propertyString);
5477
        $propertyString = str_replace('} {', ',', $propertyString);
5478
        if (empty($propertyString)) {
5479
            return array();
5480
        }
5481
        if ($propertyString == '{}') {
5482
            return array();
5483
        }
5484
5485
        $jsonFormat = $this->isJson($propertyString, true);
5486
        $property = array();
5487
        // old format
5488
        if ($jsonFormat === false) {
5489
            $props = explode('&', $propertyString);
5490
            foreach ($props as $prop) {
5491
5492
                if (empty($prop)) {
5493
                    continue;
5494
                } elseif (strpos($prop, '=') === false) {
5495
                    $property[trim($prop)] = '';
5496
                    continue;
5497
                }
5498
5499
                $_ = explode('=', $prop, 2);
5500
                $key = trim($_[0]);
5501
                $p = explode(';', trim($_[1]));
5502
                switch ($p[1]) {
5503
                    case 'list':
5504
                    case 'list-multi':
5505
                    case 'checkbox':
5506
                    case 'radio':
5507
                        $value = !isset($p[3]) ? '' : $p[3];
5508
                        break;
5509
                    default:
5510
                        $value = !isset($p[2]) ? '' : $p[2];
5511
                }
5512
                if (!empty($key)) {
5513
                    $property[$key] = $value;
5514
                }
5515
            }
5516
            // new json-format
5517
        } else if (!empty($jsonFormat)) {
5518
            foreach ($jsonFormat as $key => $row) {
5519
                if (!empty($key)) {
5520
                    if (is_array($row)) {
5521
                        if (isset($row[0]['value'])) {
5522
                            $value = $row[0]['value'];
5523
                        }
5524
                    } else {
5525
                        $value = $row;
5526
                    }
5527
                    if (isset($value) && $value !== '') {
5528
                        $property[$key] = $value;
5529
                    }
5530
                }
5531
            }
5532
        }
5533
        if (!empty($elementName) && !empty($elementType)) {
5534
            $out = $this->invokeEvent('OnParseProperties', array(
5535
                'element' => $elementName,
5536
                'type' => $elementType,
5537
                'args' => $property
5538
            ));
5539
            if (is_array($out)) {
5540
                $out = array_pop($out);
5541
            }
5542
            if (is_array($out)) {
5543
                $property = $out;
5544
            }
5545
        }
5546
        return $property;
5547
    }
5548
5549
    /**
5550
     * Parses docBlock from a file and returns the result as an array
5551
     *
5552
     * @param string $element_dir
5553
     * @param string $filename
5554
     * @param boolean $escapeValues
5555
     * @return array Associative array in the form property name => property value
5556
     */
5557
    public function parseDocBlockFromFile($element_dir, $filename, $escapeValues = false)
0 ignored issues
show
Coding Style Naming introduced by
The parameter $element_dir is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
5558
    {
5559
        $params = array();
5560
        $fullpath = $element_dir . '/' . $filename;
5561
        if (is_readable($fullpath)) {
5562
            $tpl = @fopen($fullpath, "r");
5563
            if ($tpl) {
5564
                $params['filename'] = $filename;
5565
                $docblock_start_found = false;
5566
                $name_found = false;
5567
                $description_found = false;
5568
                $docblock_end_found = false;
5569
                $arrayParams = array('author', 'documentation', 'reportissues', 'link');
5570
5571
                while (!feof($tpl)) {
5572
                    $line = fgets($tpl);
5573
                    $r = $this->parseDocBlockLine($line, $docblock_start_found, $name_found, $description_found, $docblock_end_found);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $r. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5574
                    $docblock_start_found = $r['docblock_start_found'];
5575
                    $name_found = $r['name_found'];
5576
                    $description_found = $r['description_found'];
5577
                    $docblock_end_found = $r['docblock_end_found'];
5578
                    $param = $r['param'];
5579
                    $val = $r['val'];
5580
                    if (!$docblock_end_found) {
5581
                        break;
5582
                    }
5583
                    if (!$docblock_start_found || !$name_found || !$description_found || empty($param)) {
5584
                        continue;
5585
                    }
5586 View Code Duplication
                    if (!empty($param)) {
5587
                        if (in_array($param, $arrayParams)) {
5588
                            if (!isset($params[$param])) {
5589
                                $params[$param] = array();
5590
                            }
5591
                            $params[$param][] = $escapeValues ? $this->db->escape($val) : $val;
5592
                        } else {
5593
                            $params[$param] = $escapeValues ? $this->db->escape($val) : $val;
5594
                        }
5595
                    }
5596
                }
5597
                @fclose($tpl);
5598
            }
5599
        }
5600
        return $params;
5601
    }
5602
5603
    /**
5604
     * Parses docBlock from string and returns the result as an array
5605
     *
5606
     * @param string $string
5607
     * @param boolean $escapeValues
5608
     * @return array Associative array in the form property name => property value
5609
     */
5610
    public function parseDocBlockFromString($string, $escapeValues = false)
5611
    {
5612
        $params = array();
5613
        if (!empty($string)) {
5614
            $string = str_replace('\r\n', '\n', $string);
5615
            $exp = explode('\n', $string);
5616
            $docblock_start_found = false;
5617
            $name_found = false;
5618
            $description_found = false;
5619
            $docblock_end_found = false;
5620
            $arrayParams = array('author', 'documentation', 'reportissues', 'link');
5621
5622
            foreach ($exp as $line) {
5623
                $r = $this->parseDocBlockLine($line, $docblock_start_found, $name_found, $description_found, $docblock_end_found);
5624
                $docblock_start_found = $r['docblock_start_found'];
5625
                $name_found = $r['name_found'];
5626
                $description_found = $r['description_found'];
5627
                $docblock_end_found = $r['docblock_end_found'];
5628
                $param = $r['param'];
5629
                $val = $r['val'];
5630
                if (!$docblock_start_found) {
5631
                    continue;
5632
                }
5633
                if ($docblock_end_found) {
5634
                    break;
5635
                }
5636 View Code Duplication
                if (!empty($param)) {
5637
                    if (in_array($param, $arrayParams)) {
5638
                        if (!isset($params[$param])) {
5639
                            $params[$param] = array();
5640
                        }
5641
                        $params[$param][] = $escapeValues ? $this->db->escape($val) : $val;
5642
                    } else {
5643
                        $params[$param] = $escapeValues ? $this->db->escape($val) : $val;
5644
                    }
5645
                }
5646
            }
5647
        }
5648
        return $params;
5649
    }
5650
5651
    /**
5652
     * Parses docBlock of a component´s source-code and returns the result as an array
5653
     * (modified parseDocBlock() from modules/stores/setup.info.php by Bumkaka & Dmi3yy)
5654
     *
5655
     * @param string $line
5656
     * @param boolean $docblock_start_found
5657
     * @param boolean $name_found
5658
     * @param boolean $description_found
5659
     * @param boolean $docblock_end_found
5660
     * @return array Associative array in the form property name => property value
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,boolean|string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
5661
     */
5662
    public function parseDocBlockLine($line, $docblock_start_found, $name_found, $description_found, $docblock_end_found)
0 ignored issues
show
Coding Style Naming introduced by
The parameter $docblock_start_found is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style Naming introduced by
The parameter $name_found is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style Naming introduced by
The parameter $description_found is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style Naming introduced by
The parameter $docblock_end_found is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
5663
    {
5664
        $param = '';
5665
        $val = '';
5666
        $ma = null;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ma. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5667
        if (!$docblock_start_found) {
5668
            // find docblock start
5669
            if (strpos($line, '/**') !== false) {
5670
                $docblock_start_found = true;
5671
            }
5672
        } elseif (!$name_found) {
5673
            // find name
5674
            if (preg_match("/^\s+\*\s+(.+)/", $line, $ma)) {
5675
                $param = 'name';
5676
                $val = trim($ma[1]);
5677
                $name_found = !empty($val);
5678
            }
5679
        } elseif (!$description_found) {
5680
            // find description
5681
            if (preg_match("/^\s+\*\s+(.+)/", $line, $ma)) {
5682
                $param = 'description';
5683
                $val = trim($ma[1]);
5684
                $description_found = !empty($val);
5685
            }
5686
        } else {
5687
            if (preg_match("/^\s+\*\s+\@([^\s]+)\s+(.+)/", $line, $ma)) {
5688
                $param = trim($ma[1]);
5689
                $val = trim($ma[2]);
5690
                if (!empty($param) && !empty($val)) {
5691
                    if ($param == 'internal') {
5692
                        $ma = null;
5693
                        if (preg_match("/\@([^\s]+)\s+(.+)/", $val, $ma)) {
5694
                            $param = trim($ma[1]);
5695
                            $val = trim($ma[2]);
5696
                        }
5697
                    }
5698
                }
5699
            } elseif (preg_match("/^\s*\*\/\s*$/", $line)) {
5700
                $docblock_end_found = true;
5701
            }
5702
        }
5703
        return array(
5704
            'docblock_start_found' => $docblock_start_found,
5705
            'name_found' => $name_found,
5706
            'description_found' => $description_found,
5707
            'docblock_end_found' => $docblock_end_found,
5708
            'param' => $param,
5709
            'val' => $val
5710
        );
5711
    }
5712
5713
    /**
5714
     * Renders docBlock-parameters into human readable list
5715
     *
5716
     * @param array $parsed
5717
     * @return string List in HTML-format
5718
     */
5719
    public function convertDocBlockIntoList($parsed)
5720
    {
5721
        global $_lang;
5722
5723
        // Replace special placeholders & make URLs + Emails clickable
5724
        $ph = array('site_url' => MODX_SITE_URL);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ph. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5725
        $regexUrl = "/((http|https|ftp|ftps)\:\/\/[^\/]+(\/[^\s]+[^,.?!:;\s])?)/";
5726
        $regexEmail = '#([0-9a-z]([-_.]?[0-9a-z])*@[0-9a-z]([-.]?[0-9a-z])*\\.[a-wyz][a-z](fo|g|l|m|mes|o|op|pa|ro|seum|t|u|v|z)?)#i';
5727
        $emailSubject = isset($parsed['name']) ? '?subject=' . $parsed['name'] : '';
5728
        $emailSubject .= isset($parsed['version']) ? ' v' . $parsed['version'] : '';
5729
        foreach ($parsed as $key => $val) {
5730
            if (is_array($val)) {
5731
                foreach ($val as $key2 => $val2) {
5732
                    $val2 = $this->parseText($val2, $ph);
5733 View Code Duplication
                    if (preg_match($regexUrl, $val2, $url)) {
5734
                        $val2 = preg_replace($regexUrl, "<a href=\"{$url[0]}\" target=\"_blank\">{$url[0]}</a> ", $val2);
5735
                    }
5736 View Code Duplication
                    if (preg_match($regexEmail, $val2, $url)) {
5737
                        $val2 = preg_replace($regexEmail, '<a href="mailto:\\1' . $emailSubject . '">\\1</a>', $val2);
5738
                    }
5739
                    $parsed[$key][$key2] = $val2;
5740
                }
5741
            } else {
5742
                $val = $this->parseText($val, $ph);
5743 View Code Duplication
                if (preg_match($regexUrl, $val, $url)) {
5744
                    $val = preg_replace($regexUrl, "<a href=\"{$url[0]}\" target=\"_blank\">{$url[0]}</a> ", $val);
5745
                }
5746 View Code Duplication
                if (preg_match($regexEmail, $val, $url)) {
5747
                    $val = preg_replace($regexEmail, '<a href="mailto:\\1' . $emailSubject . '">\\1</a>', $val);
5748
                }
5749
                $parsed[$key] = $val;
5750
            }
5751
        }
5752
5753
        $arrayParams = array(
5754
            'documentation' => $_lang['documentation'],
5755
            'reportissues' => $_lang['report_issues'],
5756
            'link' => $_lang['further_info'],
5757
            'author' => $_lang['author_infos']
5758
        );
5759
5760
        $nl = "\n";
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $nl. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5761
        $list = isset($parsed['logo']) ? '<img src="' . $this->config['base_url'] . ltrim($parsed['logo'], "/") . '" style="float:right;max-width:100px;height:auto;" />' . $nl : '';
5762
        $list .= '<p>' . $nl;
5763
        $list .= isset($parsed['name']) ? '<strong>' . $parsed['name'] . '</strong><br/>' . $nl : '';
5764
        $list .= isset($parsed['description']) ? $parsed['description'] . $nl : '';
5765
        $list .= '</p><br/>' . $nl;
5766
        $list .= isset($parsed['version']) ? '<p><strong>' . $_lang['version'] . ':</strong> ' . $parsed['version'] . '</p>' . $nl : '';
5767
        $list .= isset($parsed['license']) ? '<p><strong>' . $_lang['license'] . ':</strong> ' . $parsed['license'] . '</p>' . $nl : '';
5768
        $list .= isset($parsed['lastupdate']) ? '<p><strong>' . $_lang['last_update'] . ':</strong> ' . $parsed['lastupdate'] . '</p>' . $nl : '';
5769
        $list .= '<br/>' . $nl;
5770
        $first = true;
5771
        foreach ($arrayParams as $param => $label) {
5772
            if (isset($parsed[$param])) {
5773
                if ($first) {
5774
                    $list .= '<p><strong>' . $_lang['references'] . '</strong></p>' . $nl;
5775
                    $list .= '<ul class="docBlockList">' . $nl;
5776
                    $first = false;
5777
                }
5778
                $list .= '    <li><strong>' . $label . '</strong>' . $nl;
5779
                $list .= '        <ul>' . $nl;
5780
                foreach ($parsed[$param] as $val) {
5781
                    $list .= '            <li>' . $val . '</li>' . $nl;
5782
                }
5783
                $list .= '        </ul></li>' . $nl;
5784
            }
5785
        }
5786
        $list .= !$first ? '</ul>' . $nl : '';
5787
5788
        return $list;
5789
    }
5790
5791
    /**
5792
     * @param string $string
5793
     * @return string
5794
     */
5795
    public function removeSanitizeSeed($string = '')
5796
    {
5797
        global $sanitize_seed;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
5798
5799
        if (!$string || strpos($string, $sanitize_seed) === false) {
5800
            return $string;
5801
        }
5802
5803
        return str_replace($sanitize_seed, '', $string);
5804
    }
5805
5806
    /**
5807
     * @param string $content
5808
     * @return string
5809
     */
5810
    public function cleanUpMODXTags($content = '')
5811
    {
5812
        if ($this->minParserPasses < 1) {
5813
            return $content;
5814
        }
5815
5816
        $enable_filter = $this->config['enable_filter'];
5817
        $this->config['enable_filter'] = 1;
5818
        $_ = array('[* *]', '[( )]', '{{ }}', '[[ ]]', '[+ +]');
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $_. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
5819
        foreach ($_ as $brackets) {
5820
            list($left, $right) = explode(' ', $brackets);
5821
            if (strpos($content, $left) !== false) {
5822
                if ($left === '[*') {
5823
                    $content = $this->mergeDocumentContent($content);
5824
                } elseif ($left === '[(') {
5825
                    $content = $this->mergeSettingsContent($content);
5826
                } elseif ($left === '{{') {
5827
                    $content = $this->mergeChunkContent($content);
5828
                } elseif ($left === '[[') {
5829
                    $content = $this->evalSnippets($content);
5830
                }
5831
            }
5832
        }
5833
        foreach ($_ as $brackets) {
5834
            list($left, $right) = explode(' ', $brackets);
5835
            if (strpos($content, $left) !== false) {
5836
                $matches = $this->getTagsFromContent($content, $left, $right);
5837
                $content = str_replace($matches[0], '', $content);
5838
            }
5839
        }
5840
        $this->config['enable_filter'] = $enable_filter;
5841
        return $content;
5842
    }
5843
5844
    /**
5845
     * @param string $str
5846
     * @param string $allowable_tags
5847
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
5848
     */
5849
    public function strip_tags($str = '', $allowable_tags = '')
0 ignored issues
show
Coding Style Naming introduced by
The parameter $allowable_tags is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
5850
    {
5851
        $str = strip_tags($str, $allowable_tags);
5852
        modx_sanitize_gpc($str);
5853
        return $str;
5854
    }
5855
5856
    /**
5857
     * @param string $name
5858
     * @param string $phpCode
5859
     */
5860
    public function addSnippet($name, $phpCode)
5861
    {
5862
        $this->snippetCache['#' . $name] = $phpCode;
5863
    }
5864
5865
    /**
5866
     * @param string $name
5867
     * @param string $text
5868
     */
5869
    public function addChunk($name, $text)
5870
    {
5871
        $this->chunkCache['#' . $name] = $text;
5872
    }
5873
5874
    /**
5875
     * @param string $phpcode
5876
     * @param string $evalmode
5877
     * @param string $safe_functions
5878
     * @return string|void
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
5879
     */
5880
    public function safeEval($phpcode = '', $evalmode = '', $safe_functions = '')
0 ignored issues
show
Coding Style introduced by
safeEval uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
safeEval uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style Naming introduced by
The parameter $safe_functions is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
5881
    {
5882
        if ($evalmode == '') {
5883
            $evalmode = $this->config['allow_eval'];
5884
        }
5885
        if ($safe_functions == '') {
5886
            $safe_functions = $this->config['safe_functions_at_eval'];
5887
        }
5888
5889
        modx_sanitize_gpc($phpcode);
5890
5891
        switch ($evalmode) {
5892
            case 'with_scan'         :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
5893
                $isSafe = $this->isSafeCode($phpcode, $safe_functions);
0 ignored issues
show
Bug introduced by
It seems like $phpcode can also be of type array; however, DocumentParser::isSafeCode() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
5894
                break;
5895
            case 'with_scan_at_post' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
5896
                $isSafe = $_POST ? $this->isSafeCode($phpcode, $safe_functions) : true;
0 ignored issues
show
Bug introduced by
It seems like $phpcode can also be of type array; however, DocumentParser::isSafeCode() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
5897
                break;
5898
            case 'everytime_eval'    :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
5899
                $isSafe = true;
5900
                break; // Should debug only
5901
            case 'dont_eval'         :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
5902
            default                  :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
5903
                return $phpcode;
5904
        }
5905
5906
        if (!$isSafe) {
5907
            $msg = $phpcode . "\n" . $this->currentSnippet . "\n" . print_r($_SERVER, true);
5908
            $title = sprintf('Unknown eval was executed (%s)', $this->htmlspecialchars(substr(trim($phpcode), 0, 50)));
5909
            $this->messageQuit($title, '', true, '', '', 'Parser', $msg);
5910
            return;
5911
        }
5912
5913
        ob_start();
5914
        $return = eval($phpcode);
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
5915
        $echo = ob_get_clean();
5916
5917
        if (is_array($return)) {
5918
            return 'array()';
5919
        }
5920
5921
        $output = $echo . $return;
5922
        modx_sanitize_gpc($output);
5923
        return $this->htmlspecialchars($output); // Maybe, all html tags are dangerous
5924
    }
5925
5926
    /**
5927
     * @param string $phpcode
5928
     * @param string $safe_functions
5929
     * @return bool
5930
     */
5931
    public function isSafeCode($phpcode = '', $safe_functions = '')
0 ignored issues
show
Coding Style Naming introduced by
The parameter $safe_functions is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
5932
    { // return true or false
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5933
        if ($safe_functions == '') {
5934
            return false;
5935
        }
5936
5937
        $safe = explode(',', $safe_functions);
5938
5939
        $phpcode = rtrim($phpcode, ';') . ';';
5940
        $tokens = token_get_all('<?php ' . $phpcode);
5941
        foreach ($tokens as $i => $token) {
5942
            if (!is_array($token)) {
5943
                continue;
5944
            }
5945
            $tokens[$i]['token_name'] = token_name($token[0]);
5946
        }
5947
        foreach ($tokens as $token) {
5948
            if (!is_array($token)) {
5949
                continue;
5950
            }
5951
            switch ($token['token_name']) {
5952
                case 'T_STRING':
5953
                    if (!in_array($token[1], $safe)) {
5954
                        return false;
5955
                    }
5956
                    break;
5957
                case 'T_VARIABLE':
5958
                    if ($token[1] == '$GLOBALS') {
5959
                        return false;
5960
                    }
5961
                    break;
5962
                case 'T_EVAL':
5963
                    return false;
5964
            }
5965
        }
5966
        return true;
5967
    }
5968
5969
    /**
5970
     * @param string $str
5971
     * @return bool|mixed|string
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
5972
     */
5973
    public function atBindFileContent($str = '')
5974
    {
5975
5976
        $search_path = array('assets/tvs/', 'assets/chunks/', 'assets/templates/', $this->config['rb_base_url'] . 'files/', '');
5977
5978
        if (stripos($str, '@FILE') !== 0) {
5979
            return $str;
5980
        }
5981 View Code Duplication
        if (strpos($str, "\n") !== false) {
5982
            $str = substr($str, 0, strpos("\n", $str));
5983
        }
5984
5985
        if ($this->getExtFromFilename($str) === '.php') {
5986
            return 'Could not retrieve PHP file.';
5987
        }
5988
5989
        $str = substr($str, 6);
5990
        $str = trim($str);
5991
        if (strpos($str, '\\') !== false) {
5992
            $str = str_replace('\\', '/', $str);
5993
        }
5994
        $str = ltrim($str, '/');
5995
5996
        $errorMsg = sprintf("Could not retrieve string '%s'.", $str);
5997
5998
        foreach ($search_path as $path) {
5999
            $file_path = MODX_BASE_PATH . $path . $str;
6000
            if (strpos($file_path, MODX_MANAGER_PATH) === 0) {
6001
                return $errorMsg;
6002
            } elseif (is_file($file_path)) {
6003
                break;
6004
            } else {
6005
                $file_path = false;
6006
            }
6007
        }
6008
6009
        if (!$file_path) {
0 ignored issues
show
Bug introduced by
The variable $file_path 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...
Bug Best Practice introduced by
The expression $file_path of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
6010
            return $errorMsg;
6011
        }
6012
6013
        $content = (string)file_get_contents($file_path);
6014
        if ($content === false) {
6015
            return $errorMsg;
6016
        }
6017
6018
        return $content;
6019
    }
6020
6021
    /**
6022
     * @param $str
6023
     * @return bool|string
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use false|string.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
6024
     */
6025
    public function getExtFromFilename($str)
6026
    {
6027
        $str = strtolower(trim($str));
6028
        $pos = strrpos($str, '.');
6029
        if ($pos === false) {
6030
            return false;
6031
        } else {
6032
            return substr($str, $pos);
6033
        }
6034
    }
6035
    /***************************************************************************************/
6036
    /* End of API functions                                       */
6037
    /***************************************************************************************/
6038
6039
    /**
6040
     * PHP error handler set by http://www.php.net/manual/en/function.set-error-handler.php
6041
     *
6042
     * Checks the PHP error and calls messageQuit() unless:
6043
     *  - error_reporting() returns 0, or
6044
     *  - the PHP error level is 0, or
6045
     *  - the PHP error level is 8 (E_NOTICE) and stopOnNotice is false
6046
     *
6047
     * @param int $nr The PHP error level as per http://www.php.net/manual/en/errorfunc.constants.php
6048
     * @param string $text Error message
6049
     * @param string $file File where the error was detected
6050
     * @param string $line Line number within $file
6051
     * @return boolean
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
6052
     */
6053
    public function phpError($nr, $text, $file, $line)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $nr. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
6054
    {
6055
        if (error_reporting() == 0 || $nr == 0) {
6056
            return true;
6057
        }
6058
        if ($this->stopOnNotice == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
6059
            switch ($nr) {
6060
                case E_NOTICE:
6061
                    if ($this->error_reporting <= 2) {
6062
                        return true;
6063
                    }
6064
                    $isError = false;
6065
                    $msg = 'PHP Minor Problem (this message show logged in only)';
6066
                    break;
6067
                case E_STRICT:
6068 View Code Duplication
                case E_DEPRECATED:
6069
                    if ($this->error_reporting <= 1) {
6070
                        return true;
6071
                    }
6072
                    $isError = true;
6073
                    $msg = 'PHP Strict Standards Problem';
6074
                    break;
6075 View Code Duplication
                default:
6076
                    if ($this->error_reporting === 0) {
6077
                        return true;
6078
                    }
6079
                    $isError = true;
6080
                    $msg = 'PHP Parse Error';
6081
            }
6082
        }
6083
        if (is_readable($file)) {
6084
            $source = file($file);
6085
            $source = $this->htmlspecialchars($source[$line - 1]);
6086
        } else {
6087
            $source = "";
6088
        } //Error $nr in $file at $line: <div><code>$source</code></div>
6089
6090
        $this->messageQuit($msg, '', $isError, $nr, $file, $source, $text, $line);
0 ignored issues
show
Bug introduced by
The variable $msg 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...
Bug introduced by
The variable $isError 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...
6091
    }
6092
6093
    /**
6094
     * @param string $msg
6095
     * @param string $query
6096
     * @param bool $is_error
6097
     * @param string $nr
6098
     * @param string $file
6099
     * @param string $source
6100
     * @param string $text
6101
     * @param string $line
6102
     * @param string $output
6103
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be null|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
6104
     */
6105
    public function messageQuit($msg = 'unspecified error', $query = '', $is_error = true, $nr = '', $file = '', $source = '', $text = '', $line = '', $output = '')
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $nr. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Coding Style introduced by
messageQuit uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
messageQuit uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
messageQuit uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style Naming introduced by
The parameter $is_error is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
6106
    {
6107
6108
        if (0 < $this->messageQuitCount) {
6109
            return;
6110
        }
6111
        $this->messageQuitCount++;
6112
6113
        if (!class_exists('makeTable')) {
6114
            include_once('extenders/maketable.class.php');
6115
        }
6116
        $MakeTable = new MakeTable();
6117
        $MakeTable->setTableClass('grid');
6118
        $MakeTable->setRowRegularClass('gridItem');
6119
        $MakeTable->setRowAlternateClass('gridAltItem');
6120
        $MakeTable->setColumnWidths(array('100px'));
6121
6122
        $table = array();
6123
6124
        $version = isset ($GLOBALS['modx_version']) ? $GLOBALS['modx_version'] : '';
6125
        $release_date = isset ($GLOBALS['release_date']) ? $GLOBALS['release_date'] : '';
6126
        $request_uri = "http://" . $_SERVER['HTTP_HOST'] . ($_SERVER["SERVER_PORT"] == 80 ? "" : (":" . $_SERVER["SERVER_PORT"])) . $_SERVER['REQUEST_URI'];
6127
        $request_uri = $this->htmlspecialchars($request_uri, ENT_QUOTES, $this->config['modx_charset']);
6128
        $ua = $this->htmlspecialchars($_SERVER['HTTP_USER_AGENT'], ENT_QUOTES, $this->config['modx_charset']);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ua. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
6129
        $referer = $this->htmlspecialchars($_SERVER['HTTP_REFERER'], ENT_QUOTES, $this->config['modx_charset']);
6130
        if ($is_error) {
6131
            $str = '<h2 style="color:red">&laquo; Evo Parse Error &raquo;</h2>';
6132
            if ($msg != 'PHP Parse Error') {
6133
                $str .= '<h3 style="color:red">' . $msg . '</h3>';
6134
            }
6135
        } else {
6136
            $str = '<h2 style="color:#003399">&laquo; Evo Debug/ stop message &raquo;</h2>';
6137
            $str .= '<h3 style="color:#003399">' . $msg . '</h3>';
6138
        }
6139
6140
        if (!empty ($query)) {
6141
            $str .= '<div style="font-weight:bold;border:1px solid #ccc;padding:8px;color:#333;background-color:#ffffcd;margin-bottom:15px;">SQL &gt; <span id="sqlHolder">' . $query . '</span></div>';
6142
        }
6143
6144
        $errortype = array(
6145
            E_ERROR => "ERROR",
6146
            E_WARNING => "WARNING",
6147
            E_PARSE => "PARSING ERROR",
6148
            E_NOTICE => "NOTICE",
6149
            E_CORE_ERROR => "CORE ERROR",
6150
            E_CORE_WARNING => "CORE WARNING",
6151
            E_COMPILE_ERROR => "COMPILE ERROR",
6152
            E_COMPILE_WARNING => "COMPILE WARNING",
6153
            E_USER_ERROR => "USER ERROR",
6154
            E_USER_WARNING => "USER WARNING",
6155
            E_USER_NOTICE => "USER NOTICE",
6156
            E_STRICT => "STRICT NOTICE",
6157
            E_RECOVERABLE_ERROR => "RECOVERABLE ERROR",
6158
            E_DEPRECATED => "DEPRECATED",
6159
            E_USER_DEPRECATED => "USER DEPRECATED"
6160
        );
6161
6162
        if (!empty($nr) || !empty($file)) {
6163
            if ($text != '') {
6164
                $str .= '<div style="font-weight:bold;border:1px solid #ccc;padding:8px;color:#333;background-color:#ffffcd;margin-bottom:15px;">Error : ' . $text . '</div>';
6165
            }
6166
            if ($output != '') {
6167
                $str .= '<div style="font-weight:bold;border:1px solid #ccc;padding:8px;color:#333;background-color:#ffffcd;margin-bottom:15px;">' . $output . '</div>';
6168
            }
6169
            if ($nr !== '') {
6170
                $table[] = array('ErrorType[num]', $errortype [$nr] . "[" . $nr . "]");
6171
            }
6172
            if ($file) {
6173
                $table[] = array('File', $file);
6174
            }
6175
            if ($line) {
6176
                $table[] = array('Line', $line);
6177
            }
6178
6179
        }
6180
6181
        if ($source != '') {
6182
            $table[] = array("Source", $source);
6183
        }
6184
6185
        if (!empty($this->currentSnippet)) {
6186
            $table[] = array('Current Snippet', $this->currentSnippet);
6187
        }
6188
6189
        if (!empty($this->event->activePlugin)) {
6190
            $table[] = array('Current Plugin', $this->event->activePlugin . '(' . $this->event->name . ')');
6191
        }
6192
6193
        $str .= $MakeTable->create($table, array('Error information', ''));
6194
        $str .= "<br />";
6195
6196
        $table = array();
6197
        $table[] = array('REQUEST_URI', $request_uri);
6198
6199
        if ($this->manager->action) {
6200
            include_once(MODX_MANAGER_PATH . 'includes/actionlist.inc.php');
6201
            global $action_list;
6202
            $actionName = (isset($action_list[$this->manager->action])) ? " - {$action_list[$this->manager->action]}" : '';
6203
6204
            $table[] = array('Manager action', $this->manager->action . $actionName);
6205
        }
6206
6207
        if (preg_match('@^[0-9]+@', $this->documentIdentifier)) {
6208
            $resource = $this->getDocumentObject('id', $this->documentIdentifier);
6209
            $url = $this->makeUrl($this->documentIdentifier, '', '', 'full');
6210
            $table[] = array('Resource', '[' . $this->documentIdentifier . '] <a href="' . $url . '" target="_blank">' . $resource['pagetitle'] . '</a>');
6211
        }
6212
        $table[] = array('Referer', $referer);
6213
        $table[] = array('User Agent', $ua);
6214
        $table[] = array('IP', $_SERVER['REMOTE_ADDR']);
6215
        $table[] = array('Current time', date("Y-m-d H:i:s", $_SERVER['REQUEST_TIME'] + $this->config['server_offset_time']));
6216
        $str .= $MakeTable->create($table, array('Basic info', ''));
6217
        $str .= "<br />";
6218
6219
        $table = array();
6220
        $table[] = array('MySQL', '[^qt^] ([^q^] Requests)');
6221
        $table[] = array('PHP', '[^p^]');
6222
        $table[] = array('Total', '[^t^]');
6223
        $table[] = array('Memory', '[^m^]');
6224
        $str .= $MakeTable->create($table, array('Benchmarks', ''));
6225
        $str .= "<br />";
6226
6227
        $totalTime = ($this->getMicroTime() - $this->tstart);
6228
6229
        $mem = memory_get_peak_usage(true);
6230
        $total_mem = $mem - $this->mstart;
6231
        $total_mem = ($total_mem / 1024 / 1024) . ' mb';
6232
6233
        $queryTime = $this->queryTime;
6234
        $phpTime = $totalTime - $queryTime;
6235
        $queries = isset ($this->executedQueries) ? $this->executedQueries : 0;
6236
        $queryTime = sprintf("%2.4f s", $queryTime);
6237
        $totalTime = sprintf("%2.4f s", $totalTime);
6238
        $phpTime = sprintf("%2.4f s", $phpTime);
6239
6240
        $str = str_replace('[^q^]', $queries, $str);
6241
        $str = str_replace('[^qt^]', $queryTime, $str);
6242
        $str = str_replace('[^p^]', $phpTime, $str);
6243
        $str = str_replace('[^t^]', $totalTime, $str);
6244
        $str = str_replace('[^m^]', $total_mem, $str);
6245
6246
        if (isset($php_errormsg) && !empty($php_errormsg)) {
6247
            $str = "<b>{$php_errormsg}</b><br />\n{$str}";
6248
        }
6249
        $str .= $this->get_backtrace(debug_backtrace());
6250
        // Log error
6251
        if (!empty($this->currentSnippet)) {
6252
            $source = 'Snippet - ' . $this->currentSnippet;
6253
        } elseif (!empty($this->event->activePlugin)) {
6254
            $source = 'Plugin - ' . $this->event->activePlugin;
6255
        } elseif ($source !== '') {
6256
            $source = 'Parser - ' . $source;
6257
        } elseif ($query !== '') {
6258
            $source = 'SQL Query';
6259
        } else {
6260
            $source = 'Parser';
6261
        }
6262
        if ($msg) {
6263
            $source .= ' / ' . $msg;
6264
        }
6265
        if (isset($actionName) && !empty($actionName)) {
6266
            $source .= $actionName;
6267
        }
6268 View Code Duplication
        switch ($nr) {
6269
            case E_DEPRECATED :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
6270
            case E_USER_DEPRECATED :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
6271
            case E_STRICT :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
6272
            case E_NOTICE :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
6273
            case E_USER_NOTICE :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
6274
                $error_level = 2;
6275
                break;
6276
            default:
6277
                $error_level = 3;
6278
        }
6279
        $this->logEvent(0, $error_level, $str, $source);
6280
6281
        if ($error_level === 2 && $this->error_reporting !== '99') {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $this->error_reporting (integer) and '99' (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
6282
            return true;
6283
        }
6284
        if ($this->error_reporting === '99' && !isset($_SESSION['mgrValidated'])) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $this->error_reporting (integer) and '99' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
6285
            return true;
6286
        }
6287
6288
        // Set 500 response header
6289
        if ($error_level !== 2) {
6290
            header('HTTP/1.1 500 Internal Server Error');
6291
        }
6292
6293
        // Display error
6294
        if (isset($_SESSION['mgrValidated'])) {
6295
            echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html><head><title>EVO Content Manager ' . $version . ' &raquo; ' . $release_date . '</title>
6296
                 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6297
                 <link rel="stylesheet" type="text/css" href="' . $this->config['site_manager_url'] . 'media/style/' . $this->config['manager_theme'] . '/style.css" />
6298
                 <style type="text/css">body { padding:10px; } td {font:inherit;}</style>
6299
                 </head><body>
6300
                 ' . $str . '</body></html>';
6301
6302
        } else {
6303
            echo 'Error';
6304
        }
6305
        ob_end_flush();
6306
        exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method messageQuit() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
6307
    }
6308
6309
    /**
6310
     * @param $backtrace
6311
     * @return string
6312
     */
6313
    public function get_backtrace($backtrace)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
6314
    {
6315
        if (!class_exists('makeTable')) {
6316
            include_once('extenders/maketable.class.php');
6317
        }
6318
        $MakeTable = new MakeTable();
6319
        $MakeTable->setTableClass('grid');
6320
        $MakeTable->setRowRegularClass('gridItem');
6321
        $MakeTable->setRowAlternateClass('gridAltItem');
6322
        $table = array();
6323
        $backtrace = array_reverse($backtrace);
6324
        foreach ($backtrace as $key => $val) {
6325
            $key++;
6326
            if (substr($val['function'], 0, 11) === 'messageQuit') {
6327
                break;
6328
            } elseif (substr($val['function'], 0, 8) === 'phpError') {
6329
                break;
6330
            }
6331
            $path = str_replace('\\', '/', $val['file']);
6332
            if (strpos($path, MODX_BASE_PATH) === 0) {
6333
                $path = substr($path, strlen(MODX_BASE_PATH));
6334
            }
6335
            switch ($val['type']) {
6336
                case '->':
6337
                case '::':
6338
                    $functionName = $val['function'] = $val['class'] . $val['type'] . $val['function'];
6339
                    break;
6340
                default:
6341
                    $functionName = $val['function'];
6342
            }
6343
            $tmp = 1;
6344
            $_ = (!empty($val['args'])) ? count($val['args']) : 0;
6345
            $args = array_pad(array(), $_, '$var');
6346
            $args = implode(", ", $args);
6347
            $modx = &$this;
6348
            $args = preg_replace_callback('/\$var/', function () use ($modx, &$tmp, $val) {
6349
                $arg = $val['args'][$tmp - 1];
6350
                switch (true) {
6351
                    case is_null($arg): {
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
6352
                        $out = 'NULL';
6353
                        break;
6354
                    }
6355
                    case is_numeric($arg): {
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
6356
                        $out = $arg;
6357
                        break;
6358
                    }
6359
                    case is_scalar($arg): {
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
6360
                        $out = strlen($arg) > 20 ? 'string $var' . $tmp : ("'" . $this->htmlspecialchars(str_replace("'", "\\'", $arg)) . "'");
6361
                        break;
6362
                    }
6363
                    case is_bool($arg): {
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
6364
                        $out = $arg ? 'TRUE' : 'FALSE';
6365
                        break;
6366
                    }
6367
                    case is_array($arg): {
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
6368
                        $out = 'array $var' . $tmp;
6369
                        break;
6370
                    }
6371
                    case is_object($arg): {
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
6372
                        $out = get_class($arg) . ' $var' . $tmp;
6373
                        break;
6374
                    }
6375
                    default: {
0 ignored issues
show
Coding Style introduced by
DEFAULT statements must be defined using a colon

As per the PSR-2 coding standard, default statements should not be wrapped in curly braces.

switch ($expr) {
    default: { //wrong
        doSomething();
        break;
    }
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
6376
                        $out = '$var' . $tmp;
6377
                    }
6378
                }
6379
                $tmp++;
6380
                return $out;
6381
            }, $args);
6382
            $line = array(
6383
                "<strong>" . $functionName . "</strong>(" . $args . ")",
6384
                $path . " on line " . $val['line']
6385
            );
6386
            $table[] = array(implode("<br />", $line));
6387
        }
6388
        return $MakeTable->create($table, array('Backtrace'));
6389
    }
6390
6391
    /**
6392
     * @return string
6393
     */
6394
    public function getRegisteredClientScripts()
6395
    {
6396
        return implode("\n", $this->jscripts);
6397
    }
6398
6399
    /**
6400
     * @return string
6401
     */
6402
    public function getRegisteredClientStartupScripts()
6403
    {
6404
        return implode("\n", $this->sjscripts);
6405
    }
6406
6407
    /**
6408
     * Format alias to be URL-safe. Strip invalid characters.
6409
     *
6410
     * @param string $alias Alias to be formatted
6411
     * @return string Safe alias
6412
     */
6413
    public function stripAlias($alias)
6414
    {
6415
        // let add-ons overwrite the default behavior
6416
        $results = $this->invokeEvent('OnStripAlias', array('alias' => $alias));
6417
        if (!empty($results)) {
6418
            // if multiple plugins are registered, only the last one is used
6419
            return end($results);
6420
        } else {
6421
            // default behavior: strip invalid characters and replace spaces with dashes.
6422
            $alias = strip_tags($alias); // strip HTML
6423
            $alias = preg_replace('/[^\.A-Za-z0-9 _-]/', '', $alias); // strip non-alphanumeric characters
6424
            $alias = preg_replace('/\s+/', '-', $alias); // convert white-space to dash
6425
            $alias = preg_replace('/-+/', '-', $alias);  // convert multiple dashes to one
6426
            $alias = trim($alias, '-'); // trim excess
6427
            return $alias;
6428
        }
6429
    }
6430
6431
    /**
6432
     * @param $size
6433
     * @return string
6434
     */
6435
    public function nicesize($size)
6436
    {
6437
        $sizes = array('Tb' => 1099511627776, 'Gb' => 1073741824, 'Mb' => 1048576, 'Kb' => 1024, 'b' => 1);
6438
        $precisions = count($sizes) - 1;
6439
        foreach ($sizes as $unit => $bytes) {
6440
            if ($size >= $bytes) {
6441
                return number_format($size / $bytes, $precisions) . ' ' . $unit;
6442
            }
6443
            $precisions--;
6444
        }
6445
        return '0 b';
6446
    }
6447
6448
    /**
6449
     * @param $parentid
6450
     * @param $alias
6451
     * @return bool
6452
     */
6453
    public function getHiddenIdFromAlias($parentid, $alias)
6454
    {
6455
        $table = $this->getFullTableName('site_content');
6456
        $query = $this->db->query("SELECT sc.id, children.id AS child_id, children.alias, COUNT(children2.id) AS children_count
6457
            FROM {$table} sc
6458
            JOIN {$table} children ON children.parent = sc.id
6459
            LEFT JOIN {$table} children2 ON children2.parent = children.id
6460
            WHERE sc.parent = {$parentid} AND sc.alias_visible = '0' GROUP BY children.id;");
6461
6462
        while ($child = $this->db->getRow($query)) {
6463
            if ($child['alias'] == $alias || $child['child_id'] == $alias) {
6464
                return $child['child_id'];
6465
            }
6466
6467
            if ($child['children_count'] > 0) {
6468
                $id = $this->getHiddenIdFromAlias($child['id'], $alias);
6469
                if ($id) {
6470
                    return $id;
6471
                }
6472
            }
6473
        }
6474
6475
        return false;
6476
    }
6477
6478
    /**
6479
     * @param $alias
6480
     * @return bool|int
6481
     */
6482
    public function getIdFromAlias($alias)
6483
    {
6484
        if (isset($this->documentListing[$alias])) {
6485
            return $this->documentListing[$alias];
6486
        }
6487
6488
        $tbl_site_content = $this->getFullTableName('site_content');
6489
        if ($this->config['use_alias_path'] == 1) {
6490
            if ($alias == '.') {
6491
                return 0;
6492
            }
6493
6494
            if (strpos($alias, '/') !== false) {
6495
                $_a = explode('/', $alias);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $_a. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
6496
            } else {
6497
                $_a[] = $alias;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$_a was never initialized. Although not strictly required by PHP, it is generally a good practice to add $_a = 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...
6498
            }
6499
            $id = 0;
6500
6501
            foreach ($_a as $alias) {
6502
                if ($id === false) {
6503
                    break;
6504
                }
6505
                $alias = $this->db->escape($alias);
6506
                $rs = $this->db->select('id', $tbl_site_content, "deleted=0 and parent='{$id}' and alias='{$alias}'");
6507
                if ($this->db->getRecordCount($rs) == 0) {
6508
                    $rs = $this->db->select('id', $tbl_site_content, "deleted=0 and parent='{$id}' and id='{$alias}'");
6509
                }
6510
                $next = $this->db->getValue($rs);
6511
                $id = !$next ? $this->getHiddenIdFromAlias($id, $alias) : $next;
6512
            }
6513
        } else {
6514
            $rs = $this->db->select('id', $tbl_site_content, "deleted=0 and alias='{$alias}'", 'parent, menuindex');
6515
            $id = $this->db->getValue($rs);
6516
            if (!$id) {
6517
                $id = false;
6518
            }
6519
        }
6520
        return $id;
6521
    }
6522
6523
    /**
6524
     * @param string $str
6525
     * @return bool|mixed|string
6526
     */
6527
    public function atBindInclude($str = '')
6528
    {
6529
        if (strpos($str, '@INCLUDE') !== 0) {
6530
            return $str;
6531
        }
6532 View Code Duplication
        if (strpos($str, "\n") !== false) {
6533
            $str = substr($str, 0, strpos("\n", $str));
6534
        }
6535
6536
        $str = substr($str, 9);
6537
        $str = trim($str);
6538
        $str = str_replace('\\', '/', $str);
6539
        $str = ltrim($str, '/');
6540
6541
        $tpl_dir = 'assets/templates/';
6542
6543
        if (strpos($str, MODX_MANAGER_PATH) === 0) {
6544
            return false;
6545
        } elseif (is_file(MODX_BASE_PATH . $str)) {
6546
            $file_path = MODX_BASE_PATH . $str;
6547
        } elseif (is_file(MODX_BASE_PATH . "{$tpl_dir}{$str}")) {
6548
            $file_path = MODX_BASE_PATH . $tpl_dir . $str;
6549
        } else {
6550
            return false;
6551
        }
6552
6553
        if (!$file_path || !is_file($file_path)) {
6554
            return false;
6555
        }
6556
6557
        ob_start();
6558
        $modx = &$this;
6559
        $result = include($file_path);
6560
        if ($result === 1) {
6561
            $result = '';
6562
        }
6563
        $content = ob_get_clean();
6564
        if (!$content && $result) {
6565
            $content = $result;
6566
        }
6567
        return $content;
6568
    }
6569
6570
    // php compat
6571
6572
    /**
6573
     * @param $str
6574
     * @param int $flags
6575
     * @param string $encode
6576
     * @return mixed
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
6577
     */
6578
    public function htmlspecialchars($str, $flags = ENT_COMPAT, $encode = '')
6579
    {
6580
        $this->loadExtension('PHPCOMPAT');
6581
        return $this->phpcompat->htmlspecialchars($str, $flags, $encode);
6582
    }
6583
6584
    /**
6585
     * @param $string
6586
     * @param bool $returnData
6587
     * @return bool|mixed
6588
     */
6589
    public function isJson($string, $returnData = false)
6590
    {
6591
        $data = json_decode($string, true);
6592
        return (json_last_error() == JSON_ERROR_NONE) ? ($returnData ? $data : true) : false;
6593
    }
6594
6595
    /**
6596
     * @param $key
6597
     * @return array
6598
     */
6599
    public function splitKeyAndFilter($key)
6600
    {
6601
        if ($this->config['enable_filter'] == 1 && strpos($key, ':') !== false && stripos($key, '@FILE') !== 0) {
6602
            list($key, $modifiers) = explode(':', $key, 2);
6603
        } else {
6604
            $modifiers = false;
6605
        }
6606
6607
        $key = trim($key);
6608
        if ($modifiers !== false) {
6609
            $modifiers = trim($modifiers);
6610
        }
6611
6612
        return array($key, $modifiers);
6613
    }
6614
6615
    /**
6616
     * @param string $value
6617
     * @param bool $modifiers
6618
     * @param string $key
6619
     * @return string
6620
     */
6621
    public function applyFilter($value = '', $modifiers = false, $key = '')
6622
    {
6623
        if ($modifiers === false || $modifiers == 'raw') {
6624
            return $value;
6625
        }
6626
        if ($modifiers !== false) {
6627
            $modifiers = trim($modifiers);
6628
        }
6629
6630
        $this->loadExtension('MODIFIERS');
6631
        return $this->filter->phxFilter($key, $value, $modifiers);
0 ignored issues
show
Bug introduced by
It seems like $modifiers defined by parameter $modifiers on line 6621 can also be of type false; however, MODIFIERS::phxFilter() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
6632
    }
6633
6634
    // End of class.
6635
6636
6637
    /**
6638
     * Get Clean Query String
6639
     *
6640
     * Fixes the issue where passing an array into the q get variable causes errors
6641
     *
6642
     */
6643
    private static function _getCleanQueryString()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
Coding Style introduced by
_getCleanQueryString uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
6644
    {
6645
        $q = MODX_CLI ? null : $_GET['q'];
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $q. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
6646
6647
        //Return null if the query doesn't exist
6648
        if (empty($q)) {
6649
            return null;
6650
        }
6651
6652
        //If we have a string, return it
6653
        if (is_string($q)) {
6654
            return $q;
6655
        }
6656
6657
        //If we have an array, return the first element
6658
        if (is_array($q)) {
6659
            return $q[0];
6660
        }
6661
    }
6662
6663
    /**
6664
     * @param string $title
6665
     * @param string $msg
6666
     * @param int $type
6667
     */
6668
    public function addLog($title = 'no title', $msg = '', $type = 1)
0 ignored issues
show
Coding Style introduced by
addLog uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
6669
    {
6670
        if ($title === '') {
6671
            $title = 'no title';
6672
        }
6673
        if (is_array($msg)) {
6674
            $msg = '<pre>' . print_r($msg, true) . '</pre>';
6675
        } elseif ($msg === '') {
6676
            $msg = $_SERVER['REQUEST_URI'];
6677
        }
6678
        $this->logEvent(0, $type, $msg, $title);
6679
    }
6680
6681
}
6682
6683
/**
6684
 * System Event Class
6685
 */
6686
class SystemEvent
0 ignored issues
show
Coding Style introduced by
The property $_propagate is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
The property $_output is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
6687
{
6688
    public $name = '';
6689
    public $_propagate = true;
6690
    public $_output = '';
6691
    public $activated = false;
6692
    public $activePlugin = '';
6693
    public $params = array();
6694
6695
    /**
6696
     * @param string $name Name of the event
6697
     */
6698
    public function __construct($name = "")
6699
    {
6700
        $this->_resetEventObject();
6701
        $this->name = $name;
6702
    }
6703
6704
    /**
6705
     * Display a message to the user
6706
     *
6707
     * @global array $SystemAlertMsgQueque
6708
     * @param string $msg The message
6709
     */
6710
    public function alert($msg)
6711
    {
6712
        global $SystemAlertMsgQueque;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
6713
        if ($msg == "") {
6714
            return;
6715
        }
6716
        if (is_array($SystemAlertMsgQueque)) {
6717
            $title = '';
6718
            if ($this->name && $this->activePlugin) {
6719
                $title = "<div><b>" . $this->activePlugin . "</b> - <span style='color:maroon;'>" . $this->name . "</span></div>";
6720
            }
6721
            $SystemAlertMsgQueque[] = "$title<div style='margin-left:10px;margin-top:3px;'>$msg</div>";
6722
        }
6723
    }
6724
6725
    /**
6726
     * Output
6727
     *
6728
     * @param string $msg
6729
     */
6730
    public function output($msg)
6731
    {
6732
        $this->_output .= $msg;
6733
    }
6734
6735
    /**
6736
     * Stop event propogation
6737
     */
6738
    public function stopPropagation()
6739
    {
6740
        $this->_propagate = false;
6741
    }
6742
6743
    public function _resetEventObject()
6744
    {
6745
        unset ($this->returnedValues);
6746
        $this->name = "";
6747
        $this->_output = "";
6748
        $this->_propagate = true;
6749
        $this->activated = false;
6750
    }
6751
}
6752