@@ -129,4 +129,167 @@ public static function tokenName(File $file, int $position): string
129
129
130
130
return $ file ->getTokens ()[$ namePosition ]['content ' ];
131
131
}
132
- }
132
+
133
+ /**
134
+ * @param int $start
135
+ * @param int $end
136
+ * @param File $file
137
+ * @param array ...$types
138
+ * @return array[]
139
+ */
140
+ public static function filterTokensByType (
141
+ int $ start ,
142
+ int $ end ,
143
+ File $ file ,
144
+ ...$ types
145
+ ): array {
146
+
147
+ return array_filter (
148
+ $ file ->getTokens (),
149
+ function (array $ token , int $ position ) use ($ start , $ end , $ types ): bool {
150
+ return
151
+ $ position >= $ start
152
+ && $ position <= $ end
153
+ && in_array ($ token ['code ' ] ?? '' , $ types , true );
154
+ },
155
+ ARRAY_FILTER_USE_BOTH
156
+ );
157
+ }
158
+
159
+ /**
160
+ * @param File $file
161
+ * @param int $closurePosition
162
+ * @param bool $lookForFilters
163
+ * @param bool $lookForActions
164
+ * @return bool
165
+ */
166
+ public static function isHookClosure (
167
+ File $ file ,
168
+ int $ closurePosition ,
169
+ bool $ lookForFilters = true ,
170
+ bool $ lookForActions = true
171
+ ): bool {
172
+
173
+ $ tokens = $ file ->getTokens ();
174
+ if (($ tokens [$ closurePosition ]['code ' ] ?? '' ) !== T_CLOSURE ) {
175
+ return false ;
176
+ }
177
+
178
+ $ lookForComma = $ file ->findPrevious (
179
+ [T_WHITESPACE ],
180
+ $ closurePosition - 1 ,
181
+ null ,
182
+ true ,
183
+ null ,
184
+ true
185
+ );
186
+
187
+ if (!$ lookForComma || ($ tokens [$ lookForComma ]['code ' ] ?? '' ) !== T_COMMA ) {
188
+ return false ;
189
+ }
190
+
191
+ $ functionCallOpen = $ file ->findPrevious (
192
+ [T_OPEN_PARENTHESIS ],
193
+ $ lookForComma - 2 ,
194
+ null ,
195
+ false ,
196
+ null ,
197
+ true
198
+ );
199
+
200
+ if (!$ functionCallOpen ) {
201
+ return false ;
202
+ }
203
+
204
+ $ functionCall = $ file ->findPrevious (
205
+ [T_WHITESPACE ],
206
+ $ functionCallOpen - 1 ,
207
+ null ,
208
+ true ,
209
+ null ,
210
+ true
211
+ );
212
+
213
+ $ actions = [];
214
+ $ lookForFilters and $ actions [] = 'add_filter ' ;
215
+ $ lookForActions and $ actions [] = 'add_action ' ;
216
+
217
+ return in_array (($ tokens [$ functionCall ]['content ' ] ?? '' ), $ actions , true );
218
+ }
219
+
220
+ /**
221
+ * @param File $file
222
+ * @param int $functionPosition
223
+ * @return array
224
+ */
225
+ public static function functionBoundaries (File $ file , int $ functionPosition ): array
226
+ {
227
+ $ tokens = $ file ->getTokens ();
228
+ $ functionStart = $ tokens [$ functionPosition ]['scope_opener ' ] ?? 0 ;
229
+ $ functionEnd = $ tokens [$ functionPosition ]['scope_closer ' ] ?? 0 ;
230
+ if (!$ functionStart || !$ functionEnd || $ functionStart >= ($ functionEnd - 1 )) {
231
+ return [-1 , -1 ];
232
+ }
233
+
234
+ return [$ functionStart , $ functionEnd ];
235
+ }
236
+
237
+ /**
238
+ * @param File $file
239
+ * @param int $functionPosition
240
+ * @return array
241
+ */
242
+ public static function countReturns (File $ file , int $ functionPosition ): array
243
+ {
244
+ list ($ functionStart , $ functionEnd ) = self ::functionBoundaries ($ file , $ functionPosition );
245
+ if ($ functionStart < 0 || $ functionEnd <= 0 ) {
246
+ return [0 , 0 ];
247
+ }
248
+
249
+ $ returnTokens = self ::filterTokensByType (
250
+ $ functionStart ,
251
+ $ functionEnd ,
252
+ $ file ,
253
+ T_RETURN
254
+ );
255
+
256
+ if (!$ returnTokens ) {
257
+ return [0 , 0 ];
258
+ }
259
+
260
+ $ nonVoidReturnCount = $ voidReturnCount = 0 ;
261
+ $ scopeClosers = [];
262
+ foreach ($ returnTokens as $ i => $ token ) {
263
+ if ($ scopeClosers && $ i === $ scopeClosers [0 ]) {
264
+ array_shift ($ scopeClosers );
265
+ }
266
+ if ($ token ['type ' ] === 'T_FUNCTION ' ) {
267
+ array_unshift ($ scopeClosers , $ token ['scope_closer ' ]);
268
+ }
269
+ if (!$ scopeClosers ) {
270
+ Helpers::isVoidReturn ($ file , $ i ) ? $ voidReturnCount ++ : $ nonVoidReturnCount ++;
271
+ }
272
+ }
273
+
274
+ return [$ nonVoidReturnCount , $ voidReturnCount ];
275
+ }
276
+
277
+ /**
278
+ * @param File $file
279
+ * @param int $returnPosition
280
+ * @return bool
281
+ */
282
+ public static function isVoidReturn (File $ file , int $ returnPosition ): bool
283
+ {
284
+ $ tokens = $ file ->getTokens ();
285
+
286
+ if (($ tokens [$ returnPosition ]['code ' ] ?? '' ) !== T_RETURN ) {
287
+ return false ;
288
+ }
289
+
290
+ $ returnPosition ++;
291
+ $ nextToReturn = $ file ->findNext ([T_WHITESPACE ], $ returnPosition , null , true , null , true );
292
+
293
+ return $ nextToReturn && ($ tokens [$ nextToReturn ]['type ' ] ?? '' ) === 'T_SEMICOLON ' ;
294
+ }
295
+ }
0 commit comments