77
88namespace WordPressVIPMinimum \Sniffs \Functions ;
99
10+ use PHP_CodeSniffer \Util \Tokens ;
1011use WordPressVIPMinimum \Sniffs \Sniff ;
1112
1213/**
13- * This sniff enforces that certain functions are not
14- * dynamically called.
14+ * This sniff enforces that certain functions are not dynamically called.
1515 *
1616 * An example:
17- *
18- * <code>
17+ * ```php
1918 * $func = 'func_num_args';
2019 * $func();
21- * </code>
20+ * ```
2221 *
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.
2423 *
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
2725 */
2826class DynamicCallsSniff extends Sniff {
27+
2928 /**
3029 * Functions that should not be called dynamically.
3130 *
3231 * @var array
3332 */
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 ,
4443 ];
4544
4645 /**
47- * Array of functions encountered, along with their values.
48- * Populated on run-time.
46+ * Array of variable assignments encountered, along with their values.
4947 *
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.
5151 */
5252 private $ variables_arr = [];
5353
@@ -61,8 +61,6 @@ class DynamicCallsSniff extends Sniff {
6161 /**
6262 * Returns the token types that this sniff is interested in.
6363 *
64- * We want everything variable- and function-related.
65- *
6664 * @return array(int)
6765 */
6866 public function register () {
@@ -87,163 +85,104 @@ public function process_token( $stackPtr ) {
8785 }
8886
8987 /**
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.
9390 *
9491 * @return void
9592 */
9693 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- }
10894
10995 $ current_var_name = $ this ->tokens [ $ this ->stackPtr ]['content ' ];
11096
11197 /*
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.
115100 */
116-
117101 $ t_item_key = $ this ->phpcsFile ->findNext (
118- [ T_WHITESPACE ] ,
102+ Tokens:: $ emptyTokens ,
119103 $ this ->stackPtr + 1 ,
120104 null ,
121105 true ,
122106 null ,
123107 true
124108 );
125109
126- if ( $ t_item_key === false ) {
110+ if ( $ t_item_key === false || $ this -> tokens [ $ t_item_key ][ ' code ' ] !== T_EQUAL ) {
127111 return ;
128112 }
129113
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 ;
132131 }
133132
134- if ( $ this ->tokens [ $ t_item_key ]['length ' ] !== 1 ) {
133+ if ( isset ( $ value_ptr ) === false ) {
134+ // Parse error. Bow out.
135135 return ;
136136 }
137137
138138 /*
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.
140141 */
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 ' ] );
149143
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.
151146 return ;
152147 }
153148
154149 /*
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.
158151 */
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 ;
165153 }
166154
167155 /**
168156 * 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.
171159 *
172160 * @return void
173161 */
174162 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.
180164 if ( empty ( $ this ->variables_arr ) ) {
181165 return ;
182166 }
183167
184168 /*
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.
200171 */
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 ' ] ] ) ) {
205173 return ;
206174 }
207175
208176 /*
209- * Check if we have an '(' next, or separated by whitespaces
210- * from our current position.
177+ * Check if we have an '(' next.
211178 */
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 ) {
241181 return ;
242182 }
243183
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(). ' ;
246185 $ 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 );
248187 }
249188}
0 commit comments