PhpUnitTestCaseIndicator.php 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of PHP CS Fixer.
  5. *
  6. * (c) Fabien Potencier <fabien@symfony.com>
  7. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  8. *
  9. * This source file is subject to the MIT license that is bundled
  10. * with this source code in the file LICENSE.
  11. */
  12. namespace PhpCsFixer\Indicator;
  13. use PhpCsFixer\Preg;
  14. use PhpCsFixer\Tokenizer\Tokens;
  15. /**
  16. * @internal
  17. */
  18. final class PhpUnitTestCaseIndicator
  19. {
  20. public function isPhpUnitClass(Tokens $tokens, int $index): bool
  21. {
  22. if (!$tokens[$index]->isGivenKind(T_CLASS)) {
  23. throw new \LogicException(sprintf('No "T_CLASS" at given index %d, got "%s".', $index, $tokens[$index]->getName()));
  24. }
  25. $index = $tokens->getNextMeaningfulToken($index);
  26. if (!$tokens[$index]->isGivenKind(T_STRING)) {
  27. return false;
  28. }
  29. $extendsIndex = $tokens->getNextTokenOfKind($index, ['{', [T_EXTENDS]]);
  30. if (!$tokens[$extendsIndex]->isGivenKind(T_EXTENDS)) {
  31. return false;
  32. }
  33. if (Preg::match('/(?:Test|TestCase)$/', $tokens[$index]->getContent())) {
  34. return true;
  35. }
  36. while (null !== $index = $tokens->getNextMeaningfulToken($index)) {
  37. if ($tokens[$index]->equals('{')) {
  38. break; // end of class signature
  39. }
  40. if (!$tokens[$index]->isGivenKind(T_STRING)) {
  41. continue; // not part of extends nor part of implements; so continue
  42. }
  43. if (Preg::match('/(?:Test|TestCase)(?:Interface)?$/', $tokens[$index]->getContent())) {
  44. return true;
  45. }
  46. }
  47. return false;
  48. }
  49. /**
  50. * Returns an indices of PHPUnit classes in reverse appearance order.
  51. * Order is important - it's reverted, so if we inject tokens into collection,
  52. * we do it for bottom of file first, and then to the top of the file, so we
  53. * mitigate risk of not visiting whole collections (final indices).
  54. *
  55. * @return iterable<array{0: int, 1: int}> array of [int start, int end] indices from later to earlier classes
  56. */
  57. public function findPhpUnitClasses(Tokens $tokens): iterable
  58. {
  59. for ($index = $tokens->count() - 1; $index > 0; --$index) {
  60. if (!$tokens[$index]->isGivenKind(T_CLASS) || !$this->isPhpUnitClass($tokens, $index)) {
  61. continue;
  62. }
  63. $startIndex = $tokens->getNextTokenOfKind($index, ['{']);
  64. if (null === $startIndex) {
  65. return;
  66. }
  67. $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $startIndex);
  68. yield [$startIndex, $endIndex];
  69. }
  70. }
  71. }