@@ -68,6 +68,49 @@ abstract class WordPress_AbstractFunctionRestrictionsSniff extends WordPress_Sni
68
68
*/
69
69
protected $ excluded_groups = array ();
70
70
71
+ /**
72
+ * List of known PHP and WP function which take a callback as an argument.
73
+ *
74
+ * @since 0.11.0
75
+ *
76
+ * @var array <string function name> => <int callback argument position>
77
+ */
78
+ protected $ callback_functions = array (
79
+ 'add_filter ' => array ( 2 ),
80
+ 'add_action ' => array ( 2 ),
81
+ 'call_user_func ' => array ( 1 ),
82
+ 'call_user_func_array ' => array ( 1 ),
83
+ 'forward_static_call ' => array ( 1 ),
84
+ 'forward_static_call_array ' => array ( 1 ),
85
+ 'array_diff_uassoc ' => array ( -1 ), // = last argument passed.
86
+ 'array_diff_ukey ' => array ( -1 ), // = last argument passed.
87
+ 'array_filter ' => array ( 2 ),
88
+ 'array_intersect_uassoc ' => array ( -1 ), // = last argument passed.
89
+ 'array_intersect_ukey ' => array ( -1 ), // = last argument passed.
90
+ 'array_map ' => array ( 1 ),
91
+ 'array_reduce ' => array ( 2 ),
92
+ 'array_udiff_assoc ' => array ( -1 ), // = last argument passed.
93
+ 'array_udiff_uassoc ' => array ( -1 , -2 ), // = last argument passed.
94
+ 'array_udiff ' => array ( -1 ), // = last argument passed.
95
+ 'array_uintersect_assoc ' => array ( -1 ), // = last argument passed.
96
+ 'array_uintersect_uassoc ' => array ( -1 , -2 ), // = last argument passed.
97
+ 'array_uintersect ' => array ( -1 ), // = last argument passed.
98
+ 'array_walk ' => array ( 2 ),
99
+ 'array_walk_recursive ' => array ( 2 ),
100
+ 'iterator_apply ' => array ( 2 ),
101
+ 'usort ' => array ( 2 ),
102
+ 'uasort ' => array ( 2 ),
103
+ 'uksort ' => array ( 2 ),
104
+ 'preg_replace_callback ' => array ( 2 ),
105
+ 'mb_ereg_replace_callback ' => array ( 2 ),
106
+ 'header_register_callback ' => array ( 1 ),
107
+ 'ob_start ' => array ( 1 ),
108
+ 'set_error_handler ' => array ( 1 ),
109
+ 'set_exception_handler ' => array ( 1 ),
110
+ 'register_shutdown_function ' => array ( 1 ),
111
+ 'register_tick_function ' => array ( 1 ),
112
+ );
113
+
71
114
/**
72
115
* Groups of functions to restrict.
73
116
*
@@ -170,7 +213,14 @@ public function process( PHP_CodeSniffer_File $phpcsFile, $stackPtr ) {
170
213
$ this ->init ( $ phpcsFile );
171
214
172
215
if ( true === $ this ->is_targetted_token ( $ stackPtr ) ) {
216
+
217
+ $ callback_matches = $ this ->check_for_callback_matches ( $ stackPtr );
218
+ if ( $ callback_matches ) {
219
+ return $ callback_matches ;
220
+ }
221
+
173
222
return $ this ->check_for_matches ( $ stackPtr );
223
+
174
224
}
175
225
176
226
} // End process().
@@ -229,7 +279,7 @@ public function is_targetted_token( $stackPtr ) {
229
279
* normal file processing.
230
280
*/
231
281
public function check_for_matches ( $ stackPtr ) {
232
- $ token_content = strtolower ( $ this ->tokens [ $ stackPtr ]['content ' ] );
282
+ $ token_content = strtolower ( $ this ->strip_quotes ( $ this -> tokens [ $ stackPtr ]['content ' ] ) );
233
283
$ skip_to = array ();
234
284
235
285
foreach ( $ this ->groups as $ groupName => $ group ) {
@@ -255,6 +305,64 @@ public function check_for_matches( $stackPtr ) {
255
305
256
306
} // End check_for_matches().
257
307
308
+ /**
309
+ * Verify if the current token is one of the targetted functions as callback.
310
+ *
311
+ * @since 0.11.0
312
+ *
313
+ * @param int $stackPtr The position of the current token in the stack.
314
+ *
315
+ * @return int|false
316
+ */
317
+ public function check_for_callback_matches ( $ stackPtr ) {
318
+
319
+ $ token_content = strtolower ( $ this ->tokens [ $ stackPtr ]['content ' ] );
320
+
321
+ // Check if the function is used as a callback.
322
+ if ( ! isset ( $ this ->callback_functions [ $ token_content ] ) ) {
323
+ return false ;
324
+ }
325
+
326
+ $ skip_to = array ();
327
+ $ parameters = $ this ->get_function_call_parameters ( $ stackPtr );
328
+ $ positions = $ this ->callback_functions [ $ token_content ];
329
+
330
+ foreach ( $ positions as $ position ) {
331
+
332
+ // Calculate the last argument if the position is negative.
333
+ if ( $ position < 0 ) {
334
+ $ position = count ( $ parameters ) + 1 + $ position ;
335
+ }
336
+
337
+ if ( ! isset ( $ parameters [ $ position ] ) ) {
338
+ return false ;
339
+ }
340
+
341
+ // Only get function name not anonymous funtions.
342
+ $ callback = $ this ->phpcsFile ->findNext (
343
+ array ( T_CONSTANT_ENCAPSED_STRING ),
344
+ $ parameters [ $ position ]['start ' ],
345
+ null ,
346
+ false ,
347
+ null ,
348
+ true
349
+ );
350
+
351
+ if ( ! $ callback ) {
352
+ return false ;
353
+ }
354
+
355
+ $ skip_to [] = $ this ->check_for_matches ( $ callback );
356
+ }
357
+
358
+ if ( empty ( $ skip_to ) || min ( $ skip_to ) === 0 ) {
359
+ return false ;
360
+ }
361
+
362
+ return min ( $ skip_to );
363
+
364
+ }
365
+
258
366
/**
259
367
* Process a matched token.
260
368
*
0 commit comments