Issues (4122)

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/libs/JavaScriptMinifier.php (1 issue)

Labels
Severity

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
// @codingStandardsIgnoreFile File external to MediaWiki. Ignore coding conventions checks.
3
/**
4
 * JavaScript Minifier
5
 *
6
 * @file
7
 * @author Paul Copperman <[email protected]>
8
 * @license Choose any of Apache, MIT, GPL, LGPL
9
 */
10
11
/**
12
 * This class is meant to safely minify javascript code, while leaving syntactically correct
13
 * programs intact. Other libraries, such as JSMin require a certain coding style to work
14
 * correctly. OTOH, libraries like jsminplus, that do parse the code correctly are rather
15
 * slow, because they construct a complete parse tree before outputting the code minified.
16
 * So this class is meant to allow arbitrary (but syntactically correct) input, while being
17
 * fast enough to be used for on-the-fly minifying.
18
 */
19
class JavaScriptMinifier {
20
21
	/* Class constants */
22
	/* Parsing states.
23
	 * The state machine is only necessary to decide whether to parse a slash as division
24
	 * operator or as regexp literal.
25
	 * States are named after the next expected item. We only distinguish states when the
26
	 * distinction is relevant for our purpose.
27
	 */
28
	const STATEMENT                = 0;
29
	const CONDITION                = 1;
30
	const PROPERTY_ASSIGNMENT      = 2;
31
	const EXPRESSION               = 3;
32
	const EXPRESSION_NO_NL         = 4; // only relevant for semicolon insertion
33
	const EXPRESSION_OP            = 5;
34
	const EXPRESSION_FUNC          = 6;
35
	const EXPRESSION_TERNARY       = 7; // used to determine the role of a colon
36
	const EXPRESSION_TERNARY_OP    = 8;
37
	const EXPRESSION_TERNARY_FUNC  = 9;
38
	const PAREN_EXPRESSION         = 10; // expression which is not on the top level
39
	const PAREN_EXPRESSION_OP      = 11;
40
	const PAREN_EXPRESSION_FUNC    = 12;
41
	const PROPERTY_EXPRESSION      = 13; // expression which is within an object literal
42
	const PROPERTY_EXPRESSION_OP   = 14;
43
	const PROPERTY_EXPRESSION_FUNC = 15;
44
45
	/* Token types */
46
	const TYPE_UN_OP       = 1; // unary operators
47
	const TYPE_INCR_OP     = 2; // ++ and --
48
	const TYPE_BIN_OP      = 3; // binary operators
49
	const TYPE_ADD_OP      = 4; // + and - which can be either unary or binary ops
50
	const TYPE_HOOK        = 5; // ?
51
	const TYPE_COLON       = 6; // :
52
	const TYPE_COMMA       = 7; // ,
53
	const TYPE_SEMICOLON   = 8; // ;
54
	const TYPE_BRACE_OPEN  = 9; // {
55
	const TYPE_BRACE_CLOSE = 10; // }
56
	const TYPE_PAREN_OPEN  = 11; // ( and [
57
	const TYPE_PAREN_CLOSE = 12; // ) and ]
58
	const TYPE_RETURN      = 13; // keywords: break, continue, return, throw
59
	const TYPE_IF          = 14; // keywords: catch, for, with, switch, while, if
60
	const TYPE_DO          = 15; // keywords: case, var, finally, else, do, try
61
	const TYPE_FUNC        = 16; // keywords: function
62
	const TYPE_LITERAL     = 17; // all literals, identifiers and unrecognised tokens
63
64
	// Sanity limit to avoid excessive memory usage
65
	const STACK_LIMIT = 1000;
66
67
	/* Static functions */
68
69
	/**
70
	 * Returns minified JavaScript code.
71
	 *
72
	 * NOTE: $maxLineLength isn't a strict maximum. Longer lines will be produced when
73
	 *       literals (e.g. quoted strings) longer than $maxLineLength are encountered
74
	 *       or when required to guard against semicolon insertion.
75
	 *
76
	 * @param string $s JavaScript code to minify
77
	 * @param bool $statementsOnOwnLine Whether to put each statement on its own line
78
	 * @param int $maxLineLength Maximum length of a single line, or -1 for no maximum.
79
	 * @return String Minified code
80
	 */
81
	public static function minify( $s, $statementsOnOwnLine = false, $maxLineLength = 1000 ) {
82
		// First we declare a few tables that contain our parsing rules
83
84
		// $opChars : characters, which can be combined without whitespace in between them
85
		$opChars = array(
86
			'!' => true,
87
			'"' => true,
88
			'%' => true,
89
			'&' => true,
90
			"'" => true,
91
			'(' => true,
92
			')' => true,
93
			'*' => true,
94
			'+' => true,
95
			',' => true,
96
			'-' => true,
97
			'.' => true,
98
			'/' => true,
99
			':' => true,
100
			';' => true,
101
			'<' => true,
102
			'=' => true,
103
			'>' => true,
104
			'?' => true,
105
			'[' => true,
106
			']' => true,
107
			'^' => true,
108
			'{' => true,
109
			'|' => true,
110
			'}' => true,
111
			'~' => true
112
		);
113
114
		// $tokenTypes : maps keywords and operators to their corresponding token type
115
		$tokenTypes = array(
116
			'!'          => self::TYPE_UN_OP,
117
			'~'          => self::TYPE_UN_OP,
118
			'delete'     => self::TYPE_UN_OP,
119
			'new'        => self::TYPE_UN_OP,
120
			'typeof'     => self::TYPE_UN_OP,
121
			'void'       => self::TYPE_UN_OP,
122
			'++'         => self::TYPE_INCR_OP,
123
			'--'         => self::TYPE_INCR_OP,
124
			'!='         => self::TYPE_BIN_OP,
125
			'!=='        => self::TYPE_BIN_OP,
126
			'%'          => self::TYPE_BIN_OP,
127
			'%='         => self::TYPE_BIN_OP,
128
			'&'          => self::TYPE_BIN_OP,
129
			'&&'         => self::TYPE_BIN_OP,
130
			'&='         => self::TYPE_BIN_OP,
131
			'*'          => self::TYPE_BIN_OP,
132
			'*='         => self::TYPE_BIN_OP,
133
			'+='         => self::TYPE_BIN_OP,
134
			'-='         => self::TYPE_BIN_OP,
135
			'.'          => self::TYPE_BIN_OP,
136
			'/'          => self::TYPE_BIN_OP,
137
			'/='         => self::TYPE_BIN_OP,
138
			'<'          => self::TYPE_BIN_OP,
139
			'<<'         => self::TYPE_BIN_OP,
140
			'<<='        => self::TYPE_BIN_OP,
141
			'<='         => self::TYPE_BIN_OP,
142
			'='          => self::TYPE_BIN_OP,
143
			'=='         => self::TYPE_BIN_OP,
144
			'==='        => self::TYPE_BIN_OP,
145
			'>'          => self::TYPE_BIN_OP,
146
			'>='         => self::TYPE_BIN_OP,
147
			'>>'         => self::TYPE_BIN_OP,
148
			'>>='        => self::TYPE_BIN_OP,
149
			'>>>'        => self::TYPE_BIN_OP,
150
			'>>>='       => self::TYPE_BIN_OP,
151
			'^'          => self::TYPE_BIN_OP,
152
			'^='         => self::TYPE_BIN_OP,
153
			'|'          => self::TYPE_BIN_OP,
154
			'|='         => self::TYPE_BIN_OP,
155
			'||'         => self::TYPE_BIN_OP,
156
			'in'         => self::TYPE_BIN_OP,
157
			'instanceof' => self::TYPE_BIN_OP,
158
			'+'          => self::TYPE_ADD_OP,
159
			'-'          => self::TYPE_ADD_OP,
160
			'?'          => self::TYPE_HOOK,
161
			':'          => self::TYPE_COLON,
162
			','          => self::TYPE_COMMA,
163
			';'          => self::TYPE_SEMICOLON,
164
			'{'          => self::TYPE_BRACE_OPEN,
165
			'}'          => self::TYPE_BRACE_CLOSE,
166
			'('          => self::TYPE_PAREN_OPEN,
167
			'['          => self::TYPE_PAREN_OPEN,
168
			')'          => self::TYPE_PAREN_CLOSE,
169
			']'          => self::TYPE_PAREN_CLOSE,
170
			'break'      => self::TYPE_RETURN,
171
			'continue'   => self::TYPE_RETURN,
172
			'return'     => self::TYPE_RETURN,
173
			'throw'      => self::TYPE_RETURN,
174
			'catch'      => self::TYPE_IF,
175
			'for'        => self::TYPE_IF,
176
			'if'         => self::TYPE_IF,
177
			'switch'     => self::TYPE_IF,
178
			'while'      => self::TYPE_IF,
179
			'with'       => self::TYPE_IF,
180
			'case'       => self::TYPE_DO,
181
			'do'         => self::TYPE_DO,
182
			'else'       => self::TYPE_DO,
183
			'finally'    => self::TYPE_DO,
184
			'try'        => self::TYPE_DO,
185
			'var'        => self::TYPE_DO,
186
			'function'   => self::TYPE_FUNC
187
		);
188
189
		// $goto : This is the main table for our state machine. For every state/token pair
190
		//         the following state is defined. When no rule exists for a given pair,
191
		//         the state is left unchanged.
192
		$goto = array(
193
			self::STATEMENT => array(
194
				self::TYPE_UN_OP      => self::EXPRESSION,
195
				self::TYPE_INCR_OP    => self::EXPRESSION,
196
				self::TYPE_ADD_OP     => self::EXPRESSION,
197
				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
198
				self::TYPE_RETURN     => self::EXPRESSION_NO_NL,
199
				self::TYPE_IF         => self::CONDITION,
200
				self::TYPE_FUNC       => self::CONDITION,
201
				self::TYPE_LITERAL    => self::EXPRESSION_OP
202
			),
203
			self::CONDITION => array(
204
				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
205
			),
206
			self::PROPERTY_ASSIGNMENT => array(
207
				self::TYPE_COLON      => self::PROPERTY_EXPRESSION,
208
				self::TYPE_BRACE_OPEN => self::STATEMENT
209
			),
210
			self::EXPRESSION => array(
211
				self::TYPE_SEMICOLON  => self::STATEMENT,
212
				self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
213
				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
214
				self::TYPE_FUNC       => self::EXPRESSION_FUNC,
215
				self::TYPE_LITERAL    => self::EXPRESSION_OP
216
			),
217
			self::EXPRESSION_NO_NL => array(
218
				self::TYPE_SEMICOLON  => self::STATEMENT,
219
				self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
220
				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
221
				self::TYPE_FUNC       => self::EXPRESSION_FUNC,
222
				self::TYPE_LITERAL    => self::EXPRESSION_OP
223
			),
224
			self::EXPRESSION_OP => array(
225
				self::TYPE_BIN_OP     => self::EXPRESSION,
226
				self::TYPE_ADD_OP     => self::EXPRESSION,
227
				self::TYPE_HOOK       => self::EXPRESSION_TERNARY,
228
				self::TYPE_COLON      => self::STATEMENT,
229
				self::TYPE_COMMA      => self::EXPRESSION,
230
				self::TYPE_SEMICOLON  => self::STATEMENT,
231
				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
232
			),
233
			self::EXPRESSION_FUNC => array(
234
				self::TYPE_BRACE_OPEN => self::STATEMENT
235
			),
236
			self::EXPRESSION_TERNARY => array(
237
				self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
238
				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
239
				self::TYPE_FUNC       => self::EXPRESSION_TERNARY_FUNC,
240
				self::TYPE_LITERAL    => self::EXPRESSION_TERNARY_OP
241
			),
242
			self::EXPRESSION_TERNARY_OP => array(
243
				self::TYPE_BIN_OP     => self::EXPRESSION_TERNARY,
244
				self::TYPE_ADD_OP     => self::EXPRESSION_TERNARY,
245
				self::TYPE_HOOK       => self::EXPRESSION_TERNARY,
246
				self::TYPE_COMMA      => self::EXPRESSION_TERNARY,
247
				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
248
			),
249
			self::EXPRESSION_TERNARY_FUNC => array(
250
				self::TYPE_BRACE_OPEN => self::STATEMENT
251
			),
252
			self::PAREN_EXPRESSION => array(
253
				self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
254
				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
255
				self::TYPE_FUNC       => self::PAREN_EXPRESSION_FUNC,
256
				self::TYPE_LITERAL    => self::PAREN_EXPRESSION_OP
257
			),
258
			self::PAREN_EXPRESSION_OP => array(
259
				self::TYPE_BIN_OP     => self::PAREN_EXPRESSION,
260
				self::TYPE_ADD_OP     => self::PAREN_EXPRESSION,
261
				self::TYPE_HOOK       => self::PAREN_EXPRESSION,
262
				self::TYPE_COLON      => self::PAREN_EXPRESSION,
263
				self::TYPE_COMMA      => self::PAREN_EXPRESSION,
264
				self::TYPE_SEMICOLON  => self::PAREN_EXPRESSION,
265
				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
266
			),
267
			self::PAREN_EXPRESSION_FUNC => array(
268
				self::TYPE_BRACE_OPEN => self::STATEMENT
269
			),
270
			self::PROPERTY_EXPRESSION => array(
271
				self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
272
				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
273
				self::TYPE_FUNC       => self::PROPERTY_EXPRESSION_FUNC,
274
				self::TYPE_LITERAL    => self::PROPERTY_EXPRESSION_OP
275
			),
276
			self::PROPERTY_EXPRESSION_OP => array(
277
				self::TYPE_BIN_OP     => self::PROPERTY_EXPRESSION,
278
				self::TYPE_ADD_OP     => self::PROPERTY_EXPRESSION,
279
				self::TYPE_HOOK       => self::PROPERTY_EXPRESSION,
280
				self::TYPE_COMMA      => self::PROPERTY_ASSIGNMENT,
281
				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
282
			),
283
			self::PROPERTY_EXPRESSION_FUNC => array(
284
				self::TYPE_BRACE_OPEN => self::STATEMENT
285
			)
286
		);
287
288
		// $push : This table contains the rules for when to push a state onto the stack.
289
		//         The pushed state is the state to return to when the corresponding
290
		//         closing token is found
291
		$push = array(
292
			self::STATEMENT => array(
293
				self::TYPE_BRACE_OPEN => self::STATEMENT,
294
				self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
295
			),
296
			self::CONDITION => array(
297
				self::TYPE_PAREN_OPEN => self::STATEMENT
298
			),
299
			self::PROPERTY_ASSIGNMENT => array(
300
				self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT
301
			),
302
			self::EXPRESSION => array(
303
				self::TYPE_BRACE_OPEN => self::EXPRESSION_OP,
304
				self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
305
			),
306
			self::EXPRESSION_NO_NL => array(
307
				self::TYPE_BRACE_OPEN => self::EXPRESSION_OP,
308
				self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
309
			),
310
			self::EXPRESSION_OP => array(
311
				self::TYPE_HOOK       => self::EXPRESSION,
312
				self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
313
			),
314
			self::EXPRESSION_FUNC => array(
315
				self::TYPE_BRACE_OPEN => self::EXPRESSION_OP
316
			),
317
			self::EXPRESSION_TERNARY => array(
318
				self::TYPE_BRACE_OPEN => self::EXPRESSION_TERNARY_OP,
319
				self::TYPE_PAREN_OPEN => self::EXPRESSION_TERNARY_OP
320
			),
321
			self::EXPRESSION_TERNARY_OP => array(
322
				self::TYPE_HOOK       => self::EXPRESSION_TERNARY,
323
				self::TYPE_PAREN_OPEN => self::EXPRESSION_TERNARY_OP
324
			),
325
			self::EXPRESSION_TERNARY_FUNC => array(
326
				self::TYPE_BRACE_OPEN => self::EXPRESSION_TERNARY_OP
327
			),
328
			self::PAREN_EXPRESSION => array(
329
				self::TYPE_BRACE_OPEN => self::PAREN_EXPRESSION_OP,
330
				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION_OP
331
			),
332
			self::PAREN_EXPRESSION_OP => array(
333
				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION_OP
334
			),
335
			self::PAREN_EXPRESSION_FUNC => array(
336
				self::TYPE_BRACE_OPEN => self::PAREN_EXPRESSION_OP
337
			),
338
			self::PROPERTY_EXPRESSION => array(
339
				self::TYPE_BRACE_OPEN => self::PROPERTY_EXPRESSION_OP,
340
				self::TYPE_PAREN_OPEN => self::PROPERTY_EXPRESSION_OP
341
			),
342
			self::PROPERTY_EXPRESSION_OP => array(
343
				self::TYPE_PAREN_OPEN => self::PROPERTY_EXPRESSION_OP
344
			),
345
			self::PROPERTY_EXPRESSION_FUNC => array(
346
				self::TYPE_BRACE_OPEN => self::PROPERTY_EXPRESSION_OP
347
			)
348
		);
349
350
		// $pop : Rules for when to pop a state from the stack
351
		$pop = array(
352
			self::STATEMENT              => array( self::TYPE_BRACE_CLOSE => true ),
353
			self::PROPERTY_ASSIGNMENT    => array( self::TYPE_BRACE_CLOSE => true ),
354
			self::EXPRESSION             => array( self::TYPE_BRACE_CLOSE => true ),
355
			self::EXPRESSION_NO_NL       => array( self::TYPE_BRACE_CLOSE => true ),
356
			self::EXPRESSION_OP          => array( self::TYPE_BRACE_CLOSE => true ),
357
			self::EXPRESSION_TERNARY_OP  => array( self::TYPE_COLON       => true ),
358
			self::PAREN_EXPRESSION       => array( self::TYPE_PAREN_CLOSE => true ),
359
			self::PAREN_EXPRESSION_OP    => array( self::TYPE_PAREN_CLOSE => true ),
360
			self::PROPERTY_EXPRESSION    => array( self::TYPE_BRACE_CLOSE => true ),
361
			self::PROPERTY_EXPRESSION_OP => array( self::TYPE_BRACE_CLOSE => true )
362
		);
363
364
		// $semicolon : Rules for when a semicolon insertion is appropriate
365
		$semicolon = array(
366
			self::EXPRESSION_NO_NL => array(
367
				self::TYPE_UN_OP      => true,
368
				self::TYPE_INCR_OP    => true,
369
				self::TYPE_ADD_OP     => true,
370
				self::TYPE_BRACE_OPEN => true,
371
				self::TYPE_PAREN_OPEN => true,
372
				self::TYPE_RETURN     => true,
373
				self::TYPE_IF         => true,
374
				self::TYPE_DO         => true,
375
				self::TYPE_FUNC       => true,
376
				self::TYPE_LITERAL    => true
377
			),
378
			self::EXPRESSION_OP => array(
379
				self::TYPE_UN_OP      => true,
380
				self::TYPE_INCR_OP    => true,
381
				self::TYPE_BRACE_OPEN => true,
382
				self::TYPE_RETURN     => true,
383
				self::TYPE_IF         => true,
384
				self::TYPE_DO         => true,
385
				self::TYPE_FUNC       => true,
386
				self::TYPE_LITERAL    => true
387
			)
388
		);
389
390
		// Rules for when newlines should be inserted if
391
		// $statementsOnOwnLine is enabled.
392
		// $newlineBefore is checked before switching state,
393
		// $newlineAfter is checked after
394
		$newlineBefore = array(
395
			self::STATEMENT => array(
396
				self::TYPE_BRACE_CLOSE => true,
397
			),
398
		);
399
		$newlineAfter = array(
400
			self::STATEMENT => array(
401
				self::TYPE_BRACE_OPEN => true,
402
				self::TYPE_PAREN_CLOSE => true,
403
				self::TYPE_SEMICOLON => true,
404
			),
405
		);
406
407
		// $divStates : Contains all states that can be followed by a division operator
408
		$divStates = array(
409
			self::EXPRESSION_OP          => true,
410
			self::EXPRESSION_TERNARY_OP  => true,
411
			self::PAREN_EXPRESSION_OP    => true,
412
			self::PROPERTY_EXPRESSION_OP => true
413
		);
414
415
		// Here's where the minifying takes place: Loop through the input, looking for tokens
416
		// and output them to $out, taking actions to the above defined rules when appropriate.
417
		$out = '';
418
		$pos = 0;
419
		$length = strlen( $s );
420
		$lineLength = 0;
421
		$newlineFound = true;
422
		$state = self::STATEMENT;
423
		$stack = array();
424
		$last = ';'; // Pretend that we have seen a semicolon yet
425
		while( $pos < $length ) {
426
			// First, skip over any whitespace and multiline comments, recording whether we
427
			// found any newline character
428
			$skip = strspn( $s, " \t\n\r\xb\xc", $pos );
429
			if( !$skip ) {
430
				$ch = $s[$pos];
431
				if( $ch === '/' && substr( $s, $pos, 2 ) === '/*' ) {
432
					// Multiline comment. Search for the end token or EOT.
433
					$end = strpos( $s, '*/', $pos + 2 );
434
					$skip = $end === false ? $length - $pos : $end - $pos + 2;
435
				}
436
			}
437
			if( $skip ) {
438
				// The semicolon insertion mechanism needs to know whether there was a newline
439
				// between two tokens, so record it now.
440
				if( !$newlineFound && strcspn( $s, "\r\n", $pos, $skip ) !== $skip ) {
441
					$newlineFound = true;
442
				}
443
				$pos += $skip;
444
				continue;
445
			}
446
			// Handle C++-style comments and html comments, which are treated as single line
447
			// comments by the browser, regardless of whether the end tag is on the same line.
448
			// Handle --> the same way, but only if it's at the beginning of the line
449
			if( ( $ch === '/' && substr( $s, $pos, 2 ) === '//' )
0 ignored issues
show
The variable $ch 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...
450
				|| ( $ch === '<' && substr( $s, $pos, 4 ) === '<!--' )
451
				|| ( $ch === '-' && $newlineFound && substr( $s, $pos, 3 ) === '-->' )
452
			) {
453
				$pos += strcspn( $s, "\r\n", $pos );
454
				continue;
455
			}
456
457
			// Find out which kind of token we're handling. $end will point past the end of it.
458
			$end = $pos + 1;
459
			// Handle string literals
460
			if( $ch === "'" || $ch === '"' ) {
461
				// Search to the end of the string literal, skipping over backslash escapes
462
				$search = $ch . '\\';
463 View Code Duplication
				do{
464
					$end += strcspn( $s, $search, $end ) + 2;
465
				} while( $end - 2 < $length && $s[$end - 2] === '\\' );
466
				$end--;
467
			// We have to distinguish between regexp literals and division operators
468
			// A division operator is only possible in certain states
469
			} elseif( $ch === '/' && !isset( $divStates[$state] ) ) {
470
				// Regexp literal, search to the end, skipping over backslash escapes and
471
				// character classes
472
				for( ; ; ) {
473 View Code Duplication
					do{
474
						$end += strcspn( $s, '/[\\', $end ) + 2;
475
					} while( $end - 2 < $length && $s[$end - 2] === '\\' );
476
					$end--;
477
					if( $end - 1 >= $length || $s[$end - 1] === '/' ) {
478
						break;
479
					}
480 View Code Duplication
					do{
481
						$end += strcspn( $s, ']\\', $end ) + 2;
482
					} while( $end - 2 < $length && $s[$end - 2] === '\\' );
483
					$end--;
484
				};
485
				// Search past the regexp modifiers (gi)
486
				while( $end < $length && ctype_alpha( $s[$end] ) ) {
487
					$end++;
488
				}
489
			} elseif(
490
				$ch === '0'
491
				&& ($pos + 1 < $length) && ($s[$pos + 1] === 'x' || $s[$pos + 1] === 'X' )
492
			) {
493
				// Hex numeric literal
494
				$end++; // x or X
495
				$len = strspn( $s, '0123456789ABCDEFabcdef', $end );
496
				if ( !$len ) {
497
					return self::parseError($s, $pos, 'Expected a hexadecimal number but found ' . substr( $s, $pos, 5 ) . '...' );
498
				}
499
				$end += $len;
500
			} elseif(
501
				ctype_digit( $ch )
502
				|| ( $ch === '.' && $pos + 1 < $length && ctype_digit( $s[$pos + 1] ) )
503
			) {
504
				$end += strspn( $s, '0123456789', $end );
505
				$decimal = strspn( $s, '.', $end );
506
				if ($decimal) {
507
					if ( $decimal > 2 ) {
508
						return self::parseError($s, $end, 'The number has too many decimal points' );
509
					}
510
					$end += strspn( $s, '0123456789', $end + 1 ) + $decimal;
511
				}
512
				$exponent = strspn( $s, 'eE', $end );
513
				if( $exponent ) {
514
					if ( $exponent > 1 ) {
515
						return self::parseError($s, $end, 'Number with several E' );
516
					}
517
					$end++;
518
519
					// + sign is optional; - sign is required.
520
					$end += strspn( $s, '-+', $end );
521
					$len = strspn( $s, '0123456789', $end );
522
					if ( !$len ) {
523
						return self::parseError($s, $pos, 'No decimal digits after e, how many zeroes should be added?' );
524
					}
525
					$end += $len;
526
				}
527
			} elseif( isset( $opChars[$ch] ) ) {
528
				// Punctuation character. Search for the longest matching operator.
529
				while(
530
					$end < $length
531
					&& isset( $tokenTypes[substr( $s, $pos, $end - $pos + 1 )] )
532
				) {
533
					$end++;
534
				}
535
			} else {
536
				// Identifier or reserved word. Search for the end by excluding whitespace and
537
				// punctuation.
538
				$end += strcspn( $s, " \t\n.;,=<>+-{}()[]?:*/%'\"!&|^~\xb\xc\r", $end );
539
			}
540
541
			// Now get the token type from our type array
542
			$token = substr( $s, $pos, $end - $pos ); // so $end - $pos == strlen( $token )
543
			$type = isset( $tokenTypes[$token] ) ? $tokenTypes[$token] : self::TYPE_LITERAL;
544
545
			if( $newlineFound && isset( $semicolon[$state][$type] ) ) {
546
				// This token triggers the semicolon insertion mechanism of javascript. While we
547
				// could add the ; token here ourselves, keeping the newline has a few advantages.
548
				$out .= "\n";
549
				$state = self::STATEMENT;
550
				$lineLength = 0;
551
			} elseif( $maxLineLength > 0 && $lineLength + $end - $pos > $maxLineLength &&
552
					!isset( $semicolon[$state][$type] ) && $type !== self::TYPE_INCR_OP )
553
			{
554
				// This line would get too long if we added $token, so add a newline first.
555
				// Only do this if it won't trigger semicolon insertion and if it won't
556
				// put a postfix increment operator on its own line, which is illegal in js.
557
				$out .= "\n";
558
				$lineLength = 0;
559
			// Check, whether we have to separate the token from the last one with whitespace
560
			} elseif( !isset( $opChars[$last] ) && !isset( $opChars[$ch] ) ) {
561
				$out .= ' ';
562
				$lineLength++;
563
			// Don't accidentally create ++, -- or // tokens
564
			} elseif( $last === $ch && ( $ch === '+' || $ch === '-' || $ch === '/' ) ) {
565
				$out .= ' ';
566
				$lineLength++;
567
			}
568
			if (
569
				$type === self::TYPE_LITERAL
570
				&& ( $token === 'true' || $token === 'false' )
571
				&& ( $state === self::EXPRESSION || $state === self::PROPERTY_EXPRESSION )
572
				&& $last !== '.'
573
			) {
574
				$token = ( $token === 'true' ) ? '!0' : '!1';
575
			}
576
577
			$out .= $token;
578
			$lineLength += $end - $pos; // += strlen( $token )
579
			$last = $s[$end - 1];
580
			$pos = $end;
581
			$newlineFound = false;
582
583
			// Output a newline after the token if required
584
			// This is checked before AND after switching state
585
			$newlineAdded = false;
586 View Code Duplication
			if ( $statementsOnOwnLine && !$newlineAdded && isset( $newlineBefore[$state][$type] ) ) {
587
				$out .= "\n";
588
				$lineLength = 0;
589
				$newlineAdded = true;
590
			}
591
592
			// Now that we have output our token, transition into the new state.
593
			if( isset( $push[$state][$type] ) && count( $stack ) < self::STACK_LIMIT ) {
594
				$stack[] = $push[$state][$type];
595
			}
596
			if( $stack && isset( $pop[$state][$type] ) ) {
597
				$state = array_pop( $stack );
598
			} elseif( isset( $goto[$state][$type] ) ) {
599
				$state = $goto[$state][$type];
600
			}
601
602
			// Check for newline insertion again
603 View Code Duplication
			if ( $statementsOnOwnLine && !$newlineAdded && isset( $newlineAfter[$state][$type] ) ) {
604
				$out .= "\n";
605
				$lineLength = 0;
606
			}
607
		}
608
		return $out;
609
	}
610
611
	static function parseError($fullJavascript, $position, $errorMsg) {
612
		// TODO: Handle the error: trigger_error, throw exception, return false...
613
		return false;
614
	}
615
}
616