Conditions | 77 |
Paths | 24 |
Total Lines | 364 |
Code Lines | 211 |
Lines | 0 |
Ratio | 0 % |
Changes | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
1 | <?php |
||
157 | public static function getFunctionCalls(string $source, int $line, $function): array |
||
158 | { |
||
159 | static $up = [ |
||
160 | '(' => true, |
||
161 | '[' => true, |
||
162 | '{' => true, |
||
163 | T_CURLY_OPEN => true, |
||
164 | T_DOLLAR_OPEN_CURLY_BRACES => true, |
||
165 | ]; |
||
166 | static $down = [ |
||
167 | ')' => true, |
||
168 | ']' => true, |
||
169 | '}' => true, |
||
170 | ]; |
||
171 | static $modifiers = [ |
||
172 | '!' => true, |
||
173 | '@' => true, |
||
174 | '~' => true, |
||
175 | '+' => true, |
||
176 | '-' => true, |
||
177 | ]; |
||
178 | static $identifier = [ |
||
179 | T_DOUBLE_COLON => true, |
||
180 | T_STRING => true, |
||
181 | T_NS_SEPARATOR => true, |
||
182 | ]; |
||
183 | |||
184 | if (KINT_PHP80) { |
||
185 | $up[T_ATTRIBUTE] = true; |
||
186 | self::$operator[T_MATCH] = true; |
||
187 | self::$strip[T_NULLSAFE_OBJECT_OPERATOR] = true; |
||
188 | self::$classcalls[T_NULLSAFE_OBJECT_OPERATOR] = true; |
||
189 | self::$namespace[T_NAME_FULLY_QUALIFIED] = true; |
||
190 | self::$namespace[T_NAME_QUALIFIED] = true; |
||
191 | self::$namespace[T_NAME_RELATIVE] = true; |
||
192 | $identifier[T_NAME_FULLY_QUALIFIED] = true; |
||
193 | $identifier[T_NAME_QUALIFIED] = true; |
||
194 | $identifier[T_NAME_RELATIVE] = true; |
||
195 | } |
||
196 | |||
197 | if (!KINT_PHP84) { |
||
198 | self::$operator[T_NEW] = true; // @codeCoverageIgnore |
||
199 | } |
||
200 | |||
201 | /** @psalm-var list<PhpToken> */ |
||
202 | $tokens = \token_get_all($source); |
||
203 | $function_calls = []; |
||
204 | |||
205 | // Performance optimization preventing backwards loops |
||
206 | /** @psalm-var array<PhpToken|null> */ |
||
207 | $prev_tokens = [null, null, null]; |
||
208 | |||
209 | if (\is_array($function)) { |
||
210 | $class = \explode('\\', $function[0]); |
||
211 | $class = \strtolower(\end($class)); |
||
212 | $function = \strtolower($function[1]); |
||
213 | } else { |
||
214 | $class = null; |
||
215 | /** |
||
216 | * @psalm-suppress RedundantFunctionCallGivenDocblockType |
||
217 | * Psalm bug #11075 |
||
218 | */ |
||
219 | $function = \strtolower($function); |
||
220 | } |
||
221 | |||
222 | // Loop through tokens |
||
223 | foreach ($tokens as $index => $token) { |
||
224 | if (!\is_array($token)) { |
||
225 | continue; |
||
226 | } |
||
227 | |||
228 | if ($token[2] > $line) { |
||
229 | break; |
||
230 | } |
||
231 | |||
232 | // Store the last real tokens for later |
||
233 | if (isset(self::$ignore[$token[0]])) { |
||
234 | continue; |
||
235 | } |
||
236 | |||
237 | $prev_tokens = [$prev_tokens[1], $prev_tokens[2], $token]; |
||
238 | |||
239 | // The logic for 7.3 through 8.1 is far more complicated. |
||
240 | // This should speed things up without making a lot more work for us |
||
241 | if (KINT_PHP82 && $line !== $token[2]) { |
||
242 | continue; |
||
243 | } |
||
244 | |||
245 | // Check if it's the right type to be the function we're looking for |
||
246 | if (!isset(self::$namespace[$token[0]])) { |
||
247 | continue; |
||
248 | } |
||
249 | |||
250 | $ns = \explode('\\', \strtolower($token[1])); |
||
251 | |||
252 | if (\end($ns) !== $function) { |
||
253 | continue; |
||
254 | } |
||
255 | |||
256 | // Check if it's a function call |
||
257 | $nextReal = self::realTokenIndex($tokens, $index); |
||
258 | if ('(' !== ($tokens[$nextReal] ?? null)) { |
||
259 | continue; |
||
260 | } |
||
261 | |||
262 | // Check if it matches the signature |
||
263 | if (null === $class) { |
||
264 | if (null !== $prev_tokens[1] && isset(self::$classcalls[$prev_tokens[1][0]])) { |
||
265 | continue; |
||
266 | } |
||
267 | } else { |
||
268 | if (null === $prev_tokens[1] || T_DOUBLE_COLON !== $prev_tokens[1][0]) { |
||
269 | continue; |
||
270 | } |
||
271 | |||
272 | if (null === $prev_tokens[0] || !isset(self::$namespace[$prev_tokens[0][0]])) { |
||
273 | continue; |
||
274 | } |
||
275 | |||
276 | // All self::$namespace tokens are T_ constants |
||
277 | /** |
||
278 | * @psalm-var PhpTokenArray $prev_tokens[0] |
||
279 | * Psalm bug #746 (wontfix) |
||
280 | */ |
||
281 | $ns = \explode('\\', \strtolower($prev_tokens[0][1])); |
||
282 | |||
283 | if (\end($ns) !== $class) { |
||
284 | continue; |
||
285 | } |
||
286 | } |
||
287 | |||
288 | $last_line = $token[2]; |
||
289 | $depth = 1; // The depth respective to the function call |
||
290 | $offset = $nextReal + 1; // The start of the function call |
||
291 | $instring = false; // Whether we're in a string or not |
||
292 | $realtokens = false; // Whether the current scope contains anything meaningful or not |
||
293 | $paramrealtokens = false; // Whether the current parameter contains anything meaningful |
||
294 | $params = []; // All our collected parameters |
||
295 | $shortparam = []; // The short version of the parameter |
||
296 | $param_start = $offset; // The distance to the start of the parameter |
||
297 | |||
298 | // Loop through the following tokens until the function call ends |
||
299 | while (isset($tokens[$offset])) { |
||
300 | $token = $tokens[$offset]; |
||
301 | |||
302 | if (\is_array($token)) { |
||
303 | $last_line = $token[2]; |
||
304 | } |
||
305 | |||
306 | if (!isset(self::$ignore[$token[0]]) && !isset($down[$token[0]])) { |
||
307 | $paramrealtokens = $realtokens = true; |
||
308 | } |
||
309 | |||
310 | // If it's a token that makes us to up a level, increase the depth |
||
311 | if (isset($up[$token[0]])) { |
||
312 | if (1 === $depth) { |
||
313 | $shortparam[] = $token; |
||
314 | $realtokens = false; |
||
315 | } |
||
316 | |||
317 | ++$depth; |
||
318 | } elseif (isset($down[$token[0]])) { |
||
319 | --$depth; |
||
320 | |||
321 | // If this brings us down to the parameter level, and we've had |
||
322 | // real tokens since going up, fill the $shortparam with an ellipsis |
||
323 | if (1 === $depth) { |
||
324 | if ($realtokens) { |
||
325 | $shortparam[] = '...'; |
||
326 | } |
||
327 | $shortparam[] = $token; |
||
328 | } |
||
329 | } elseif ('"' === $token || 'b"' === $token) { |
||
330 | // Strings use the same symbol for up and down, but we can |
||
331 | // only ever be inside one string, so just use a bool for that |
||
332 | if ($instring) { |
||
333 | --$depth; |
||
334 | if (1 === $depth) { |
||
335 | $shortparam[] = '...'; |
||
336 | } |
||
337 | } else { |
||
338 | ++$depth; |
||
339 | } |
||
340 | |||
341 | $instring = !$instring; |
||
|
|||
342 | |||
343 | $shortparam[] = $token; |
||
344 | } elseif (1 === $depth) { |
||
345 | if (',' === $token[0]) { |
||
346 | $params[] = [ |
||
347 | 'full' => \array_slice($tokens, $param_start, $offset - $param_start), |
||
348 | 'short' => $shortparam, |
||
349 | ]; |
||
350 | $shortparam = []; |
||
351 | $paramrealtokens = false; |
||
352 | $param_start = $offset + 1; |
||
353 | } elseif (T_CONSTANT_ENCAPSED_STRING === $token[0]) { |
||
354 | $quote = $token[1][0]; |
||
355 | if ('b' === $quote) { |
||
356 | $quote = $token[1][1]; |
||
357 | if (\strlen($token[1]) > 3) { |
||
358 | $token[1] = 'b'.$quote.'...'.$quote; |
||
359 | } |
||
360 | } else { |
||
361 | if (\strlen($token[1]) > 2) { |
||
362 | $token[1] = $quote.'...'.$quote; |
||
363 | } |
||
364 | } |
||
365 | $shortparam[] = $token; |
||
366 | } else { |
||
367 | $shortparam[] = $token; |
||
368 | } |
||
369 | } |
||
370 | |||
371 | // Depth has dropped to 0 (So we've hit the closing paren) |
||
372 | if ($depth <= 0) { |
||
373 | if ($paramrealtokens) { |
||
374 | $params[] = [ |
||
375 | 'full' => \array_slice($tokens, $param_start, $offset - $param_start), |
||
376 | 'short' => $shortparam, |
||
377 | ]; |
||
378 | } |
||
379 | |||
380 | break; |
||
381 | } |
||
382 | |||
383 | ++$offset; |
||
384 | } |
||
385 | |||
386 | // If we're not passed (or at) the line at the end |
||
387 | // of the function call, we're too early so skip it |
||
388 | // Only applies to < 8.2 since we check line explicitly above that |
||
389 | if (!KINT_PHP82 && $last_line < $line) { |
||
390 | continue; // @codeCoverageIgnore |
||
391 | } |
||
392 | |||
393 | $formatted_parameters = []; |
||
394 | |||
395 | // Format the final output parameters |
||
396 | foreach ($params as $param) { |
||
397 | $name = self::tokensFormatted($param['short']); |
||
398 | $path = self::tokensToString(self::tokensTrim($param['full'])); |
||
399 | $expression = false; |
||
400 | $literal = false; |
||
401 | $new_without_parens = false; |
||
402 | |||
403 | foreach ($name as $token) { |
||
404 | if (self::tokenIsOperator($token)) { |
||
405 | $expression = true; |
||
406 | break; |
||
407 | } |
||
408 | } |
||
409 | |||
410 | // As of 8.4 new is only an expression when parentheses are |
||
411 | // omitted. In that case we can cheat and add them ourselves. |
||
412 | // |
||
413 | // > PHP interprets the first expression after new as a class name |
||
414 | // per https://wiki.php.net/rfc/new_without_parentheses |
||
415 | if (KINT_PHP84 && !$expression && T_NEW === $name[0][0]) { |
||
416 | $had_name_token = false; |
||
417 | $new_without_parens = true; |
||
418 | |||
419 | foreach ($name as $token) { |
||
420 | if (T_NEW === $token[0]) { |
||
421 | continue; |
||
422 | } |
||
423 | |||
424 | if (isset(self::$ignore[$token[0]])) { |
||
425 | continue; |
||
426 | } |
||
427 | |||
428 | if (T_CLASS === $token[0]) { |
||
429 | $new_without_parens = false; |
||
430 | break; |
||
431 | } |
||
432 | |||
433 | if ('(' === $token && $had_name_token) { |
||
434 | $new_without_parens = false; |
||
435 | break; |
||
436 | } |
||
437 | |||
438 | $had_name_token = true; |
||
439 | } |
||
440 | } |
||
441 | |||
442 | if (!$expression && 1 === \count($name)) { |
||
443 | switch ($name[0][0]) { |
||
444 | case T_CONSTANT_ENCAPSED_STRING: |
||
445 | case T_LNUMBER: |
||
446 | case T_DNUMBER: |
||
447 | $literal = true; |
||
448 | break; |
||
449 | case T_STRING: |
||
450 | switch (\strtolower($name[0][1])) { |
||
451 | case 'null': |
||
452 | case 'true': |
||
453 | case 'false': |
||
454 | $literal = true; |
||
455 | } |
||
456 | } |
||
457 | |||
458 | $name = self::tokensToString($name); |
||
459 | } else { |
||
460 | $name = self::tokensToString($name); |
||
461 | |||
462 | if (!$expression) { |
||
463 | switch (\strtolower($name)) { |
||
464 | case 'array()': |
||
465 | case '[]': |
||
466 | $literal = true; |
||
467 | break; |
||
468 | } |
||
469 | } |
||
470 | } |
||
471 | |||
472 | $formatted_parameters[] = [ |
||
473 | 'name' => $name, |
||
474 | 'path' => $path, |
||
475 | 'expression' => $expression, |
||
476 | 'literal' => $literal, |
||
477 | 'new_without_parens' => $new_without_parens, |
||
478 | ]; |
||
479 | } |
||
480 | |||
481 | // Skip first-class callables |
||
482 | if (KINT_PHP81 && 1 === \count($formatted_parameters) && '...' === \reset($formatted_parameters)['path']) { |
||
483 | continue; |
||
484 | } |
||
485 | |||
486 | // Get the modifiers |
||
487 | --$index; |
||
488 | |||
489 | while (isset($tokens[$index])) { |
||
490 | if (!isset(self::$ignore[$tokens[$index][0]]) && !isset($identifier[$tokens[$index][0]])) { |
||
491 | break; |
||
492 | } |
||
493 | |||
494 | --$index; |
||
495 | } |
||
496 | |||
497 | $mods = []; |
||
498 | |||
499 | while (isset($tokens[$index])) { |
||
500 | if (isset(self::$ignore[$tokens[$index][0]])) { |
||
501 | --$index; |
||
502 | continue; |
||
503 | } |
||
504 | |||
505 | if (isset($modifiers[$tokens[$index][0]])) { |
||
506 | $mods[] = $tokens[$index]; |
||
507 | --$index; |
||
508 | continue; |
||
509 | } |
||
510 | |||
511 | break; |
||
512 | } |
||
513 | |||
514 | $function_calls[] = [ |
||
515 | 'parameters' => $formatted_parameters, |
||
516 | 'modifiers' => $mods, |
||
517 | ]; |
||
518 | } |
||
519 | |||
520 | return $function_calls; |
||
521 | } |
||
660 |