Cache::php_strip_whitespace()   D
last analyzed

Complexity

Conditions 22
Paths 60

Size

Total Lines 68

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
nc 60
nop 1
dl 0
loc 68
rs 4.1666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php namespace EvolutionCMS\Legacy;
2
3
use EvolutionCMS\Models;
4
5
/**
6
 * @class: synccache
7
 */
8
class Cache
9
{
10
    public $cachePath;
11
    public $showReport;
12
    public $deletedfiles = array();
13
    /**
14
     * @var array
15
     */
16
    public $aliases = array();
17
    /**
18
     * @var array
19
     */
20
    public $parents = array();
21
    /**
22
     * @var array
23
     */
24
    public $aliasVisible = array();
25
    public $request_time;
26
    public $cacheRefreshTime;
27
28
    /**
29
     * synccache constructor.
30
     */
31
    public function __construct()
32
    {
33
        $modx = evolutionCMS();
34
        $this->request_time = $_SERVER['REQUEST_TIME'] + $modx->getConfig('server_offset_time');
35
    }
36
37
    /**
38
     * @param string $path
39
     */
40
    public function setCachepath($path)
41
    {
42
        $this->cachePath = $path;
43
    }
44
45
    /**
46
     * @param bool $bool
47
     */
48
    public function setReport($bool)
49
    {
50
        $this->showReport = $bool;
51
    }
52
53
    /**
54
     * @param string $s
55
     * @return string
56
     */
57
    public function escapeSingleQuotes($s)
58
    {
59
        if ($s === '') {
60
            return $s;
61
        }
62
        $q1 = array("\\", "'");
63
        $q2 = array("\\\\", "\\'");
64
65
        return str_replace($q1, $q2, $s);
66
    }
67
68
    /**
69
     * @param string $s
70
     * @return string
71
     */
72
    public function escapeDoubleQuotes($s)
73
    {
74
        $q1 = array("\\", "\"", "\r", "\n", "\$");
75
        $q2 = array("\\\\", "\\\"", "\\r", "\\n", "\\$");
76
77
        return str_replace($q1, $q2, $s);
78
    }
79
80
    /**
81
     * @param int|string $id
82
     * @param string $path
83
     * @return string
84
     */
85
    public function getParents($id, $path = '')
86
    { // modx:returns child's parent
87
        $modx = evolutionCMS();
88
        if (empty($this->aliases)) {
89
            $rs = $modx->getDatabase()->select(
90
                "id, IF(alias='', id, alias) AS alias, parent, alias_visible",
91
                $modx->getDatabase()->getFullTableName('site_content'),
92
                'deleted=0'
93
            );
94
            while ($row = $modx->getDatabase()->getRow($rs)) {
95
                $docid = $row['id'];
96
                $this->aliases[$docid] = $row['alias'];
97
                $this->parents[$docid] = $row['parent'];
98
                $this->aliasVisible[$docid] = $row['alias_visible'];
99
            }
100
        }
101
        if (isset($this->aliases[$id])) {
102
            if ($this->aliasVisible[$id] == 1) {
103
                if ($path != '') {
104
                    $path = $this->aliases[$id] . '/' . $path;
105
                } else {
106
                    $path = $this->aliases[$id];
107
                }
108
            }
109
110
            return $this->getParents($this->parents[$id], $path);
111
        }
112
113
        return $path;
114
    }
115
116
    /**
117
     * @param null|DocumentParser $modx
118
     */
119
    public function emptyCache($modx = null)
120
    {
121
        if (!($modx instanceof Interfaces\CoreInterface)) {
0 ignored issues
show
Bug introduced by
The class EvolutionCMS\Legacy\Interfaces\CoreInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
122
            $modx = $GLOBALS['modx'];
123
        }
124
        if (!isset($this->cachePath)) {
125
            $modx->getService('ExceptionHandler')->messageQuit("Cache path not set.");
126
        }
127
128
        $files = glob(realpath($this->cachePath) . '/*.pageCache.php');
129
        $filesincache = count($files);
130
        $deletedfiles = array();
131
        while ($file = array_shift($files)) {
132
            $name = basename($file);
133
            clearstatcache();
134
            if (is_file($file)) {
135
                if (unlink($file)) {
136
                    $deletedfiles[] = $name;
137
                }
138
            }
139
        }
140
        $opcache_restrict_api = trim(ini_get('opcache.restrict_api'));
141
        $opcache_restrict_api = $opcache_restrict_api && mb_stripos(__FILE__, $opcache_restrict_api) !== 0;
142
143
        if (!$opcache_restrict_api && function_exists('opcache_get_status')) {
144
            $opcache = opcache_get_status();
145
            if (!empty($opcache['opcache_enabled'])) {
146
                opcache_reset();
147
            }
148
        }
149
150
        $this->buildCache($modx);
151
152
        $this->publishTimeConfig();
153
154
        // finished cache stuff.
155
        if ($this->showReport == 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...
156
            global $_lang;
157
            $total = count($deletedfiles);
158
            echo sprintf($_lang['refresh_cache'], $filesincache, $total);
159
            if ($total > 0) {
160
                if (isset($opcache)) {
161
                    echo '<p>Opcache empty.</p>';
162
                }
163
                echo '<p>' . $_lang['cache_files_deleted'] . '</p><ul>';
164
                foreach ($deletedfiles as $deletedfile) {
165
                    echo '<li>' . $deletedfile . '</li>';
166
                }
167
                echo '</ul>';
168
            }
169
        }
170
    }
171
172
    /**
173
     * @param string|int $cacheRefreshTime
174
     */
175
    public function publishTimeConfig($cacheRefreshTime = '')
176
    {
177
        $cacheRefreshTimeFromDB = $this->getCacheRefreshTime();
178
        if (!preg_match('@^[0-9]+$]@', $cacheRefreshTime) || $cacheRefreshTimeFromDB < $cacheRefreshTime) {
179
            $cacheRefreshTime = $cacheRefreshTimeFromDB;
180
        }
181
182
183
        // write the file
184
        $content = '<?php' . "\n";
185
        $content .= '$recent_update=\'' . $this->request_time . '\';' . "\n";
186
        $content .= '$cacheRefreshTime=\'' . $cacheRefreshTime . '\';' . "\n";
187
188
        $filename = evolutionCMS()->getSitePublishingFilePath();
189
        if (!$handle = fopen($filename, 'w')) {
190
            exit("Cannot open file ({$filename}");
191
        }
192
193
        $content .= "\n";
194
195
        // Write $somecontent to our opened file.
196
        if (fwrite($handle, $content) === false) {
197
            exit("Cannot write publishing info file! Make sure the assets/cache directory is writable!");
198
        }
199
    }
200
201
    /**
202
     * @return int
203
     */
204
    public function getCacheRefreshTime()
205
    {
206
        $modx = evolutionCMS();
207
208
        // update publish time file
209
        $timesArr = array();
210
211
        $result = $modx->getDatabase()->select(
212
            'MIN(pub_date) AS minpub',
213
            $modx->getDatabase()->getFullTableName('site_content'),
214
            'pub_date>' . $this->request_time
215
        );
216
        if (!$result) {
217
            echo "Couldn't determine next publish event!";
218
        }
219
220
        $minpub = $modx->getDatabase()->getValue($result);
221
        if ($minpub != null) {
222
            $timesArr[] = $minpub;
223
        }
224
225
        $result = $modx->getDatabase()->select(
226
            'MIN(unpub_date) AS minunpub',
227
            $modx->getDatabase()->getFullTableName('site_content'),
228
            'unpub_date>' . $this->request_time
229
        );
230
        if (!$result) {
231
            echo "Couldn't determine next unpublish event!";
232
        }
233
234
        $minunpub = $modx->getDatabase()->getValue($result);
235
        if ($minunpub != null) {
236
            $timesArr[] = $minunpub;
237
        }
238
239
        if (isset($this->cacheRefreshTime) && !empty($this->cacheRefreshTime)) {
240
            $timesArr[] = $this->cacheRefreshTime;
241
        }
242
243
        if (count($timesArr) > 0) {
244
            $cacheRefreshTime = min($timesArr);
245
        } else {
246
            $cacheRefreshTime = 0;
247
        }
248
249
        return $cacheRefreshTime;
250
    }
251
252
    /**
253
     * build siteCache file
254
     * @param Interfaces\CoreInterface $modx
255
     * @return boolean success
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...
256
     */
257
    public function buildCache($modx)
258
    {
259
        $content = "<?php\n";
260
261
        // SETTINGS & DOCUMENT LISTINGS CACHE
262
263
        // get settings
264
        $rs = $modx->getDatabase()->select('*', $modx->getDatabase()->getFullTableName('system_settings'));
265
        $config = array();
266
        $content .= '$c=&$this->config;';
267
        while (list($key, $value) = $modx->getDatabase()->getRow($rs, 'num')) {
268
            $content .= '$c[\'' . $modx->getDatabase()->escape($key) . '\']="' . $this->escapeDoubleQuotes($value) . '";';
269
            $config[$key] = $value;
270
        }
271
272
        if ($config['enable_filter']) {
273
            if (Models\SitePlugin::activePhx()->count()) {
274
                $content .= '$this->config[\'enable_filter\']=\'0\';';
275
            }
276
        }
277
278
        if ($config['aliaslistingfolder'] == 1) {
279
            $f['id'] = 'c.id';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$f was never initialized. Although not strictly required by PHP, it is generally a good practice to add $f = 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...
280
            $f['alias'] = "IF( c.alias='', c.id, c.alias)";
281
            $f['parent'] = 'c.parent';
282
            $f['isfolder'] = 'c.isfolder';
283
            $f['alias_visible'] = 'c.alias_visible';
284
            $from = array();
285
            $from[] = $modx->getDatabase()->getFullTableName('site_content') . ' c';
286
            $from[] = 'LEFT JOIN ' . $modx->getDatabase()->getFullTableName('site_content') . ' p ON p.id=c.parent';
287
            $where = 'c.deleted=0 AND (c.isfolder=1 OR p.alias_visible=0)';
288
            $rs = $modx->getDatabase()->select($f, $from, $where, 'c.parent, c.menuindex');
289
        } else {
290
            $rs = $modx->getDatabase()->select(
291
                "id, IF(alias='', id, alias) AS alias, parent, isfolder, alias_visible",
292
                $modx->getDatabase()->getFullTableName('site_content'),
293
                'deleted=0',
294
                'parent, menuindex'
295
            );
296
        }
297
298
        $use_alias_path = ($config['friendly_urls'] && $config['use_alias_path']) ? 1 : 0;
299
        $tmpPath = '';
300
        $content .= '$this->aliasListing=array();';
301
        $content .= '$a=&$this->aliasListing;';
302
        $content .= '$d=&$this->documentListing;';
303
        $content .= '$m=&$this->documentMap;';
304
        while ($doc = $modx->getDatabase()->getRow($rs)) {
305
            $docid = $doc['id'];
306
            if ($use_alias_path) {
307
                $tmpPath = $this->getParents($doc['parent']);
308
                $alias = (strlen($tmpPath) > 0 ? "$tmpPath/" : '') . $doc['alias'];
309
                $key = $alias;
310
            } else {
311
                $key = $doc['alias'];
312
            }
313
314
            $doc['path'] = $tmpPath;
315
            $content .= '$a[' . $docid . ']=array(\'id\'=>' . $docid . ',\'alias\'=>\'' . $doc['alias'] . '\',\'path\'=>\'' . $doc['path'] . '\',\'parent\'=>' . $doc['parent'] . ',\'isfolder\'=>' . $doc['isfolder'] . ',\'alias_visible\'=>' . $doc['alias_visible'] . ');';
316
            $content .= '$d[\'' . $key . '\']=' . $docid . ';';
317
            $content .= '$m[]=array(' . $doc['parent'] . '=>' . $docid . ');';
318
        }
319
320
        // get content types
321
        $rs = $modx->getDatabase()->select(
322
            'id, contentType',
323
            $modx->getDatabase()->getFullTableName('site_content'),
324
            "contentType!='text/html'"
325
        );
326
        $content .= '$c=&$this->contentTypes;';
327
        while ($doc = $modx->getDatabase()->getRow($rs)) {
328
            $content .= '$c[\'' . $doc['id'] . '\']=\'' . $doc['contentType'] . '\';';
329
        }
330
331
        // WRITE Chunks to cache file
332
        $rs = $modx->getDatabase()->select('*', $modx->getDatabase()->getFullTableName('site_htmlsnippets'));
333
        $content .= '$c=&$this->chunkCache;';
334
        while ($doc = $modx->getDatabase()->getRow($rs)) {
335
            $content .= '$c[\'' . $modx->getDatabase()->escape($doc['name']) . '\']=\'' . ($doc['disabled'] ? '' : $this->escapeSingleQuotes($doc['snippet'])) . '\';';
336
        }
337
338
        // WRITE snippets to cache file
339
        $f = 'ss.*, sm.properties as sharedproperties';
340
        $from = $modx->getDatabase()->getFullTableName('site_snippets') . ' ss LEFT JOIN ' .
341
            $modx->getDatabase()->getFullTableName('site_modules') . ' sm on sm.guid=ss.moduleguid';
342
        $rs = $modx->getDatabase()->select($f, $from);
343
        $content .= '$s=&$this->snippetCache;';
344
        while ($row = $modx->getDatabase()->getRow($rs)) {
345
            $key = $modx->getDatabase()->escape($row['name']);
346
            if ($row['disabled']) {
347
                $content .= '$s[\'' . $key . '\']=\'return false;\';';
348
            } else {
349
                $value = trim($row['snippet']);
350
                if ($modx->getConfig('minifyphp_incache')) {
351
                    $value = $this->php_strip_whitespace($value);
352
                }
353
                $content .= '$s[\'' . $key . '\']=\'' . $this->escapeSingleQuotes($value) . '\';';
354
                $properties = $modx->parseProperties($row['properties']);
355
                $sharedproperties = $modx->parseProperties($row['sharedproperties']);
356
                $properties = array_merge($sharedproperties, $properties);
357 View Code Duplication
                if (0 < count($properties)) {
358
                    $content .= '$s[\'' . $key . 'Props\']=\'' . $this->escapeSingleQuotes(json_encode($properties)) . '\';';
359
                }
360
            }
361
        }
362
363
        // WRITE plugins to cache file
364
        $f = 'sp.*, sm.properties as sharedproperties';
365
        $from = [];
366
        $from[] = $modx->getDatabase()->getFullTableName('site_plugins') . ' sp';
367
        $from[] = 'LEFT JOIN ' . $modx->getDatabase()->getFullTableName('site_modules') . ' sm on sm.guid=sp.moduleguid';
368
        $rs = $modx->getDatabase()->select($f, $from, 'sp.disabled=0');
369
        $content .= '$p=&$this->pluginCache;';
370
        while ($row = $modx->getDatabase()->getRow($rs)) {
371
            $key = $modx->getDatabase()->escape($row['name']);
372
            $value = trim($row['plugincode']);
373
            if ($modx->getConfig('minifyphp_incache')) {
374
                $value = $this->php_strip_whitespace($value);
375
            }
376
            $content .= '$p[\'' . $key . '\']=\'' . $this->escapeSingleQuotes($value) . '\';';
377
            if ($row['properties'] != '' || $row['sharedproperties'] != '') {
378
                $properties = $modx->parseProperties($row['properties']);
379
                $sharedproperties = $modx->parseProperties($row['sharedproperties']);
380
                $properties = array_merge($sharedproperties, $properties);
381 View Code Duplication
                if (0 < count($properties)) {
382
                    $content .= '$p[\'' . $key . 'Props\']=\'' . $this->escapeSingleQuotes(json_encode($properties)) . '\';';
383
                }
384
            }
385
        }
386
387
        // WRITE system event triggers
388
        $f = 'sysevt.name as evtname, event.pluginid, plugin.name as pname';
389
        $from = array();
390
        $from[] = $modx->getDatabase()->getFullTableName('system_eventnames') . ' sysevt';
391
        $from[] = 'INNER JOIN ' . $modx->getDatabase()->getFullTableName('site_plugin_events') . ' event ON event.evtid=sysevt.id';
392
        $from[] = 'INNER JOIN ' . $modx->getDatabase()->getFullTableName('site_plugins') . ' plugin ON plugin.id=event.pluginid';
393
        $rs = $modx->getDatabase()->select($f, $from, 'plugin.disabled=0', 'sysevt.name, event.priority');
394
        $content .= '$e=&$this->pluginEvent;';
395
        $events = array();
396
        while ($row = $modx->getDatabase()->getRow($rs)) {
397
            $evtname = $row['evtname'];
398
            if (!isset($events[$evtname])) {
399
                $events[$evtname] = array();
400
            }
401
            $events[$evtname][] = $row['pname'];
402
        }
403
        foreach ($events as $evtname => $pluginnames) {
404
            $events[$evtname] = $pluginnames;
405
            $content .= '$e[\'' . $evtname . '\']=array(\'' . implode('\',\'',
406
                    $this->escapeSingleQuotes($pluginnames)) . '\');';
407
        }
408
409
        $content .= "\n";
410
411
        // close and write the file
412
        $filename = $modx->getSiteCacheFilePath();
413
414
        // invoke OnBeforeCacheUpdate event
415
        $modx->invokeEvent('OnBeforeCacheUpdate');
416
417
        if (@file_put_contents($filename, $content) === false) {
418
            exit("Cannot write main Evolution CMS cache file! Make sure the assets/cache directory is writable!");
419
        }
420
421
        if (!is_file($this->cachePath . '/.htaccess')) {
422
            file_put_contents($this->cachePath . '/.htaccess', "order deny,allow\ndeny from all\n");
423
        }
424
425
        // invoke OnCacheUpdate event
426
        $modx->invokeEvent('OnCacheUpdate');
427
428
        return true;
429
    }
430
431
    /**
432
     * @param string $source
433
     * @return string
434
     *
435
     * @see http://php.net/manual/en/tokenizer.examples.php
436
     */
437
    public function php_strip_whitespace($source)
438
    {
439
440
        $source = trim($source);
441
        if (substr($source, 0, 5) !== '<?php') {
442
            $source = '<?php ' . $source;
443
        }
444
445
        $tokens = token_get_all($source);
446
        $_ = '';
447
        $prev_token = 0;
448
        $chars = explode(' ', '( ) ; , = { } ? :');
449
        foreach ($tokens as $i => $token) {
450
            if (is_string($token)) {
451
                if (in_array($token, array('=', ':'))) {
452
                    $_ = trim($_);
453
                } elseif (in_array($token, array('(', '{')) && in_array($prev_token, array(T_IF, T_ELSE, T_ELSEIF))) {
454
                    $_ = trim($_);
455
                }
456
                $_ .= $token;
457
                if ($prev_token == T_END_HEREDOC) {
458
                    $_ .= "\n";
459
                }
460
                continue;
461
            }
462
463
            list($type, $text) = $token;
464
465
            switch ($type) {
466
                case T_COMMENT    :
467
                case T_DOC_COMMENT:
468
                    break;
469
                case T_WHITESPACE :
470
                    if ($prev_token != T_END_HEREDOC) {
471
                        $_ = trim($_);
472
                    }
473
                    $lastChar = substr($_, -1);
474
                    if (!in_array($lastChar, $chars)) {// ,320,327,288,284,289
475
                        if (!in_array($prev_token,
476
                            array(T_FOREACH, T_WHILE, T_FOR, T_BOOLEAN_AND, T_BOOLEAN_OR, T_DOUBLE_ARROW))) {
477
                            $_ .= ' ';
478
                        }
479
                    }
480
                    break;
481
                case T_IS_EQUAL :
482
                case T_IS_IDENTICAL :
483
                case T_IS_NOT_EQUAL :
484
                case T_DOUBLE_ARROW :
485
                case T_BOOLEAN_AND :
486
                case T_BOOLEAN_OR :
487
                case T_START_HEREDOC :
488
                    if ($prev_token != T_START_HEREDOC) {
489
                        $_ = trim($_);
490
                    }
491
                    $prev_token = $type;
492
                    $_ .= $text;
493
                    break;
494
                default:
495
                    $prev_token = $type;
496
                    $_ .= $text;
497
            }
498
        }
499
        $source = preg_replace(array('@^<\?php@i', '|\s+|', '|<!--|', '|-->|', '|-->\s+<!--|'),
500
            array('', ' ', "\n" . '<!--', '-->' . "\n", '-->' . "\n" . '<!--'), $_);
501
        $source = trim($source);
502
503
        return $source;
504
    }
505
}
506