Issues (1626)

Security Analysis    not enabled

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

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

includes/js/class.JavaScriptPacker.inc.php (36 issues)

Upgrade to new PHP Analysis Engine

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

1
<?php
2
/* 25 October 2011. version 1.1-FF4
3
 *
4
 * This is the php version of the Dean Edwards JavaScript's Packer,
5
 * Based on :
6
 *
7
 * ParseMaster, version 1.0.2 (2005-08-19) Copyright 2005, Dean Edwards
8
 * a multi-pattern parser.
9
 * KNOWN BUG: erroneous behavior when using escapeChar with a replacement
10
 * value that is a function
11
 *
12
 * packer, version 2.0.2 (2005-08-19) Copyright 2004-2005, Dean Edwards
13
 *
14
 * License: http://creativecommons.org/licenses/LGPL/2.1/
15
 *
16
 * Ported to PHP by Nicolas Martin.
17
 *
18
 * ----------------------------------------------------------------------
19
 * changelog:
20
 * 1.1 : correct a bug, '\0' packed then unpacked becomes '\'.
21
 * 1.1-FF4 : Firefox 4 fix, Copyright 2011, Mieczyslaw Nalewaj
22
 * ----------------------------------------------------------------------
23
 *
24
 * examples of usage :
25
 * $myPacker = new JavaScriptPacker($script, 62, true, false);
26
 * $packed = $myPacker->pack();
27
 *
28
 * or
29
 *
30
 * $myPacker = new JavaScriptPacker($script, 'Normal', true, false);
31
 * $packed = $myPacker->pack();
32
 *
33
 * or (default values)
34
 *
35
 * $myPacker = new JavaScriptPacker($script);
36
 * $packed = $myPacker->pack();
37
 *
38
 *
39
 * params of the constructor :
40
 * $script:       the JavaScript to pack, string.
41
 * $encoding:     level of encoding, int or string :
42
 *                0,10,62,95 or 'None', 'Numeric', 'Normal', 'High ASCII'.
43
 *                default: 62.
44
 * $fastDecode:   include the fast decoder in the packed result, boolean.
45
 *                default : true.
46
 * $specialChars: if you are flagged your private and local variables
47
 *                in the script, boolean.
48
 *                default: false.
49
 *
50
 * The pack() method return the compressed JavasScript, as a string.
51
 *
52
 * see http://dean.edwards.name/packer/usage/ for more information.
53
 *
54
 * Notes :
55
 * # need PHP 5 . Tested with PHP 5.1.2, 5.1.3, 5.1.4, 5.2.3
56
 *
57
 * # The packed result may be different than with the Dean Edwards
58
 *   version, but with the same length. The reason is that the PHP
59
 *   function usort to sort array don't necessarily preserve the
60
 *   original order of two equal member. The Javascript sort function
61
 *   in fact preserve this order (but that's not require by the
62
 *   ECMAScript standard). So the encoded keywords order can be
63
 *   different in the two results.
64
 *
65
 * # Be careful with the 'High ASCII' Level encoding if you use
66
 *   UTF-8 in your files...
67
 */
68
69
70
class JavaScriptPacker
0 ignored issues
show
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...
71
{
72
    // constants
73
    const IGNORE = '$1';
74
75
    // validate parameters
76
    private $_script = '';
77
    private $_encoding = 62;
78
    private $_fastDecode = true;
79
    private $_specialChars = false;
80
81
    private $LITERAL_ENCODING = array(
82
        'None' => 0,
83
        'Numeric' => 10,
84
        'Normal' => 62,
85
        'High ASCII' => 95
86
    );
87
88
    public function __construct($_script, $_encoding = 62, $_fastDecode = true, $_specialChars = false)
89
    {
90
        $this->_script = $_script . "\n";
91
        if (array_key_exists($_encoding, $this->LITERAL_ENCODING))
92
            $_encoding = $this->LITERAL_ENCODING[$_encoding];
93
        $this->_encoding = min((int) $_encoding, 95);
94
        $this->_fastDecode = $_fastDecode;
95
        $this->_specialChars = $_specialChars;
96
    }
97
98
    public function pack()
99
    {
100
        $this->_addParser('_basicCompression');
101
        if ($this->_specialChars)
102
            $this->_addParser('_encodeSpecialChars');
103
        if ($this->_encoding)
104
            $this->_addParser('_encodeKeywords');
105
106
        // go!
107
        return $this->_pack($this->_script);
108
    }
109
110
    // apply all parsing routines
111
    private function _pack($script)
112
    {
113
        for ($i = 0; isset($this->_parsers[$i]); $i++) {
114
            $script = call_user_func(array(&$this, $this->_parsers[$i]), $script);
115
        }
116
117
        return $script;
118
    }
119
120
    // keep a list of parsing functions, they'll be executed all at once
121
    private $_parsers = array();
122
    private function _addParser($parser)
123
    {
124
        $this->_parsers[] = $parser;
125
    }
126
127
    // zero encoding - just removal of white space and comments
128
    private function _basicCompression($script)
0 ignored issues
show
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...
129
    {
130
        $parser = new ParseMaster();
131
        // make safe
132
        $parser->escapeChar = '\\';
133
        // protect strings
134
        $parser->add('/\'[^\'\\n\\r]*\'/', self::IGNORE);
135
        $parser->add('/"[^"\\n\\r]*"/', self::IGNORE);
136
        // remove comments
137
        $parser->add('/\\/\\/[^\\n\\r]*[\\n\\r]/', ' ');
138
        $parser->add('/\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\//', ' ');
139
        // protect regular expressions
140
        $parser->add('/\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)/', '$2'); // IGNORE
141
        $parser->add('/[^\\w\\x24\\/\'"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?/', self::IGNORE);
142
        // remove: ;;; doSomething();
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...
143
        if ($this->_specialChars) $parser->add('/;;;[^\\n\\r]+[\\n\\r]/');
144
        // remove redundant semi-colons
145
        $parser->add('/\\(;;\\)/', self::IGNORE); // protect for (;;) loops
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% 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...
146
        $parser->add('/;+\\s*([};])/', '$2');
147
        // apply the above
148
        $script = $parser->exec($script);
149
150
        // remove white-space
151
        $parser->add('/(\\b|\\x24)\\s+(\\b|\\x24)/', '$2 $3');
152
        $parser->add('/([+\\-])\\s+([+\\-])/', '$2 $3');
153
        $parser->add('/\\s+/', '');
154
        // done
155
        return $parser->exec($script);
156
    }
157
158
    private function _encodeSpecialChars($script)
0 ignored issues
show
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...
159
    {
160
        $parser = new ParseMaster();
161
        // replace: $name -> n, $$name -> na
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...
162
        $parser->add('/((\\x24+)([a-zA-Z$_]+))(\\d*)/',
163
                     array('fn' => '_replace_name')
0 ignored issues
show
array('fn' => '_replace_name') is of type array<string,string,{"fn":"string"}>, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
164
        );
165
        // replace: _name -> _0, double-underscore (__name) is ignored
166
        $regexp = '/\\b_[A-Za-z\\d]\\w*/';
167
        // build the word list
168
        $keywords = $this->_analyze($script, $regexp, '_encodePrivate');
169
        // quick ref
170
        $encoded = $keywords['encoded'];
171
172
        $parser->add($regexp,
173
            array(
0 ignored issues
show
array('fn' => '_replace_...d', 'data' => $encoded) is of type array<string,string|arra...tring","data":"array"}>, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
174
                'fn' => '_replace_encoded',
175
                'data' => $encoded
176
            )
177
        );
178
179
        return $parser->exec($script);
180
    }
181
182
    private function _encodeKeywords($script)
0 ignored issues
show
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...
183
    {
184
        // escape high-ascii values already in the script (i.e. in strings)
185
        if ($this->_encoding > 62)
186
            $script = $this->_escape95($script);
187
        // create the parser
188
        $parser = new ParseMaster();
189
        $encode = $this->_getEncoder($this->_encoding);
190
        // for high-ascii, don't encode single character low-ascii
191
        $regexp = ($this->_encoding > 62) ? '/\\w\\w+/' : '/\\w+/';
192
        // build the word list
193
        $keywords = $this->_analyze($script, $regexp, $encode);
194
        $encoded = $keywords['encoded'];
195
196
        // encode
197
        $parser->add($regexp,
198
            array(
0 ignored issues
show
array('fn' => '_replace_...d', 'data' => $encoded) is of type array<string,string|arra...tring","data":"array"}>, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
199
                'fn' => '_replace_encoded',
200
                'data' => $encoded
201
            )
202
        );
203
        if (empty($script)) return $script;
204
        else {
205
            //$res = $parser->exec($script);
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...
206
            //$res = $this->_bootStrap($res, $keywords);
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...
207
            //return $res;
208
            return $this->_bootStrap($parser->exec($script), $keywords);
209
        }
210
    }
211
212
    private function _analyze($script, $regexp, $encode)
213
    {
214
        // analyse
215
        // retreive all words in the script
216
        $all = array();
217
        preg_match_all($regexp, $script, $all);
218
        $_sorted = array(); // list of words sorted by frequency
219
        $_encoded = array(); // dictionary of word->encoding
220
        $_protected = array(); // instances of "protected" words
221
        $all = $all[0]; // simulate the javascript comportement of global match
222
        if (!empty($all)) {
223
            $unsorted = array(); // same list, not sorted
224
            $protected = array(); // "protected" words (dictionary of word->"word")
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
225
            $value = array(); // dictionary of charCode->encoding (eg. 256->ff)
0 ignored issues
show
$value is not used, you could remove the assignment.

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

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

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

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

Loading history...
226
            $this->_count = array(); // word->count
227
            $i = count($all); $j = 0; //$word = null;
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...
228
            // count the occurrences - used for sorting later
229
            do {
230
                --$i;
231
                $word = '$' . $all[$i];
232
                if (!isset($this->_count[$word])) {
233
                    $this->_count[$word] = 0;
234
                    $unsorted[$j] = $word;
235
                    // make a dictionary of all of the protected words in this script
236
                    //  these are words that might be mistaken for encoding
237
                    //if (is_string($encode) && method_exists($this, $encode))
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...
238
                    $values[$j] = call_user_func(array(&$this, $encode), $j);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$values was never initialized. Although not strictly required by PHP, it is generally a good practice to add $values = 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...
239
                    $protected['$' . $values[$j]] = $j++;
0 ignored issues
show
The variable $values 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...
240
                }
241
                // increment the word counter
242
                $this->_count[$word]++;
243
            } while ($i > 0);
244
            // prepare to sort the word list, first we must protect
245
            //  words that are also used as codes. we assign them a code
246
            //  equivalent to the word itself.
247
            // e.g. if "do" falls within our encoding range
248
            //      then we store keywords["do"] = "do";
249
            // this avoids problems when decoding
250
            $i = count($unsorted);
251
            do {
252
                $word = $unsorted[--$i];
253
                if (isset($protected[$word]) /*!= null*/) {
254
                    $_sorted[$protected[$word]] = substr($word, 1);
255
                    $_protected[$protected[$word]] = true;
256
                    $this->_count[$word] = 0;
257
                }
258
            } while ($i);
259
260
            // sort the words by frequency
261
            // Note: the javascript and php version of sort can be different :
262
            // in php manual, usort :
263
            // " If two members compare as equal,
264
            // their order in the sorted array is undefined."
265
            // so the final packed script is different of the Dean's javascript version
266
            // but equivalent.
267
            // the ECMAscript standard does not guarantee this behaviour,
268
            // and thus not all browsers (e.g. Mozilla versions dating back to at
269
            // least 2003) respect this.
270
            usort($unsorted, array(&$this, '_sortWords'));
271
            $j = 0;
272
            // because there are "protected" words in the list
273
            //  we must add the sorted words around them
274
            do {
275
                if (!isset($_sorted[$i]))
276
                    $_sorted[$i] = substr($unsorted[$j++], 1);
277
                $_encoded[$_sorted[$i]] = $values[$i];
278
            } while (++$i < count($unsorted));
279
        }
280
281
        return array(
282
            'sorted'  => $_sorted,
283
            'encoded' => $_encoded,
284
            'protected' => $_protected);
285
    }
286
287
    private $_count = array();
288
    private function _sortWords($match1, $match2)
289
    {
290
        return $this->_count[$match2] - $this->_count[$match1];
291
    }
292
293
    // build the boot function used for loading and decoding
294
    private function _bootStrap($packed, $keywords)
295
    {
296
        $ENCODE = $this->_safeRegExp('$encode\\($count\\)');
297
298
        // $packed: the packed script
299
        $packed = "'" . $this->_escape($packed) . "'";
300
301
        // $ascii: base for encoding
302
        $ascii = min(count($keywords['sorted']), $this->_encoding);
303
        if ($ascii == 0) $ascii = 1;
304
305
        // $count: number of words contained in the script
306
        $count = count($keywords['sorted']);
307
308
        // $keywords: list of words contained in the script
309
        foreach ($keywords['protected'] as $i=>$value) {
310
            $keywords['sorted'][$i] = '';
311
        }
312
        // convert from a string to an array
313
        ksort($keywords['sorted']);
314
        $keywords = "'" . implode('|', $keywords['sorted']) . "'.split('|')";
315
316
        $encode = ($this->_encoding > 62) ? '_encode95' : $this->_getEncoder($ascii);
317
        $encode = $this->_getJSFunction($encode);
318
        $encode = preg_replace('/_encoding/', '$ascii', $encode);
319
        $encode = preg_replace('/arguments\\.callee/', '$encode', $encode);
320
        $inline = '\\$count' . ($ascii > 10 ? '.toString(\\$ascii)' : '');
321
322
        // $decode: code snippet to speed up decoding
323
        if ($this->_fastDecode) {
324
            // create the decoder
325
            $decode = $this->_getJSFunction('_decodeBody');
326
            if ($this->_encoding > 62)
327
                $decode = preg_replace('/\\\\w/', '[\\xa1-\\xff]', $decode);
328
            // perform the encoding inline for lower ascii values
329
            elseif ($ascii < 36)
330
                $decode = preg_replace($ENCODE, $inline, $decode);
331
            // special case: when $count==0 there are no keywords. I want to keep
332
            //  the basic shape of the unpacking funcion so i'll frig the code...
333
            if ($count == 0)
334
                $decode = preg_replace($this->_safeRegExp('($count)\\s*=\\s*1'), '$1=0', $decode, 1);
335
        }
336
337
        // boot function
338
        $unpack = $this->_getJSFunction('_unpack');
339
        if ($this->_fastDecode) {
340
            // insert the decoder
341
            $this->buffer = $decode;
0 ignored issues
show
The variable $decode 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...
342
            $unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastDecode'), $unpack, 1);
343
        }
344
        $unpack = preg_replace('/"/', "'", $unpack);
345
        if ($this->_encoding > 62) { // high-ascii
346
            // get rid of the word-boundaries for regexp matches
347
            $unpack = preg_replace('/\'\\\\\\\\b\'\s*\\+|\\+\s*\'\\\\\\\\b\'/', '', $unpack);
348
        }
349
        if ($ascii > 36 || $this->_encoding > 62 || $this->_fastDecode) {
350
            // insert the encode function
351
            $this->buffer = $encode;
352
            $unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastEncode'), $unpack, 1);
353
        } else {
354
            // perform the encoding inline
355
            $unpack = preg_replace($ENCODE, $inline, $unpack);
356
        }
357
        // pack the boot function too
358
        $unpackPacker = new JavaScriptPacker($unpack, 0, false, true);
359
        $unpack = $unpackPacker->pack();
360
361
        // arguments
362
        $params = array($packed, $ascii, $count, $keywords);
363
        if ($this->_fastDecode) {
364
            $params[] = 0;
365
            $params[] = '{}';
366
        }
367
        $params = implode(',', $params);
368
369
        // the whole thing
370
        //Firefox 4 fix, old: return 'eval(' . $unpack . '(' . $params . "))\n";
371
        return "(typeof setTimeout=='function'?setTimeout:eval)(" . $unpack . "(" . $params . "));\n";
372
    }
373
374
    private $buffer;
375
    private function _insertFastDecode($match)
0 ignored issues
show
The parameter $match is not used and could be removed.

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

Loading history...
376
    {
377
        return '{' . $this->buffer . ';';
378
    }
379
    private function _insertFastEncode($match)
0 ignored issues
show
The parameter $match is not used and could be removed.

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

Loading history...
380
    {
381
        return '{$encode=' . $this->buffer . ';';
382
    }
383
384
    // mmm.. ..which one do i need ??
385
    private function _getEncoder($ascii)
386
    {
387
        return $ascii > 10 ? $ascii > 36 ? $ascii > 62 ?
388
               '_encode95' : '_encode62' : '_encode36' : '_encode10';
389
    }
390
391
    // zero encoding
392
    // characters: 0123456789
393
    private function _encode10($charCode)
0 ignored issues
show
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...
394
    {
395
        return $charCode;
396
    }
397
398
    // inherent base36 support
399
    // characters: 0123456789abcdefghijklmnopqrstuvwxyz
400
    private function _encode36($charCode)
401
    {
402
        return base_convert($charCode, 10, 36);
403
    }
404
405
    // hitch a ride on base36 and add the upper case alpha characters
406
    // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
407
    private function _encode62($charCode)
408
    {
409
        $res = '';
410
        if ($charCode >= $this->_encoding) {
411
            $res = $this->_encode62((int) ($charCode / $this->_encoding));
412
        }
413
        $charCode = $charCode % $this->_encoding;
414
415
        if ($charCode > 35)
416
            return $res . chr($charCode + 29);
417
        else
418
            return $res . base_convert($charCode, 10, 36);
419
    }
420
421
    // use high-ascii values
422
    // characters: ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ
423
    private function _encode95($charCode)
424
    {
425
        $res = '';
426
        if ($charCode >= $this->_encoding)
427
            $res = $this->_encode95($charCode / $this->_encoding);
428
429
        return $res . chr(($charCode % $this->_encoding) + 161);
430
    }
431
432
    private function _safeRegExp($string)
433
    {
434
        return '/'.preg_replace('/\$/', '\\\$', $string).'/';
435
    }
436
437
    private function _encodePrivate($charCode)
438
    {
439
        return "_" . $charCode;
440
    }
441
442
    // protect characters used by the parser
443
    private function _escape($script)
0 ignored issues
show
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...
444
    {
445
        return preg_replace('/([\\\\\'])/', '\\\$1', $script);
446
    }
447
448
    // protect high-ascii characters already in the script
449
    private function _escape95($script)
0 ignored issues
show
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...
450
    {
451
        return preg_replace_callback(
452
            '/[\\xa1-\\xff]/',
453
            array(&$this, '_escape95Bis'),
454
            $script
455
        );
456
    }
457
    private function _escape95Bis($match)
458
    {
459
        return '\x'.((string) dechex(ord($match)));
460
    }
461
462
    private function _getJSFunction($aName)
463
    {
464
        if (defined('self::JSFUNCTION'.$aName))
465
            return constant('self::JSFUNCTION'.$aName);
466
        else
467
            return '';
468
    }
469
470
    // JavaScript Functions used.
471
    // Note : In Dean's version, these functions are converted
472
    // with 'String(aFunctionName);'.
473
    // This internal conversion complete the original code, ex :
474
    // 'while (aBool) anAction();' is converted to
475
    // 'while (aBool) { anAction(); }'.
476
    // The JavaScript functions below are corrected.
477
478
    // unpacking function - this is the boot strap function
479
    //  data extracted from this packing routine is passed to
480
    //  this function when decoded in the target
481
    // NOTE ! : without the ';' final.
482
    const JSFUNCTION_unpack =
483
484
'function ($packed, $ascii, $count, $keywords, $encode, $decode) {
485
    while ($count--) {
486
        if ($keywords[$count]) {
487
            $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);
488
        }
489
    }
490
491
    return $packed;
492
}';
493
/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
74% 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...
494
'function ($packed, $ascii, $count, $keywords, $encode, $decode) {
495
    while ($count--)
496
        if ($keywords[$count])
497
            $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);
498
499
    return $packed;
500
}';
501
*/
502
503
    // code-snippet inserted into the unpacker to speed up decoding
504
    const JSFUNCTION_decodeBody =
505
//_decode = function () {
506
// does the browser support String.replace where the
507
//  replacement value is a function?
508
509
'    if (!\'\'.replace(/^/, String)) {
510
        // decode all the values we need
511
        while ($count--) {
512
            $decode[$encode($count)] = $keywords[$count] || $encode($count);
513
        }
514
        // global replacement function
515
        $keywords = [function ($encoded) {return $decode[$encoded]}];
516
        // generic match
517
        $encode = function () {return \'\\\\w+\'};
518
        // reset the loop counter -  we are now doing a global replace
519
        $count = 1;
520
    }
521
';
522
//};
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...
523
/*
524
'	if (!\'\'.replace(/^/, String)) {
525
        // decode all the values we need
526
        while ($count--) $decode[$encode($count)] = $keywords[$count] || $encode($count);
527
        // global replacement function
528
        $keywords = [function ($encoded) {return $decode[$encoded]}];
529
        // generic match
530
        $encode = function () {return\'\\\\w+\'};
531
        // reset the loop counter -  we are now doing a global replace
532
        $count = 1;
533
    }';
534
*/
535
536
     // zero encoding
537
     // characters: 0123456789
538
     const JSFUNCTION_encode10 =
539
'function ($charCode) {
540
    return $charCode;
541
}';//;';
542
543
     // inherent base36 support
544
     // characters: 0123456789abcdefghijklmnopqrstuvwxyz
545
     const JSFUNCTION_encode36 =
546
'function ($charCode) {
547
    return $charCode.toString(36);
548
}';//;';
549
550
    // hitch a ride on base36 and add the upper case alpha characters
551
    // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
552
    const JSFUNCTION_encode62 =
553
'function ($charCode) {
554
    return ($charCode < _encoding ? \'\' : arguments.callee(parseInt($charCode / _encoding))) +
555
    (($charCode = $charCode % _encoding) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36));
556
}';
557
558
    // use high-ascii values
559
    // characters: ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ
560
    const JSFUNCTION_encode95 =
561
'function ($charCode) {
562
    return ($charCode < _encoding ? \'\' : arguments.callee($charCode / _encoding)) +
563
        String.fromCharCode($charCode % _encoding + 161);
564
}';
565
566
}
567
568
class ParseMaster
0 ignored issues
show
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...
569
{
570
    public $ignoreCase = false;
571
    public $escapeChar = '';
572
573
    // constants
574
    const EXPRESSION = 0;
575
    const REPLACEMENT = 1;
576
    const LENGTH = 2;
577
578
    // used to determine nesting levels
579
    private $GROUPS = '/\\(/';//g
580
    private $SUB_REPLACE = '/\\$\\d/';
581
    private $INDEXED = '/^\\$\\d+$/';
582
    private $TRIM = '/([\'"])\\1\\.(.*)\\.\\1\\1$/';
0 ignored issues
show
The property $TRIM is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
583
    private $ESCAPE = '/\\\./';//g
584
    private $QUOTE = '/\'/';
585
    private $DELETED = '/\\x01[^\\x01]*\\x01/';//g
586
587
    public function add($expression, $replacement = '')
588
    {
589
        // count the number of sub-expressions
590
        //  - add one because each pattern is itself a sub-expression
591
        $length = 1 + preg_match_all($this->GROUPS, $this->_internalEscape((string) $expression), $out);
592
593
        // treat only strings $replacement
594
        if (is_string($replacement)) {
595
            // does the pattern deal with sub-expressions?
596
            if (preg_match($this->SUB_REPLACE, $replacement)) {
597
                // a simple lookup? (e.g. "$2")
598
                if (preg_match($this->INDEXED, $replacement)) {
599
                    // store the index (used for fast retrieval of matched strings)
600
                    $replacement = (int) (substr($replacement, 1)) - 1;
601
                } else { // a complicated lookup (e.g. "Hello $2 $1")
602
                    // build a function to do the lookup
603
                    $quote = preg_match($this->QUOTE, $this->_internalEscape($replacement))
604
                             ? '"' : "'";
605
                    $replacement = array(
606
                        'fn' => '_backReferences',
607
                        'data' => array(
608
                            'replacement' => $replacement,
609
                            'length' => $length,
610
                            'quote' => $quote
611
                        )
612
                    );
613
                }
614
            }
615
        }
616
        // pass the modified arguments
617
        if (!empty($expression)) $this->_add($expression, $replacement, $length);
618
        else $this->_add('/^$/', $replacement, $length);
619
    }
620
621
    public function exec($string)
0 ignored issues
show
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...
622
    {
623
        // execute the global replacement
624
        $this->_escaped = array();
625
626
        // simulate the _patterns.toSTring of Dean
627
        $regexp = '/';
628
        foreach ($this->_patterns as $reg) {
629
            $regexp .= '(' . substr($reg[self::EXPRESSION], 1, -1) . ')|';
630
        }
631
        $regexp = substr($regexp, 0, -1) . '/';
632
        $regexp .= ($this->ignoreCase) ? 'i' : '';
633
634
        $string = $this->_escape($string, $this->escapeChar);
635
        $string = preg_replace_callback(
636
            $regexp,
637
            array(
638
                &$this,
639
                '_replacement'
640
            ),
641
            $string
642
        );
643
        $string = $this->_unescape($string, $this->escapeChar);
644
645
        return preg_replace($this->DELETED, '', $string);
646
    }
647
648
    public function reset()
649
    {
650
        // clear the patterns collection so that this object may be re-used
651
        $this->_patterns = array();
652
    }
653
654
    // private
655
    private $_escaped = array();  // escaped characters
656
    private $_patterns = array(); // patterns stored by index
657
658
    // create and add a new pattern to the patterns collection
659
    private function _add()
660
    {
661
        $arguments = func_get_args();
662
        $this->_patterns[] = $arguments;
663
    }
664
665
    // this is the global replace function (it's quite complicated)
666
    private function _replacement($arguments)
667
    {
668
        if (empty($arguments)) return '';
669
670
        $i = 1; $j = 0;
671
        // loop through the patterns
672
        while (isset($this->_patterns[$j])) {
673
            $pattern = $this->_patterns[$j++];
674
            // do we have a result?
675
            if (isset($arguments[$i]) && ($arguments[$i] != '')) {
676
                $replacement = $pattern[self::REPLACEMENT];
677
678
                if (is_array($replacement) && isset($replacement['fn'])) {
679
680
                    if (isset($replacement['data'])) $this->buffer = $replacement['data'];
681
                    return call_user_func(array(&$this, $replacement['fn']), $arguments, $i);
682
683
                } elseif (is_int($replacement)) {
684
                    return $arguments[$replacement + $i];
685
686
                }
687
                $delete = ($this->escapeChar == '' ||
688
                           strpos($arguments[$i], $this->escapeChar) === false)
689
                        ? '' : "\x01" . $arguments[$i] . "\x01";
690
691
                return $delete . $replacement;
692
693
            // skip over references to sub-expressions
694
            } else {
695
                $i += $pattern[self::LENGTH];
696
            }
697
        }
698
    }
699
700
    private function _backReferences($match, $offset)
0 ignored issues
show
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...
701
    {
702
        $replacement = $this->buffer['replacement'];
703
        $quote = $this->buffer['quote'];
0 ignored issues
show
$quote 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...
704
        $i = $this->buffer['length'];
705
        while ($i) {
706
            $replacement = str_replace('$'.$i--, $match[$offset + $i], $replacement);
707
        }
708
709
        return $replacement;
710
    }
711
712
    private function _replace_name($match, $offset)
713
    {
714
        $length = strlen($match[$offset + 2]);
715
        $start = $length - max($length - strlen($match[$offset + 3]), 0);
716
717
        return substr($match[$offset + 1], $start, $length) . $match[$offset + 4];
718
    }
719
720
    private function _replace_encoded($match, $offset)
0 ignored issues
show
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...
721
    {
722
        return $this->buffer[$match[$offset]];
723
    }
724
725
726
    // php : we cannot pass additional data to preg_replace_callback,
727
    // and we cannot use &$this in create_function, so let's go to lower level
728
    private $buffer;
729
730
    // encode escaped characters
731
    private function _escape($string, $escapeChar)
0 ignored issues
show
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...
732
    {
733
        if ($escapeChar) {
734
            $this->buffer = $escapeChar;
735
736
            return preg_replace_callback(
737
                '/\\' . $escapeChar . '(.)' .'/',
738
                array(&$this, '_escapeBis'),
739
                $string
740
            );
741
742
        } else {
743
            return $string;
744
        }
745
    }
746
    private function _escapeBis($match)
747
    {
748
        $this->_escaped[] = $match[1];
749
750
        return $this->buffer;
751
    }
752
753
    // decode escaped characters
754
    private function _unescape($string, $escapeChar)
0 ignored issues
show
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...
755
    {
756
        if ($escapeChar) {
757
            $regexp = '/'.'\\'.$escapeChar.'/';
758
            $this->buffer = array('escapeChar'=> $escapeChar, 'i' => 0);
759
760
            return preg_replace_callback(
761
                $regexp,
762
                array(&$this, '_unescapeBis'),
763
                $string
764
            );
765
766
        } else {
767
            return $string;
768
        }
769
    }
770
    private function _unescapeBis()
771
    {
772
        if (isset($this->_escaped[$this->buffer['i']])
773
            && $this->_escaped[$this->buffer['i']] != '')
774
        {
775
             $temp = $this->_escaped[$this->buffer['i']];
776
        } else {
777
            $temp = '';
778
        }
779
        $this->buffer['i']++;
780
781
        return $this->buffer['escapeChar'] . $temp;
782
    }
783
784
    private function _internalEscape($string)
0 ignored issues
show
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...
785
    {
786
        return preg_replace($this->ESCAPE, '', $string);
787
    }
788
}
789