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