@@ -24,10 +24,15 @@ class ProperEscapingFunctionSniff extends Sniff {
24
24
* @var array
25
25
*/
26
26
public $ escaping_functions = [
27
- 'esc_url ' ,
28
- 'esc_attr ' ,
29
- 'esc_attr__ ' ,
30
- 'esc_html ' ,
27
+ 'esc_url ' => 'url ' ,
28
+ 'esc_attr ' => 'attr ' ,
29
+ 'esc_attr__ ' => 'attr ' ,
30
+ 'esc_attr_x ' => 'attr ' ,
31
+ 'esc_attr_e ' => 'attr ' ,
32
+ 'esc_html ' => 'html ' ,
33
+ 'esc_html__ ' => 'html ' ,
34
+ 'esc_html_x ' => 'html ' ,
35
+ 'esc_html_e ' => 'html ' ,
31
36
];
32
37
33
38
/**
@@ -48,51 +53,38 @@ public function register() {
48
53
*/
49
54
public function process_token ( $ stackPtr ) {
50
55
51
- if ( in_array ( $ this ->tokens [ $ stackPtr ]['content ' ], $ this -> escaping_functions , true ) === false ) {
56
+ if ( isset ( $ this ->escaping_functions [ $ this -> tokens [ $ stackPtr ]['content ' ] ] ) === false ) {
52
57
return ;
53
58
}
54
59
55
60
$ function_name = $ this ->tokens [ $ stackPtr ]['content ' ];
56
61
57
62
$ data = [ $ function_name ];
58
63
59
- if ( $ function_name === 'esc_attr ' || $ function_name === 'esc_attr__ ' ) {
60
- // Find esc_attr being used between HTML tags.
61
- $ tokens = array_merge (
62
- Tokens::$ emptyTokens ,
63
- [
64
- 'T_ECHO ' => T_ECHO ,
65
- 'T_OPEN_TAG ' => T_OPEN_TAG ,
66
- ]
67
- );
68
- $ html_tag = $ this ->phpcsFile ->findPrevious ( $ tokens , $ stackPtr - 1 , null , true );
69
- if ( $ this ->tokens [ $ html_tag ]['type ' ] === 'T_INLINE_HTML ' && false !== strpos ( $ this ->tokens [ $ html_tag ]['content ' ], '> ' ) ) {
70
- $ message = 'Wrong escaping function, please do not use `%s()` in a context outside of HTML attributes. ' ;
71
- $ this ->phpcsFile ->addError ( $ message , $ html_tag , 'notAttrEscAttr ' , $ data );
72
- return ;
73
- }
74
- }
75
-
76
- $ echo_or_string_concat = $ this ->phpcsFile ->findPrevious ( Tokens::$ emptyTokens , $ stackPtr - 1 , null , true );
77
-
78
- if ( $ this ->tokens [ $ echo_or_string_concat ]['code ' ] === T_ECHO ) {
64
+ $ echo_or_concat_or_html = $ this ->phpcsFile ->findPrevious ( Tokens::$ emptyTokens , $ stackPtr - 1 , null , true );
65
+
66
+ if ( $ this ->tokens [ $ echo_or_concat_or_html ]['code ' ] === T_ECHO ) {
79
67
// Very likely inline HTML with <?php tag.
80
- $ php_open = $ this ->phpcsFile ->findPrevious ( Tokens::$ emptyTokens , $ echo_or_string_concat - 1 , null , true );
68
+ $ php_open = $ this ->phpcsFile ->findPrevious ( Tokens::$ emptyTokens , $ echo_or_concat_or_html - 1 , null , true );
81
69
82
70
if ( $ this ->tokens [ $ php_open ]['code ' ] !== T_OPEN_TAG ) {
83
71
return ;
84
72
}
85
73
86
74
$ html = $ this ->phpcsFile ->findPrevious ( Tokens::$ emptyTokens , $ php_open - 1 , null , true );
87
75
88
- if ( $ this ->tokens [ $ html ]['code ' ] !== T_INLINE_HTML ) {
76
+ if ( $ this ->tokens [ $ html ]['code ' ] === T_INLINE_HTML && $ this ->is_outside_html_attr_context ( $ function_name , $ this ->tokens [ $ html ]['content ' ] ) ) {
77
+ $ message = 'Wrong escaping function, please do not use `%s()` in a context outside of HTML attributes. ' ;
78
+ $ this ->phpcsFile ->addError ( $ message , $ html , 'notAttrEscAttr ' , $ data );
89
79
return ;
90
80
}
91
- } elseif ( $ this ->tokens [ $ echo_or_string_concat ]['code ' ] === T_STRING_CONCAT ) {
81
+ } elseif ( $ this ->tokens [ $ echo_or_concat_or_html ]['code ' ] === T_STRING_CONCAT || $ this -> tokens [ $ echo_or_concat_or_html ][ ' code ' ] === T_COMMA ) {
92
82
// Very likely string concatenation mixing strings and functions/variables.
93
- $ html = $ this ->phpcsFile ->findPrevious ( Tokens::$ emptyTokens , $ echo_or_string_concat - 1 , null , true );
83
+ $ html = $ this ->phpcsFile ->findPrevious ( Tokens::$ emptyTokens , $ echo_or_concat_or_html - 1 , null , true );
94
84
95
- if ( $ this ->tokens [ $ html ]['code ' ] !== T_CONSTANT_ENCAPSED_STRING ) {
85
+ if ( $ this ->tokens [ $ html ]['code ' ] === T_CONSTANT_ENCAPSED_STRING && $ this ->is_outside_html_attr_context ( $ function_name , trim ( $ this ->tokens [ $ html ]['content ' ], '" \'' ) ) ) {
86
+ $ message = 'Wrong escaping function, please do not use `%s()` in a context outside of HTML attributes. ' ;
87
+ $ this ->phpcsFile ->addError ( $ message , $ html , 'notAttrEscAttr ' , $ data );
96
88
return ;
97
89
}
98
90
} else {
@@ -171,4 +163,16 @@ public function is_html_attr( $content ) {
171
163
public function endswith ( $ haystack , $ needle ) {
172
164
return substr ( $ haystack , -strlen ( $ needle ) ) === $ needle ;
173
165
}
166
+
167
+ /**
168
+ * Tests whether provided string ends with closing HTML tag in an attribute context.
169
+ *
170
+ * @param string $function_name Escaping attribute function name.
171
+ * @param string $content Haystack in which we look for open HTML tag.
172
+ *
173
+ * @return bool True if string ends with open HTML tag.
174
+ */
175
+ public function is_outside_html_attr_context ( $ function_name , $ content ) {
176
+ return $ this ->escaping_functions [ $ function_name ] === 'attr ' && substr ( $ content , -1 ) === '> ' ;
177
+ }
174
178
}
0 commit comments