TestFinder.php 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. <?php
  2. namespace Michel\UniTester;
  3. final class TestFinder
  4. {
  5. private string $directory;
  6. public function __construct(string $directory)
  7. {
  8. if (!is_dir($directory)) {
  9. throw new \InvalidArgumentException("Directory '{$directory}' does not exist.");
  10. }
  11. $this->directory = $directory;
  12. }
  13. public function find(): array
  14. {
  15. return $this->findTestClasses();
  16. }
  17. private function findTestClasses(): array
  18. {
  19. $testClasses = [];
  20. $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory));
  21. foreach ($iterator as $file) {
  22. if ($file->isDir() || $file->getExtension() !== 'php') {
  23. continue;
  24. }
  25. $class = self::extractNamespaceAndClass($file->getPathname());
  26. if (class_exists($class) && is_subclass_of($class, TestCase::class)) {
  27. $testClasses[] = $class;
  28. }
  29. }
  30. return $testClasses;
  31. }
  32. private static function extractNamespaceAndClass(string $filePath): string
  33. {
  34. if (!file_exists($filePath)) {
  35. throw new \InvalidArgumentException('File not found: ' . $filePath);
  36. }
  37. $contents = file_get_contents($filePath);
  38. $namespace = '';
  39. $class = '';
  40. $isExtractingNamespace = false;
  41. $isExtractingClass = false;
  42. foreach (token_get_all($contents) as $token) {
  43. if (is_array($token) && $token[0] == T_NAMESPACE) {
  44. $isExtractingNamespace = true;
  45. }
  46. if (is_array($token) && $token[0] == T_CLASS) {
  47. $isExtractingClass = true;
  48. }
  49. if ($isExtractingNamespace) {
  50. if (is_array($token) && in_array($token[0], [T_STRING, T_NS_SEPARATOR, 265 /* T_NAME_QUALIFIED For PHP 8*/])) {
  51. $namespace .= $token[1];
  52. } else if ($token === ';') {
  53. $isExtractingNamespace = false;
  54. }
  55. }
  56. if ($isExtractingClass) {
  57. if (is_array($token) && $token[0] == T_STRING) {
  58. $class = $token[1];
  59. break;
  60. }
  61. }
  62. }
  63. return $namespace ? $namespace . '\\' . $class : $class;
  64. }
  65. }