Completed
Branch master (5aadec)
by Agel_Nash
07:59 queued 03:34
created

DLphx::runCode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/*####
3
#
4
#	Name: PHx (Placeholders Xtended)
5
#	Version: 2.2.0
6
#	Modified by Nick to include external files
7
#	Modified by Anton Kuzmin for using of modx snippets cache
8
#	Modified by Temus ([email protected])
9
#	Author: Armand "bS" Pondman ([email protected])
10
#	Date: March 22, 2013
11
#
12
####*/
13
include_once(MODX_BASE_PATH . 'assets/lib/APIHelpers.class.php');
14
15
/**
16
 * Class DLphx
17
 */
18
class DLphx
19
{
20
    public $placeholders = array();
21
    public $name = 'PHx';
22
    public $version = '2.2.0';
23
    public $user = array();
24
    public $cache = array(
25
        'cm' => array(),
26
        'ui' => array(),
27
        'mo' => array()
28
    );
29
    public $safetags = array(
30
        array('~(?<![\[]|^\^)\[(?=[^\+\*\(\[]|$)~s', '~(?<=[^\+\*\)\]]|^)\](?=[^\]]|$)~s'),
31
        array('&_PHX_INTERNAL_091_&', '&_PHX_INTERNAL_093_&'),
32
        array('[', ']')
33
    );
34
    public $console = array();
35
    public $debug = false;
36
    public $debugLog = false;
37
    public $curPass = 0;
38
    public $maxPasses = 50;
39
    public $swapSnippetCache = array();
40
41
    /**
42
     * DLphx constructor.
43
     * @param int|bool|string $debug
44
     * @param int $maxpass
45
     */
46
    public function __construct($debug = false, $maxpass = 50)
47
    {
48
        global $modx;
49
50
        $this->user["mgrid"] = isset($_SESSION['mgrInternalKey']) ? intval($_SESSION['mgrInternalKey']) : 0;
51
        $this->user["usrid"] = isset($_SESSION['webInternalKey']) ? intval($_SESSION['webInternalKey']) : 0;
52
        $this->user["id"] = ($this->user["usrid"] > 0) ? (-$this->user["usrid"]) : $this->user["mgrid"];
53
54
        $this->debug = (bool)$debug;
55
56
        $this->maxPasses = ($maxpass != '') ? $maxpass : 50;
57
58
        $modx->setPlaceholder("phx", "&_PHX_INTERNAL_&");
59
        if (function_exists('mb_internal_encoding')) {
60
            mb_internal_encoding($modx->config['modx_charset']);
61
        }
62
    }
63
64
    // Plugin event hook for MODx
65
    public function OnParseDocument()
66
    {
67
        global $modx;
68
        // Get document output from MODx
69
        $template = $modx->documentOutput;
70
        // To the parse cave .. let's go! *insert batman tune here*
71
        $template = $this->Parse($template);
72
        // Set processed document output in MODx
73
        $modx->documentOutput = $template;
74
    }
75
76
    // Parser: Preparation, cleaning and checkup
77
    /**
78
     * @param string $template
79
     * @return mixed|string
80
     */
81
    public function Parse($template = '')
82
    {
83
        global $modx;
84
        // If we already reached max passes don't get at it again.
85
        if ($this->curPass == $this->maxPasses) {
86
            return $template;
87
        }
88
        // Set template pre-process hash
89
        $st = md5($template);
90
        // Replace non-call characters in the template: [, ]
91
        $template = preg_replace($this->safetags[0], $this->safetags[1], $template);
92
        // To the parse mobile.. let's go! *insert batman tune here*
93
        $template = $this->ParseValues($template);
94
        // clean up unused placeholders that have modifiers attached (MODx can't clean them)
95
        preg_match_all('~(?:=`[^`@]*?)(\[\+([^:\+\[\]]+)([^\[\]]*?)\+\])~s', $template, $matches);
96
        if ($matches[0]) {
97
            $template = str_replace($matches[1], '', $template);
98
            $this->Log("Cleaning unsolved tags: \n" . implode("\n", $matches[2]));
99
        }
100
        // Restore non-call characters in the template: [, ]
101
        $template = str_replace($this->safetags[1], $this->safetags[2], $template);
102
        // Set template post-process hash
103
        $et = md5($template);
104
        // If template has changed, parse it once more...
105
        if ($st != $et) {
106
            $template = $this->Parse($template);
107
        }
108
        // Write an event log if debugging is enabled and there is something to log
109
        if ($this->debug && $this->debugLog) {
110
            $modx->logEvent($this->curPass, 1, $this->createEventLog(), $this->name . ' ' . $this->version);
111
            $this->debugLog = false;
112
        }
113
114
        // Return the processed template
115
        return $template;
116
    }
117
118
    // Parser: Tag detection and replacements
119
    /**
120
     * @param string $template
121
     * @return mixed|string
122
     */
123
    public function ParseValues($template = '')
124
    {
125
        global $modx;
126
127
        $this->curPass = $this->curPass + 1;
128
        $st = md5($template);
129
130
        $this->LogPass();
131
132
        // MODX Chunks
133
        if (preg_match_all('~(?<!(?:then|else)=`){{([^:\+{}]+)([^{}]*?)}}~s', $template, $matches)) {
134
            $this->Log('MODX Chunks -> Merging all chunk tags');
135
            $count = count($matches[0]);
136
            $var_search = array();
137
            $var_replace = array();
138
            for ($i = 0; $i < $count; $i++) {
139
                $var_search[] = $matches[0][$i];
140
                $input = $matches[1][$i];
141
                $this->Log('MODX Chunk: ' . $input);
142
                $input = $modx->mergeChunkContent('{{' . $input . '}}');
143
                $var_replace[] = $this->Filter($input, $matches[2][$i]);
144
            }
145
            $template = str_replace($var_search, $var_replace, $template);
146
        }
147
148
        // MODx Snippets
149
        //if ( preg_match_all('~\[(\[|!)([^\[]*?)(!|\])\]~s',$template, $matches)) {
150
        if (preg_match_all('~(?<!(?:then|else)=`)\[(\[)([^\[]*?)(\])\]~s', $template, $matches)) {
151
            $count = count($matches[0]);
152
            $var_search = array();
153
            $var_replace = array();
154
155
            // for each detected snippet
156
            for ($i = 0; $i < $count; $i++) {
157
                $snippet = $matches[2][$i]; // snippet call
158
                $this->Log("MODx Snippet -> " . $snippet);
159
160
                // Let MODx evaluate snippet
161
                $replace = $modx->evalSnippets("[[" . $snippet . "]]");
162
                $this->LogSnippet($replace);
163
164
                // Replace values
165
                $var_search[] = $matches[0][$i];
166
                $var_replace[] = $replace;
167
            }
168
            $template = str_replace($var_search, $var_replace, $template);
169
        }
170
171
        // PHx / MODx Tags
172
        if (preg_match_all('~\[(\+|\*|\()([^:\+\[\]]+)([^\[\]]*?)(\1|\))\]~s', $template, $matches)) {
173
174
            //$matches[0] // Complete string that's need to be replaced
175
            //$matches[1] // Type
176
            //$matches[2] // The placeholder(s)
177
            //$matches[3] // The modifiers
178
            //$matches[4] // Type (end character)
179
180
            $count = count($matches[0]);
181
            $var_search = array();
182
            $var_replace = array();
183
            for ($i = 0; $i < $count; $i++) {
184
                $input = $matches[2][$i];
185
                $modifiers = $matches[3][$i];
186
                $var_search[] = $matches[0][$i];
187
                switch ($matches[1][$i]) {
188
                    // Document / Template Variable eXtended
189
                    case "*":
190
                        $this->Log("MODx TV/DV: " . $input);
191
                        $input = $modx->mergeDocumentContent("[*" . $input . "*]");
192
                        $replace = $this->Filter($input, $modifiers);
193
                        break;
194
                    // MODx Setting eXtended
195
                    case "(":
196
                        $this->Log("MODx Setting variable: " . $input);
197
                        $input = $modx->mergeSettingsContent("[(" . $input . ")]");
198
                        $replace = $this->Filter($input, $modifiers);
199
                        break;
200
                    // MODx Placeholder eXtended
201
                    default:
202
                        $this->Log("MODx / PHx placeholder variable: " . $input);
203
                        // Check if placeholder is set
204
                        if (!array_key_exists($input, $this->placeholders) && !array_key_exists($input,
205
                                $modx->placeholders)
206
                        ) {
207
                            // not set so try again later.
208
                            $input = '';
209
                        } else {
210
                            // is set, get value and run filter
211
                            $input = $this->getPHxVariable($input);
212
                        }
213
                        $replace = $this->Filter($input, $modifiers);
214
                        break;
215
                }
216
                $var_replace[] = $replace;
217
            }
218
            $template = str_replace($var_search, $var_replace, $template);
219
        }
220
        $et = md5($template); // Post-process template hash
221
222
        // Log an event if this was the maximum pass
223
        if ($this->curPass == $this->maxPasses) {
224
            $this->Log("Max passes reached. infinite loop protection so exiting.\n If you need the extra passes set the max passes to the highest count of nested tags in your template.");
225
        }
226
        // If this pass is not at maximum passes and the template hash is not the same, get at it again.
227
        if (($this->curPass < $this->maxPasses) && ($st != $et)) {
228
            $template = $this->ParseValues($template);
229
        }
230
231
        return $template;
232
    }
233
234
    // Parser: modifier detection and eXtended processing if needed
235
    /**
236
     * @param $input
237
     * @param $modifiers
238
     * @return mixed|null|string
239
     */
240
    public function Filter($input, $modifiers)
241
    {
242
        global $modx;
243
        $output = $input;
244
        $this->Log("  |--- Input = '" . $output . "'");
245
        if (preg_match_all('~:([^:=]+)(?:=`(.*?)`(?=:[^:=]+|$))?~s', $modifiers, $matches)) {
246
            $modifier_cmd = $matches[1]; // modifier command
247
            $modifier_value = $matches[2]; // modifier value
248
            $count = count($modifier_cmd);
249
            $condition = array();
250
            for ($i = 0; $i < $count; $i++) {
251
                $output = trim($output);
252
                $this->Log("  |--- Modifier = '" . $modifier_cmd[$i] . "'");
253
                if ($modifier_value[$i] != '') {
254
                    $this->Log("  |--- Options = '" . $modifier_value[$i] . "'");
255
                }
256
                switch ($modifier_cmd[$i]) {
257
                    #####  Conditional Modifiers
258
                    case "input":
259
                    case "if":
260
                        $output = $modifier_value[$i];
261
                        break;
262
                    case "equals":
263
                    case "is":
264
                    case "eq":
265
                        $condition[] = intval(($output == $modifier_value[$i]));
266
                        break;
267
                    case "empty":
268
                        $condition[] = intval(empty($output));
269
                        break;
270
                    case "notequals":
271
                    case "isnot":
272
                    case "isnt":
273
                    case "ne":
274
                        $condition[] = intval(($output != $modifier_value[$i]));
275
                        break;
276
                    case "isgreaterthan":
277
                    case "isgt":
278
                    case "eg":
279
                        $condition[] = intval(($output >= $modifier_value[$i]));
280
                        break;
281
                    case "islowerthan":
282
                    case "islt":
283
                    case "el":
284
                        $condition[] = intval(($output <= $modifier_value[$i]));
285
                        break;
286
                    case "greaterthan":
287
                    case "gt":
288
                        $condition[] = intval(($output > $modifier_value[$i]));
289
                        break;
290
                    case "lowerthan":
291
                    case "lt":
292
                        $condition[] = intval(($output < $modifier_value[$i]));
293
                        break;
294
                    case "isinrole":
295
                    case "ir":
296
                    case "memberof":
297
                    case "mo": // Is Member Of  (same as inrole but this one can be stringed as a conditional)
298
                        if ($output == "&_PHX_INTERNAL_&") {
299
                            $output = $this->user["id"];
300
                        }
301
                        $grps = ($this->strlen($modifier_value[$i]) > 0) ? explode(",", $modifier_value[$i]) : array();
302
                        $condition[] = intval($this->isMemberOfWebGroupByUserId($output, $grps));
303
                        break;
304
                    case "or":
305
                        $condition[] = "||";
306
                        break;
307
                    case "and":
308
                        $condition[] = "&&";
309
                        break;
310
                    case "show":
311
                        $conditional = implode(' ', $condition);
312
                        $isvalid = intval($this->runCode($conditional));
313
                        if (!$isvalid) {
314
                            $output = null;
315
                        }
316
                        break;
317
                    case "then":
318
                        $conditional = implode(' ', $condition);
319
                        $isvalid = intval($this->runCode($conditional));
320
                        if ($isvalid) {
321
                            $output = $modifier_value[$i];
322
                        } else {
323
                            $output = null;
324
                        }
325
                        break;
326
                    case "else":
327
                        $conditional = implode(' ', $condition);
0 ignored issues
show
Unused Code introduced by
$conditional 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...
328
                        $isvalid = intval($this->runCode($condition));
329
                        if (!$isvalid) {
330
                            $output = $modifier_value[$i];
331
                        }
332
                        break;
333
                    case "select":
334
                        $raw = explode("&", $modifier_value[$i]);
335
                        $map = array();
336
                        $count = count($raw);
337
                        for ($m = 0; $m < $count; $m++) {
338
                            $mi = explode("=", $raw[$m]);
339
                            $map[$mi[0]] = $mi[1];
340
                        }
341
                        $output = $map[$output];
342
                        break;
343
                    ##### End of Conditional Modifiers
344
345
                    #####  String Modifiers
346
                    case "default":
347
                        $output = ($output === '') ? $modifier_value[0] : $output;
348
                        break;
349
                    case "lcase":
350
                    case "strtolower":
351
                        $output = $this->strtolower($output);
352
                        break;
353
                    case "ucase":
354
                    case "strtoupper":
355
                        $output = $this->strtoupper($output);
356
                        break;
357
                    case "ucfirst":
358
                        $output = $this->ucfirst($output);
359
                        break;
360
                    case "lcfirst":
361
                        $output = $this->lcfirst($output);
362
                        break;
363
                    case "ucwords":
364
                        $output = $this->ucwords($output);
365
                        break;
366
                    case "htmlent":
367
                    case "htmlentities":
368
                        $output = htmlentities($output, ENT_QUOTES, $modx->config['modx_charset']);
369
                        break;
370
                    case "html_entity_decode":
371
                        $output = html_entity_decode($output, ENT_QUOTES, $modx->config['modx_charset']);
372
                        break;
373
                    case "esc":
374
                        $output = preg_replace("/&amp;(#[0-9]+|[a-z]+);/i", "&$1;", APIHelpers::e($output));
375
                        $output = str_replace(array("[", "]", "`"), array("&#91;", "&#93;", "&#96;"), $output);
376
                        break;
377
                    case "strip":
378
                        $output = preg_replace("~([\n\r\t\s]+)~", " ", $output);
379
                        break;
380
                    case "notags":
381
                    case "strip_tags":
382
                        $output = strip_tags($output);
383
                        break;
384
                    case "length":
385
                    case "len":
386
                    case "strlen":
387
                        $output = $this->strlen($output);
388
                        break;
389
                    case "reverse":
390
                    case "strrev":
391
                        $output = $this->strrev($output);
392
                        break;
393
                    case "wordwrap": // default: 70
394
                        $wrapat = intval($modifier_value[$i]) ? intval($modifier_value[$i]) : 70;
395
                        $output = preg_replace_callback("@(\b\w+\b)@",function($m) use($wrapat) {return wordwrap($m[1],$wrapat,' ',1);},$output);
396
                        break;
397
                    case "limit": // default: 100
398
                        $limit = intval($modifier_value[$i]) ? intval($modifier_value[$i]) : 100;
399
                        $output = $this->substr($output, 0, $limit);
400
                        break;
401
                    case "str_shuffle":
402
                    case "shuffle":
403
                        $output = $this->str_shuffle($output);
404
                        break;
405
                    case "str_word_count":
406
                    case "word_count":
407
                    case "wordcount":
408
                        $output = $this->str_word_count($output);
409
                        break;
410
411
                    #####  Special functions
412
                    case "math":
413
                        $filter = preg_replace("~([a-zA-Z\n\r\t\s])~", "", $modifier_value[$i]);
414
                        $filter = str_replace("?", $output, $filter);
415
                        $output = eval("return " . $filter . ";");
416
                        break;
417
                    case "isnotempty":
418
                        if (!empty($output)) {
419
                            $output = $modifier_value[$i];
420
                        }
421
                        break;
422
                    case "isempty":
423
                    case "ifempty":
424
                        if (empty($output)) {
425
                            $output = $modifier_value[$i];
426
                        }
427
                        break;
428
                    case "nl2br":
429
                        $output = nl2br($output);
430
                        break;
431
                    case "date":
432
                        $output = strftime($modifier_value[$i], 0 + $output);
433
                        break;
434
                    case "set":
435
                        $c = $i + 1;
436
                        if ($count > $c && $modifier_cmd[$c] == "value") {
437
                            $output = preg_replace("~([^a-zA-Z0-9])~", "", $modifier_value[$i]);
438
                        }
439
                        break;
440
                    case "value":
441
                        if ($i > 0 && $modifier_cmd[$i - 1] == "set") {
442
                            $modx->SetPlaceholder("phx." . $output, $modifier_value[$i]);
443
                        }
444
                        $output = null;
445
                        break;
446
                    case "md5":
447
                        $output = md5($output);
448
                        break;
449
                    case "userinfo":
450
                        if ($output == "&_PHX_INTERNAL_&") {
451
                            $output = $this->user["id"];
452
                        }
453
                        $output = $this->ModUser($output, $modifier_value[$i]);
454
                        break;
455
                    case "inrole": // deprecated
456
                        if ($output == "&_PHX_INTERNAL_&") {
457
                            $output = $this->user["id"];
458
                        }
459
                        $grps = ($this->strlen($modifier_value[$i]) > 0) ? explode(",", $modifier_value[$i]) : array();
460
                        $output = intval($this->isMemberOfWebGroupByUserId($output, $grps));
461
                        break;
462
463
                    // If we haven't yet found the modifier, let's look elsewhere
464
                    default:
465
                        $snippet = '';
466
                        // modified by Anton Kuzmin (23.06.2010) //
467
                        $snippetName = 'phx:' . $modifier_cmd[$i];
468
                        if (isset($modx->snippetCache[$snippetName])) {
469
                            $snippet = $modx->snippetCache[$snippetName];
470
                        } else { // not in cache so let's check the db
471
                            $sql = "SELECT snippet FROM " . $modx->getFullTableName("site_snippets") . " WHERE " . $modx->getFullTableName("site_snippets") . ".name='" . $modx->db->escape($snippetName) . "';";
472
                            $result = $modx->dbQuery($sql);
473
                            if ($modx->recordCount($result) == 1) {
474
                                $row = $modx->fetchRow($result);
475
                                $snippet = $modx->snippetCache[$row['name']] = $row['snippet'];
476
                                $this->Log("  |--- DB -> Custom Modifier");
477
                            } else {
478
                                if ($modx->recordCount($result) == 0) { // If snippet not found, look in the modifiers folder
479
                                    $filename = $modx->config['rb_base_dir'] . 'plugins/phx/modifiers/' . $modifier_cmd[$i] . '.phx.php';
480
                                    if (@file_exists($filename)) {
481
                                        $file_contents = @file_get_contents($filename);
482
                                        $file_contents = str_replace('<' . '?php', '', $file_contents);
483
                                        $file_contents = str_replace('?' . '>', '', $file_contents);
484
                                        $file_contents = str_replace('<?', '', $file_contents);
485
                                        $snippet = $modx->snippetCache[$snippetName] = $file_contents;
486
                                        $modx->snippetCache[$snippetName . 'Props'] = '';
487
                                        $this->Log("  |--- File ($filename) -> Custom Modifier");
488
                                    } else {
489
                                        $this->Log("  |--- PHX Error:  {$modifier_cmd[$i]} could not be found");
490
                                    }
491
                                }
492
                            }
493
                        }
494
                        $cm = $snippet;
495
                        // end //
496
497
                        if (!empty($cm)) {
498
                            ob_start();
499
                            $options = $modifier_value[$i];
500
                            $custom = eval($cm);
501
                            $msg = ob_get_contents();
502
                            $output = $msg . $custom;
503
                            ob_end_clean();
504
                        } else {
505
                            $output = '';
506
                        }
507
                        break;
508
                }
509
                if (count($condition)) {
510
                    $this->Log("  |--- Condition = '" . $condition[count($condition) - 1] . "'");
511
                }
512
                $this->Log("  |--- Output = '" . $output . "'");
513
            }
514
        }
515
516
        return $output;
517
    }
518
519
    private function runCode($code){
520
        return eval("return (" . $code . ");");
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...
521
    }
522
    // Event logging (debug)
523
    /**
524
     * @return string
525
     */
526
    public function createEventLog()
527
    {
528
        if (!empty($this->console)) {
529
            $console = implode("\n", $this->console);
530
            $this->console = array();
531
532
            return '<pre style="overflow: auto;">' . $console . '</pre>';
533
        }
534
    }
535
536
    // Returns a cleaned string escaping the HTML and special MODx characters
537
    /**
538
     * @param $string
539
     * @return array|mixed|string
540
     */
541
    public function LogClean($string)
542
    {
543
        $string = preg_replace("/&amp;(#[0-9]+|[a-z]+);/i", "&$1;", $string);
544
        $string = APIHelpers::sanitarTag($string);
545
546
        return $string;
547
    }
548
549
    // Simple log entry
550
    /**
551
     * @param $string
552
     */
553
    public function Log($string)
554
    {
555
        if ($this->debug) {
556
            $this->debugLog = true;
557
            $this->console[] = (count($this->console) + 1 - $this->curPass) . " [" . strftime("%H:%M:%S",
558
                    time()) . "] " . $this->LogClean($string);
559
        }
560
    }
561
562
    // Log snippet output
563
    /**
564
     * @param $string
565
     */
566
    public function LogSnippet($string)
567
    {
568
        if ($this->debug) {
569
            $this->debugLog = true;
570
            $this->console[] = (count($this->console) + 1 - $this->curPass) . " [" . strftime("%H:%M:%S",
571
                    time()) . "] " . "  |--- Returns: <div style='margin: 10px;'>" . $this->LogClean($string) . "</div>";
572
        }
573
    }
574
575
    // Log pass
576
    public function LogPass()
577
    {
578
        $this->console[] = "<div style='margin: 2px;margin-top: 5px;border-bottom: 1px solid black;'>Pass " . $this->curPass . "</div>";
579
    }
580
581
    // Log pass
582
    /**
583
     * @param $string
584
     */
585
    public function LogSource($string)
586
    {
587
        $this->console[] = "<div style='margin: 2px;margin-top: 5px;border-bottom: 1px solid black;'>Source:</div>" . $this->LogClean($string);
588
    }
589
590
591
    // Returns the specified field from the user record
592
    // positive userid = manager, negative integer = webuser
593
    /**
594
     * @param $userid
595
     * @param $field
596
     * @return mixed
597
     */
598
    public function ModUser($userid, $field)
599
    {
600
        global $modx;
601
        if (!array_key_exists($userid, $this->cache["ui"])) {
602
            if (intval($userid) < 0) {
603
                $user = $modx->getWebUserInfo(-($userid));
604
            } else {
605
                $user = $modx->getUserInfo($userid);
606
            }
607
            $this->cache["ui"][$userid] = $user;
608
        } else {
609
            $user = $this->cache["ui"][$userid];
610
        }
611
612
        return $user[$field];
613
    }
614
615
    // Returns true if the user id is in one the specified webgroups
616
    /**
617
     * @param int $userid
618
     * @param array $groupNames
619
     * @return bool
620
     */
621
    public function isMemberOfWebGroupByUserId($userid = 0, $groupNames = array())
622
    {
623
        global $modx;
624
625
        // if $groupNames is not an array return false
626
        if (!is_array($groupNames)) {
627
            return false;
628
        }
629
630
        // if the user id is a negative number make it positive
631
        if (intval($userid) < 0) {
632
            $userid = -($userid);
633
        }
634
635
        // Creates an array with all webgroups the user id is in
636
        if (!array_key_exists($userid, $this->cache["mo"])) {
637
            $tbl = $modx->getFullTableName("webgroup_names");
638
            $tbl2 = $modx->getFullTableName("web_groups");
639
            $sql = "SELECT wgn.name FROM $tbl wgn INNER JOIN $tbl2 wg ON wg.webgroup=wgn.id AND wg.webuser='" . $userid . "'";
640
            $this->cache["mo"][$userid] = $grpNames = $modx->db->getColumn("name", $sql);
641
        } else {
642
            $grpNames = $this->cache["mo"][$userid];
643
        }
644
        // Check if a supplied group matches a webgroup from the array we just created
645
        foreach ($groupNames as $k => $v) {
646
            if (in_array(trim($v), $grpNames)) {
647
                return true;
648
            }
649
        }
650
651
        // If we get here the above logic did not find a match, so return false
652
        return false;
653
    }
654
655
    // Returns the value of a PHx/MODx placeholder.
656
    /**
657
     * @param $name
658
     * @return mixed|string
659
     */
660
    public function getPHxVariable($name)
661
    {
662
        global $modx;
663
        // Check if this variable is created by PHx
664
        if (array_key_exists($name, $this->placeholders)) {
665
            // Return the value from PHx
666
            return $this->placeholders[$name];
667
        } else {
668
            // Return the value from MODx
669
            return $modx->getPlaceholder($name);
670
        }
671
    }
672
673
    // Sets a placeholder variable which can only be access by PHx
674
    /**
675
     * @param $name
676
     * @param $value
677
     */
678
    public function setPHxVariable($name, $value)
679
    {
680
        if ($name != "phx") {
681
            $this->placeholders[$name] = $value;
682
        }
683
    }
684
685
    //mbstring
686
    /**
687
     * @param $str
688
     * @param $s
689
     * @param null $l
690
     * @return string
691
     */
692
    public function substr($str, $s, $l = null)
693
    {
694
        if (function_exists('mb_substr')) {
695
            return mb_substr($str, $s, $l);
696
        }
697
698
        return substr($str, $s, $l);
699
    }
700
701
    /**
702
     * @param $str
703
     * @return int
704
     */
705
    public function strlen($str)
706
    {
707
        if (function_exists('mb_strlen')) {
708
            return mb_strlen($str);
709
        }
710
711
        return strlen($str);
712
    }
713
714
    /**
715
     * @param $str
716
     * @return string
717
     */
718
    public function strtolower($str)
719
    {
720
        if (function_exists('mb_strtolower')) {
721
            return mb_strtolower($str);
722
        }
723
724
        return strtolower($str);
725
    }
726
727
    /**
728
     * @param $str
729
     * @return string
730
     */
731
    public function strtoupper($str)
732
    {
733
        if (function_exists('mb_strtoupper')) {
734
            return mb_strtoupper($str);
735
        }
736
737
        return strtoupper($str);
738
    }
739
740
    /**
741
     * @param $str
742
     * @return string
743
     */
744
    public function ucfirst($str)
745
    {
746
        if (function_exists('mb_strtoupper') && function_exists('mb_substr') && function_exists('mb_strlen')) {
747
            return mb_strtoupper(mb_substr($str, 0, 1)) . mb_substr($str, 1, mb_strlen($str));
748
        }
749
750
        return ucfirst($str);
751
    }
752
753
    /**
754
     * @param $str
755
     * @return string
756
     */
757
    public function lcfirst($str)
758
    {
759
        if (function_exists('mb_strtolower') && function_exists('mb_substr') && function_exists('mb_strlen')) {
760
            return mb_strtolower(mb_substr($str, 0, 1)) . mb_substr($str, 1, mb_strlen($str));
761
        }
762
763
        return lcfirst($str);
764
    }
765
766
    /**
767
     * @param $str
768
     * @return string
769
     */
770
    public function ucwords($str)
771
    {
772
        if (function_exists('mb_convert_case')) {
773
            return mb_convert_case($str, MB_CASE_TITLE);
774
        }
775
776
        return ucwords($str);
777
    }
778
779
    /**
780
     * @param $str
781
     * @return string
782
     */
783
    public function strrev($str)
784
    {
785
        preg_match_all('/./us', $str, $ar);
786
787
        return implode(array_reverse($ar[0]));
788
    }
789
790
    /**
791
     * @param $str
792
     * @return string
793
     */
794
    public function str_shuffle($str)
795
    {
796
        preg_match_all('/./us', $str, $ar);
797
        shuffle($ar[0]);
798
799
        return implode($ar[0]);
800
    }
801
802
    /**
803
     * @param $str
804
     * @return int
805
     */
806
    public function str_word_count($str)
807
    {
808
        return count(preg_split('~[^\p{L}\p{N}\']+~u', $str));
809
    }
810
}
811