-
Notifications
You must be signed in to change notification settings - Fork 41
/
Copy pathIncludingNonPHPFileSniff.php
103 lines (86 loc) · 2.79 KB
/
IncludingNonPHPFileSniff.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<?php
/**
* WordPressVIPMinimum_Sniffs_Files_IncludingNonPHPFileSniff.
*
* @package VIPCS\WordPressVIPMinimum
*/
namespace WordPressVIPMinimum\Sniffs\Files;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\BackCompat\BCFile;
use WordPressVIPMinimum\Sniffs\Sniff;
/**
* Ensure that non-PHP files are included via `file_get_contents()` instead of using `include/require[_once]`.
*
* This prevents potential PHP code embedded in those files from being automatically executed.
*/
class IncludingNonPHPFileSniff extends Sniff {
/**
* File extensions used for PHP files.
*
* Files with these extensions are allowed to be `include`d.
*
* @var array<string, bool> Key is the extension, value is irrelevant.
*/
private $php_extensions = [
'php' => true,
'inc' => true,
'phar' => true,
];
/**
* File extensions used for SVG and CSS files.
*
* @var array<string, bool> Key is the extension, value is irrelevant.
*/
private $svg_css_extensions = [
'css' => true,
'svg' => true,
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array<int|string>
*/
public function register() {
return Tokens::$includeTokens;
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param int $stackPtr The position of the current token in the stack passed in $tokens.
*
* @return void
*/
public function process_token( $stackPtr ) {
$end_of_statement = BCFile::findEndOfStatement( $this->phpcsFile, $stackPtr );
$curStackPtr = ( $end_of_statement + 1 );
do {
$curStackPtr = $this->phpcsFile->findPrevious( Tokens::$stringTokens, $curStackPtr - 1, $stackPtr );
if ( $curStackPtr === false ) {
return;
}
$stringWithoutEnclosingQuotationMarks = trim( $this->tokens[ $curStackPtr ]['content'], "\"'" );
$isFileName = preg_match( '`\.([a-z]{2,})$`i', $stringWithoutEnclosingQuotationMarks, $regexMatches );
if ( $isFileName !== 1 ) {
continue;
}
$extension = strtolower( $regexMatches[1] );
if ( isset( $this->php_extensions[ $extension ] ) === true ) {
return;
}
$message = 'Local non-PHP file should be loaded via `file_get_contents` rather than via `%s`. Found: %s';
$data = [
strtolower( $this->tokens[ $stackPtr ]['content'] ),
$this->tokens[ $curStackPtr ]['content'],
];
$code = 'IncludingNonPHPFile';
if ( isset( $this->svg_css_extensions[ $extension ] ) === true ) {
// Be more specific for SVG and CSS files.
$message = 'Local SVG and CSS files should be loaded via `file_get_contents` rather than via `%s`. Found: %s';
$code = 'IncludingSVGCSSFile';
}
$this->phpcsFile->addError( $message, $curStackPtr, $code, $data );
// Don't throw more than one error for any one statement.
return;
} while ( $curStackPtr > $stackPtr );
}
}