7
7
8
8
namespace WordPressVIPMinimum \Sniffs \Functions ;
9
9
10
+ use PHP_CodeSniffer \Util \Tokens ;
10
11
use WordPressVIPMinimum \Sniffs \Sniff ;
11
12
12
13
/**
13
- * This sniff enforces that certain functions are not
14
- * dynamically called.
14
+ * This sniff enforces that certain functions are not dynamically called.
15
15
*
16
16
* An example:
17
- *
18
- * <code>
17
+ * ```php
19
18
* $func = 'func_num_args';
20
19
* $func();
21
- * </code>
20
+ * ```
22
21
*
23
- * See here: http://php.net/manual/en/migration71.incompatible.php
22
+ * Note that this sniff does not catch all possible forms of dynamic calling, only some.
24
23
*
25
- * Note that this sniff does not catch all possible forms of dynamic
26
- * calling, only some.
24
+ * @link http://php.net/manual/en/migration71.incompatible.php
27
25
*/
28
26
class DynamicCallsSniff extends Sniff {
27
+
29
28
/**
30
29
* Functions that should not be called dynamically.
31
30
*
32
31
* @var array
33
32
*/
34
- private $ blacklisted_functions = [
35
- 'assert ' ,
36
- 'compact ' ,
37
- 'extract ' ,
38
- 'func_get_args ' ,
39
- 'func_get_arg ' ,
40
- 'func_num_args ' ,
41
- 'get_defined_vars ' ,
42
- 'mb_parse_str ' ,
43
- 'parse_str ' ,
33
+ private $ function_names = [
34
+ 'assert ' => true ,
35
+ 'compact ' => true ,
36
+ 'extract ' => true ,
37
+ 'func_get_args ' => true ,
38
+ 'func_get_arg ' => true ,
39
+ 'func_num_args ' => true ,
40
+ 'get_defined_vars ' => true ,
41
+ 'mb_parse_str ' => true ,
42
+ 'parse_str ' => true ,
44
43
];
45
44
46
45
/**
47
- * Array of functions encountered, along with their values.
48
- * Populated on run-time.
46
+ * Array of variable assignments encountered, along with their values.
49
47
*
50
- * @var array
48
+ * Populated at run-time.
49
+ *
50
+ * @var array The key is the name of the variable, the value, its assigned value.
51
51
*/
52
52
private $ variables_arr = [];
53
53
@@ -61,8 +61,6 @@ class DynamicCallsSniff extends Sniff {
61
61
/**
62
62
* Returns the token types that this sniff is interested in.
63
63
*
64
- * We want everything variable- and function-related.
65
- *
66
64
* @return array(int)
67
65
*/
68
66
public function register () {
@@ -87,163 +85,104 @@ public function process_token( $stackPtr ) {
87
85
}
88
86
89
87
/**
90
- * Finds any variable-definitions in the file being processed,
91
- * and stores them internally in a private array. The data stored
92
- * is the name of the variable and its assigned value.
88
+ * Finds any variable-definitions in the file being processed and stores them
89
+ * internally in a private array.
93
90
*
94
91
* @return void
95
92
*/
96
93
private function collect_variables () {
97
- /*
98
- * Make sure we are working with a variable,
99
- * get its value if so.
100
- */
101
-
102
- if (
103
- $ this ->tokens [ $ this ->stackPtr ]['type ' ] !==
104
- 'T_VARIABLE '
105
- ) {
106
- return ;
107
- }
108
94
109
95
$ current_var_name = $ this ->tokens [ $ this ->stackPtr ]['content ' ];
110
96
111
97
/*
112
- * Find assignments ( $foo = "bar"; )
113
- * -- do this by finding all non-whitespaces, and
114
- * check if the first one is T_EQUAL.
98
+ * Find assignments ( $foo = "bar"; ) by finding all non-whitespaces,
99
+ * and checking if the first one is T_EQUAL.
115
100
*/
116
-
117
101
$ t_item_key = $ this ->phpcsFile ->findNext (
118
- [ T_WHITESPACE ] ,
102
+ Tokens:: $ emptyTokens ,
119
103
$ this ->stackPtr + 1 ,
120
104
null ,
121
105
true ,
122
106
null ,
123
107
true
124
108
);
125
109
126
- if ( $ t_item_key === false ) {
110
+ if ( $ t_item_key === false || $ this -> tokens [ $ t_item_key ][ ' code ' ] !== T_EQUAL ) {
127
111
return ;
128
112
}
129
113
130
- if ( $ this ->tokens [ $ t_item_key ]['type ' ] !== 'T_EQUAL ' ) {
131
- return ;
114
+ /*
115
+ * Find assignments which only assign a plain text string.
116
+ */
117
+ $ end_of_statement = $ this ->phpcsFile ->findNext ( [ T_SEMICOLON , T_CLOSE_TAG ], ( $ t_item_key + 1 ) );
118
+ $ value_ptr = null ;
119
+
120
+ for ( $ i = $ t_item_key + 1 ; $ i < $ end_of_statement ; $ i ++ ) {
121
+ if ( isset ( Tokens::$ emptyTokens [ $ this ->tokens [ $ i ]['code ' ] ] ) === true ) {
122
+ continue ;
123
+ }
124
+
125
+ if ( $ this ->tokens [ $ i ]['code ' ] !== T_CONSTANT_ENCAPSED_STRING ) {
126
+ // Not a plain text string value. Value cannot be determined reliably.
127
+ return ;
128
+ }
129
+
130
+ $ value_ptr = $ i ;
132
131
}
133
132
134
- if ( $ this ->tokens [ $ t_item_key ]['length ' ] !== 1 ) {
133
+ if ( isset ( $ value_ptr ) === false ) {
134
+ // Parse error. Bow out.
135
135
return ;
136
136
}
137
137
138
138
/*
139
- * Find encapsulated string ( "" )
139
+ * If we reached the end of the loop and the $value_ptr was set, we know for sure
140
+ * this was a plain text string variable assignment.
140
141
*/
141
- $ t_item_key = $ this ->phpcsFile ->findNext (
142
- [ T_CONSTANT_ENCAPSED_STRING ],
143
- $ t_item_key + 1 ,
144
- null ,
145
- false ,
146
- null ,
147
- true
148
- );
142
+ $ current_var_value = $ this ->strip_quotes ( $ this ->tokens [ $ value_ptr ]['content ' ] );
149
143
150
- if ( $ t_item_key === false ) {
144
+ if ( isset ( $ this ->function_names [ $ current_var_value ] ) === false ) {
145
+ // Text string is not one of the ones we're looking for.
151
146
return ;
152
147
}
153
148
154
149
/*
155
- * We have found variable-assignment,
156
- * register its name and value in the
157
- * internal array for later usage.
150
+ * Register the variable name and value in the internal array for later usage.
158
151
*/
159
-
160
- $ current_var_value =
161
- $ this ->tokens [ $ t_item_key ]['content ' ];
162
-
163
- $ this ->variables_arr [ $ current_var_name ] =
164
- str_replace ( "' " , '' , $ current_var_value );
152
+ $ this ->variables_arr [ $ current_var_name ] = $ current_var_value ;
165
153
}
166
154
167
155
/**
168
156
* Find any dynamic calls being made using variables.
169
- * Report on this when found, using name of the function
170
- * in the message.
157
+ *
158
+ * Report on this when found, using the name of the function in the message.
171
159
*
172
160
* @return void
173
161
*/
174
162
private function find_dynamic_calls () {
175
- /*
176
- * No variables detected; no basis for doing
177
- * anything
178
- */
179
-
163
+ // No variables detected; no basis for doing anything.
180
164
if ( empty ( $ this ->variables_arr ) ) {
181
165
return ;
182
166
}
183
167
184
168
/*
185
- * Make sure we do have a variable to work with.
186
- */
187
-
188
- if (
189
- $ this ->tokens [ $ this ->stackPtr ]['type ' ] !==
190
- 'T_VARIABLE '
191
- ) {
192
- return ;
193
- }
194
-
195
- /*
196
- * If variable is not found in our registry of
197
- * variables, do nothing, as we cannot be
198
- * sure that the function being called is one of the
199
- * blacklisted ones.
169
+ * If variable is not found in our registry of variables, do nothing, as we cannot be
170
+ * sure that the function being called is one of the blacklisted ones.
200
171
*/
201
-
202
- if ( ! isset (
203
- $ this ->variables_arr [ $ this ->tokens [ $ this ->stackPtr ]['content ' ] ]
204
- ) ) {
172
+ if ( ! isset ( $ this ->variables_arr [ $ this ->tokens [ $ this ->stackPtr ]['content ' ] ] ) ) {
205
173
return ;
206
174
}
207
175
208
176
/*
209
- * Check if we have an '(' next, or separated by whitespaces
210
- * from our current position.
177
+ * Check if we have an '(' next.
211
178
*/
212
-
213
- $ i = 0 ;
214
-
215
- do {
216
- $ i ++;
217
- } while (
218
- $ this ->tokens [ $ this ->stackPtr + $ i ]['type ' ] ===
219
- 'T_WHITESPACE '
220
- );
221
-
222
- if (
223
- $ this ->tokens [ $ this ->stackPtr + $ i ]['type ' ] !==
224
- 'T_OPEN_PARENTHESIS '
225
- ) {
226
- return ;
227
- }
228
-
229
- $ t_item_key = $ this ->stackPtr + $ i ;
230
-
231
- /*
232
- * We have a variable match, but make sure it contains name
233
- * of a function which is on our blacklist.
234
- */
235
-
236
- if ( ! in_array (
237
- $ this ->variables_arr [ $ this ->tokens [ $ this ->stackPtr ]['content ' ] ],
238
- $ this ->blacklisted_functions ,
239
- true
240
- ) ) {
179
+ $ next = $ this ->phpcsFile ->findNext ( Tokens::$ emptyTokens , ( $ this ->stackPtr + 1 ), null , true );
180
+ if ( $ next === false || $ this ->tokens [ $ next ]['code ' ] !== T_OPEN_PARENTHESIS ) {
241
181
return ;
242
182
}
243
183
244
- // We do, so report.
245
- $ message = 'Dynamic calling is not recommended in the case of %s. ' ;
184
+ $ message = 'Dynamic calling is not recommended in the case of %s(). ' ;
246
185
$ data = [ $ this ->variables_arr [ $ this ->tokens [ $ this ->stackPtr ]['content ' ] ] ];
247
- $ this ->phpcsFile ->addError ( $ message , $ t_item_key , 'DynamicCalls ' , $ data );
186
+ $ this ->phpcsFile ->addError ( $ message , $ this -> stackPtr , 'DynamicCalls ' , $ data );
248
187
}
249
188
}
0 commit comments