PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.3.0
Defender Security – Malware Scanner, Login Security & Firewall v6.3.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / extra / php-codesniffer-php-token / Tokenizers / CSS.php

CSS.php in Defender Security – Malware Scanner, Login Security & Firewall 6.3.0, at src/extra/php-codesniffer-php-token/Tokenizers/CSS.php

534 lines 17.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Tokenizes CSS code.
4 *
5 * @author Greg Sherwood <[email protected]>
6 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
7 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8 */
9
10 namespace PHP_CodeSniffer\Tokenizers;
11
12 use PHP_CodeSniffer\Config;
13 use PHP_CodeSniffer\Exceptions\TokenizerException;
14 use PHP_CodeSniffer\Util;
15
16 class CSS extends PHP {
17
18
19 /**
20 * Initialise the tokenizer.
21 *
22 * Pre-checks the content to see if it looks minified.
23 *
24 * @param string $content The content to tokenize,
25 * @param \PHP_CodeSniffer\Config $config The config data for the run.
26 * @param string $eolChar The EOL char used in the content.
27 *
28 * @return void
29 * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the file appears to be minified.
30 */
31 public function __construct( $content, Config $config, $eolChar = '\n' ) {
32 if ( $this->isMinifiedContent( $content, $eolChar ) === true ) {
33 throw new TokenizerException( 'File appears to be minified and cannot be processed' );
34 }
35
36 parent::__construct( $content, $config, $eolChar );
37
38 }//end __construct()
39
40
41 /**
42 * Creates an array of tokens when given some CSS code.
43 *
44 * Uses the PHP tokenizer to do all the tricky work
45 *
46 * @param string $string The string to tokenize.
47 *
48 * @return array
49 */
50 public function tokenize( $string ) {
51 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
52 echo "\t*** START CSS TOKENIZING 1ST PASS ***" . PHP_EOL;
53 }
54
55 // If the content doesn't have an EOL char on the end, add one so
56 // the open and close tags we add are parsed correctly.
57 $eolAdded = false;
58 if ( substr( $string, ( strlen( $this->eolChar ) * - 1 ) ) !== $this->eolChar ) {
59 $string .= $this->eolChar;
60 $eolAdded = true;
61 }
62
63 $string = str_replace( '<?php', '^PHPCS_CSS_T_OPEN_TAG^', $string );
64 $string = str_replace( '?>', '^PHPCS_CSS_T_CLOSE_TAG^', $string );
65 $tokens = parent::tokenize( '<?php ' . $string . '?>' );
66
67 $finalTokens = [];
68 $finalTokens[0] = [
69 'code' => T_OPEN_TAG,
70 'type' => 'T_OPEN_TAG',
71 'content' => '',
72 ];
73
74 $newStackPtr = 1;
75 $numTokens = count( $tokens );
76 $multiLineComment = false;
77 for ( $stackPtr = 1; $stackPtr < $numTokens; $stackPtr ++ ) {
78 $token = $tokens[ $stackPtr ];
79
80 // CSS files don't have lists, breaks etc, so convert these to
81 // standard strings early so they can be converted into T_STYLE
82 // tokens and joined with other strings if needed.
83 if ( $token['code'] === T_BREAK
84 || $token['code'] === T_LIST
85 || $token['code'] === T_DEFAULT
86 || $token['code'] === T_SWITCH
87 || $token['code'] === T_FOR
88 || $token['code'] === T_FOREACH
89 || $token['code'] === T_WHILE
90 || $token['code'] === T_DEC
91 || $token['code'] === T_NEW
92 ) {
93 $token['type'] = 'T_STRING';
94 $token['code'] = T_STRING;
95 }
96
97 $token['content'] = str_replace( '^PHPCS_CSS_T_OPEN_TAG^', '<?php', $token['content'] );
98 $token['content'] = str_replace( '^PHPCS_CSS_T_CLOSE_TAG^', '?>', $token['content'] );
99
100 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
101 $type = $token['type'];
102 $content = Util\Common::prepareForOutput( $token['content'] );
103 echo "\tProcess token $stackPtr: $type => $content" . PHP_EOL;
104 }
105
106 if ( $token['code'] === T_BITWISE_XOR
107 && $tokens[ ( $stackPtr + 1 ) ]['content'] === 'PHPCS_CSS_T_OPEN_TAG'
108 ) {
109 $content = '<?php';
110 for ( $stackPtr += 3; $stackPtr < $numTokens; $stackPtr ++ ) {
111 if ( $tokens[ $stackPtr ]['code'] === T_BITWISE_XOR
112 && $tokens[ ( $stackPtr + 1 ) ]['content'] === 'PHPCS_CSS_T_CLOSE_TAG'
113 ) {
114 // Add the end tag and ignore the * we put at the end.
115 $content .= '?>';
116 $stackPtr += 2;
117 break;
118 } else {
119 $content .= $tokens[ $stackPtr ]['content'];
120 }
121 }
122
123 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
124 echo "\t\t=> Found embedded PHP code: ";
125 $cleanContent = Util\Common::prepareForOutput( $content );
126 echo $cleanContent . PHP_EOL;
127 }
128
129 $finalTokens[ $newStackPtr ] = [
130 'type' => 'T_EMBEDDED_PHP',
131 'code' => T_EMBEDDED_PHP,
132 'content' => $content,
133 ];
134
135 $newStackPtr ++;
136 continue;
137 }//end if
138
139 if ( $token['code'] === T_GOTO_LABEL ) {
140 // Convert these back to T_STRING followed by T_COLON so we can
141 // more easily process style definitions.
142 $finalTokens[ $newStackPtr ] = [
143 'type' => 'T_STRING',
144 'code' => T_STRING,
145 'content' => substr( $token['content'], 0, - 1 ),
146 ];
147 $newStackPtr ++;
148 $finalTokens[ $newStackPtr ] = [
149 'type' => 'T_COLON',
150 'code' => T_COLON,
151 'content' => ':',
152 ];
153 $newStackPtr ++;
154 continue;
155 }
156
157 if ( $token['code'] === T_FUNCTION ) {
158 // There are no functions in CSS, so convert this to a string.
159 $finalTokens[ $newStackPtr ] = [
160 'type' => 'T_STRING',
161 'code' => T_STRING,
162 'content' => $token['content'],
163 ];
164
165 $newStackPtr ++;
166 continue;
167 }
168
169 if ( $token['code'] === T_COMMENT
170 && substr( $token['content'], 0, 2 ) === '/*'
171 ) {
172 // Multi-line comment. Record it so we can ignore other
173 // comment tags until we get out of this one.
174 $multiLineComment = true;
175 }
176
177 if ( $token['code'] === T_COMMENT
178 && $multiLineComment === false
179 && ( substr( $token['content'], 0, 2 ) === '//'
180 || $token['content'][0] === '#' )
181 ) {
182 $content = ltrim( $token['content'], '#/' );
183
184 // Guard against PHP7+ syntax errors by stripping
185 // leading zeros so the content doesn't look like an invalid int.
186 $leadingZero = false;
187 if ( $content[0] === '0' ) {
188 $content = '1' . $content;
189 $leadingZero = true;
190 }
191
192 $commentTokens = parent::tokenize( '<?php ' . $content . '?>' );
193
194 // The first and last tokens are the open/close tags.
195 array_shift( $commentTokens );
196 array_pop( $commentTokens );
197
198 if ( $leadingZero === true ) {
199 $commentTokens[0]['content'] = substr( $commentTokens[0]['content'], 1 );
200 $content = substr( $content, 1 );
201 }
202
203 if ( $token['content'][0] === '#' ) {
204 // The # character is not a comment in CSS files, so
205 // determine what it means in this context.
206 $firstContent = $commentTokens[0]['content'];
207
208 // If the first content is just a number, it is probably a
209 // colour like 8FB7DB, which PHP splits into 8 and FB7DB.
210 if ( ( $commentTokens[0]['code'] === T_LNUMBER
211 || $commentTokens[0]['code'] === T_DNUMBER )
212 && $commentTokens[1]['code'] === T_STRING
213 ) {
214 $firstContent .= $commentTokens[1]['content'];
215 array_shift( $commentTokens );
216 }
217
218 // If the first content looks like a colour and not a class
219 // definition, join the tokens together.
220 if ( preg_match( '/^[ABCDEF0-9]+$/i', $firstContent ) === 1
221 && $commentTokens[1]['content'] !== '-'
222 ) {
223 array_shift( $commentTokens );
224 // Work out what we trimmed off above and remember to re-add it.
225 $trimmed = substr( $token['content'], 0, ( strlen( $token['content'] ) - strlen( $content ) ) );
226 $finalTokens[ $newStackPtr ] = [
227 'type' => 'T_COLOUR',
228 'code' => T_COLOUR,
229 'content' => $trimmed . $firstContent,
230 ];
231 } else {
232 $finalTokens[ $newStackPtr ] = [
233 'type' => 'T_HASH',
234 'code' => T_HASH,
235 'content' => '#',
236 ];
237 }
238 } else {
239 $finalTokens[ $newStackPtr ] = [
240 'type' => 'T_STRING',
241 'code' => T_STRING,
242 'content' => '//',
243 ];
244 }//end if
245
246 $newStackPtr ++;
247
248 array_splice( $tokens, $stackPtr, 1, $commentTokens );
249 $numTokens = count( $tokens );
250 $stackPtr --;
251 continue;
252 }//end if
253
254 if ( $token['code'] === T_COMMENT
255 && substr( $token['content'], - 2 ) === '*/'
256 ) {
257 // Multi-line comment is done.
258 $multiLineComment = false;
259 }
260
261 $finalTokens[ $newStackPtr ] = $token;
262 $newStackPtr ++;
263 }//end for
264
265 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
266 echo "\t*** END CSS TOKENIZING 1ST PASS ***" . PHP_EOL;
267 echo "\t*** START CSS TOKENIZING 2ND PASS ***" . PHP_EOL;
268 }
269
270 // A flag to indicate if we are inside a style definition,
271 // which is defined using curly braces.
272 $inStyleDef = false;
273
274 // A flag to indicate if an At-rule like "@media" is used, which will result
275 // in nested curly brackets.
276 $asperandStart = false;
277
278 $numTokens = count( $finalTokens );
279 for ( $stackPtr = 0; $stackPtr < $numTokens; $stackPtr ++ ) {
280 $token = $finalTokens[ $stackPtr ];
281
282 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
283 $type = $token['type'];
284 $content = Util\Common::prepareForOutput( $token['content'] );
285 echo "\tProcess token $stackPtr: $type => $content" . PHP_EOL;
286 }
287
288 switch ( $token['code'] ) {
289 case T_OPEN_CURLY_BRACKET:
290 // Opening curly brackets for an At-rule do not start a style
291 // definition. We also reset the asperand flag here because the next
292 // opening curly bracket could be indeed the start of a style
293 // definition.
294 if ( $asperandStart === true ) {
295 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
296 if ( $inStyleDef === true ) {
297 echo "\t\t* style definition closed *" . PHP_EOL;
298 }
299
300 if ( $asperandStart === true ) {
301 echo "\t\t* at-rule definition closed *" . PHP_EOL;
302 }
303 }
304
305 $inStyleDef = false;
306 $asperandStart = false;
307 } else {
308 $inStyleDef = true;
309 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
310 echo "\t\t* style definition opened *" . PHP_EOL;
311 }
312 }
313 break;
314 case T_CLOSE_CURLY_BRACKET:
315 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
316 if ( $inStyleDef === true ) {
317 echo "\t\t* style definition closed *" . PHP_EOL;
318 }
319
320 if ( $asperandStart === true ) {
321 echo "\t\t* at-rule definition closed *" . PHP_EOL;
322 }
323 }
324
325 $inStyleDef = false;
326 $asperandStart = false;
327 break;
328 case T_MINUS:
329 // Minus signs are often used instead of spaces inside
330 // class names, IDs and styles.
331 if ( $finalTokens[ ( $stackPtr + 1 ) ]['code'] === T_STRING ) {
332 if ( $finalTokens[ ( $stackPtr - 1 ) ]['code'] === T_STRING ) {
333 $newContent = $finalTokens[ ( $stackPtr - 1 ) ]['content'] . '-' . $finalTokens[ ( $stackPtr + 1 ) ]['content'];
334
335 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
336 echo "\t\t* token is a string joiner; ignoring this and previous token" . PHP_EOL;
337 $old = Util\Common::prepareForOutput( $finalTokens[ ( $stackPtr + 1 ) ]['content'] );
338 $new = Util\Common::prepareForOutput( $newContent );
339 echo "\t\t=> token " . ( $stackPtr + 1 ) . " content changed from \"$old\" to \"$new\"" . PHP_EOL;
340 }
341
342 $finalTokens[ ( $stackPtr + 1 ) ]['content'] = $newContent;
343 unset( $finalTokens[ $stackPtr ] );
344 unset( $finalTokens[ ( $stackPtr - 1 ) ] );
345 } else {
346 $newContent = '-' . $finalTokens[ ( $stackPtr + 1 ) ]['content'];
347
348 $finalTokens[ ( $stackPtr + 1 ) ]['content'] = $newContent;
349 unset( $finalTokens[ $stackPtr ] );
350 }
351 } else if ( $finalTokens[ ( $stackPtr + 1 ) ]['code'] === T_LNUMBER ) {
352 // They can also be used to provide negative numbers.
353 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
354 echo "\t\t* token is part of a negative number; adding content to next token and ignoring *" . PHP_EOL;
355 $content = Util\Common::prepareForOutput( $finalTokens[ ( $stackPtr + 1 ) ]['content'] );
356 echo "\t\t=> token " . ( $stackPtr + 1 ) . " content changed from \"$content\" to \"-$content\"" . PHP_EOL;
357 }
358
359 $finalTokens[ ( $stackPtr + 1 ) ]['content'] = '-' . $finalTokens[ ( $stackPtr + 1 ) ]['content'];
360 unset( $finalTokens[ $stackPtr ] );
361 }//end if
362 break;
363 case T_COLON:
364 // Only interested in colons that are defining styles.
365 if ( $inStyleDef === false ) {
366 break;
367 }
368
369 for ( $x = ( $stackPtr - 1 ); $x >= 0; $x -- ) {
370 if ( isset( Util\Tokens::$emptyTokens[ $finalTokens[ $x ]['code'] ] ) === false ) {
371 break;
372 }
373 }
374
375 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
376 $type = $finalTokens[ $x ]['type'];
377 echo "\t\t=> token $x changed from $type to T_STYLE" . PHP_EOL;
378 }
379
380 $finalTokens[ $x ]['type'] = 'T_STYLE';
381 $finalTokens[ $x ]['code'] = T_STYLE;
382 break;
383 case T_STRING:
384 if ( strtolower( $token['content'] ) === 'url' ) {
385 // Find the next content.
386 for ( $x = ( $stackPtr + 1 ); $x < $numTokens; $x ++ ) {
387 if ( isset( Util\Tokens::$emptyTokens[ $finalTokens[ $x ]['code'] ] ) === false ) {
388 break;
389 }
390 }
391
392 // Needs to be in the format "url(" for it to be a URL.
393 if ( $finalTokens[ $x ]['code'] !== T_OPEN_PARENTHESIS ) {
394 continue 2;
395 }
396
397 // Make sure the content isn't empty.
398 for ( $y = ( $x + 1 ); $y < $numTokens; $y ++ ) {
399 if ( isset( Util\Tokens::$emptyTokens[ $finalTokens[ $y ]['code'] ] ) === false ) {
400 break;
401 }
402 }
403
404 if ( $finalTokens[ $y ]['code'] === T_CLOSE_PARENTHESIS ) {
405 continue 2;
406 }
407
408 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
409 for ( $i = ( $stackPtr + 1 ); $i <= $y; $i ++ ) {
410 $type = $finalTokens[ $i ]['type'];
411 $content = Util\Common::prepareForOutput( $finalTokens[ $i ]['content'] );
412 echo "\tProcess token $i: $type => $content" . PHP_EOL;
413 }
414
415 echo "\t\t* token starts a URL *" . PHP_EOL;
416 }
417
418 // Join all the content together inside the url() statement.
419 $newContent = '';
420 for ( $i = ( $x + 2 ); $i < $numTokens; $i ++ ) {
421 if ( $finalTokens[ $i ]['code'] === T_CLOSE_PARENTHESIS ) {
422 break;
423 }
424
425 $newContent .= $finalTokens[ $i ]['content'];
426 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
427 $content = Util\Common::prepareForOutput( $finalTokens[ $i ]['content'] );
428 echo "\t\t=> token $i added to URL string and ignored: $content" . PHP_EOL;
429 }
430
431 unset( $finalTokens[ $i ] );
432 }
433
434 $stackPtr = $i;
435
436 // If the content inside the "url()" is in double quotes
437 // there will only be one token and so we don't have to do
438 // anything except change its type. If it is not empty,
439 // we need to do some token merging.
440 $finalTokens[ ( $x + 1 ) ]['type'] = 'T_URL';
441 $finalTokens[ ( $x + 1 ) ]['code'] = T_URL;
442
443 if ( $newContent !== '' ) {
444 $finalTokens[ ( $x + 1 ) ]['content'] .= $newContent;
445 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
446 $content = Util\Common::prepareForOutput( $finalTokens[ ( $x + 1 ) ]['content'] );
447 echo "\t\t=> token content changed to: $content" . PHP_EOL;
448 }
449 }
450 } else if ( $finalTokens[ $stackPtr ]['content'][0] === '-'
451 && $finalTokens[ ( $stackPtr + 1 ) ]['code'] === T_STRING
452 ) {
453 if ( isset( $finalTokens[ ( $stackPtr - 1 ) ] ) === true
454 && $finalTokens[ ( $stackPtr - 1 ) ]['code'] === T_STRING
455 ) {
456 $newContent = $finalTokens[ ( $stackPtr - 1 ) ]['content'] . $finalTokens[ $stackPtr ]['content'] . $finalTokens[ ( $stackPtr + 1 ) ]['content'];
457
458 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
459 echo "\t\t* token is a string joiner; ignoring this and previous token" . PHP_EOL;
460 $old = Util\Common::prepareForOutput( $finalTokens[ ( $stackPtr + 1 ) ]['content'] );
461 $new = Util\Common::prepareForOutput( $newContent );
462 echo "\t\t=> token " . ( $stackPtr + 1 ) . " content changed from \"$old\" to \"$new\"" . PHP_EOL;
463 }
464
465 $finalTokens[ ( $stackPtr + 1 ) ]['content'] = $newContent;
466 unset( $finalTokens[ $stackPtr ] );
467 unset( $finalTokens[ ( $stackPtr - 1 ) ] );
468 } else {
469 $newContent = $finalTokens[ $stackPtr ]['content'] . $finalTokens[ ( $stackPtr + 1 ) ]['content'];
470
471 $finalTokens[ ( $stackPtr + 1 ) ]['content'] = $newContent;
472 unset( $finalTokens[ $stackPtr ] );
473 }
474 }//end if
475 break;
476 case T_ASPERAND:
477 $asperandStart = true;
478 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
479 echo "\t\t* at-rule definition opened *" . PHP_EOL;
480 }
481 break;
482 default:
483 // Nothing special to be done with this token.
484 break;
485 }//end switch
486 }//end for
487
488 // Reset the array keys to avoid gaps.
489 $finalTokens = array_values( $finalTokens );
490 $numTokens = count( $finalTokens );
491
492 // Blank out the content of the end tag.
493 $finalTokens[ ( $numTokens - 1 ) ]['content'] = '';
494
495 if ( $eolAdded === true ) {
496 // Strip off the extra EOL char we added for tokenizing.
497 $finalTokens[ ( $numTokens - 2 ) ]['content'] = substr(
498 $finalTokens[ ( $numTokens - 2 ) ]['content'],
499 0,
500 ( strlen( $this->eolChar ) * - 1 )
501 );
502
503 if ( $finalTokens[ ( $numTokens - 2 ) ]['content'] === '' ) {
504 unset( $finalTokens[ ( $numTokens - 2 ) ] );
505 $finalTokens = array_values( $finalTokens );
506 $numTokens = count( $finalTokens );
507 }
508 }
509
510 if ( PHP_CODESNIFFER_VERBOSITY > 1 ) {
511 echo "\t*** END CSS TOKENIZING 2ND PASS ***" . PHP_EOL;
512 }
513
514 return $finalTokens;
515
516 }//end tokenize()
517
518
519 /**
520 * Performs additional processing after main tokenizing.
521 *
522 * @return void
523 */
524 public function processAdditional() {
525 /*
526 We override this method because we don't want the PHP version to
527 run during CSS processing because it is wasted processing time.
528 */
529
530 }//end processAdditional()
531
532
533 }//end class
534