PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 3.0.1
Defender Security – Malware Scanner, Login Security & Firewall v3.0.1
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 3.0.1, at src/extra/php-codesniffer-php-token/Tokenizers/CSS.php

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