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