*/ private array $classFileCache = []; /** * @return array> */ public function perTestFiles(): array { if (! PhpUnitCodeCoverage::instance()->isActive()) { return []; } try { $lineCoverage = PhpUnitCodeCoverage::instance() ->codeCoverage() ->getData() ->lineCoverage(); } catch (Throwable) { return []; } /** @var array> $edges */ $edges = []; foreach ($lineCoverage as $sourceFile => $lines) { $testIds = []; foreach ($lines as $hits) { if ($hits === null) { continue; } foreach ($hits as $id) { $testIds[$id] = true; } } foreach (array_keys($testIds) as $testId) { $testFile = $this->testIdToFile($testId); if ($testFile === null) { continue; } $edges[$testFile][$sourceFile] = true; } } $out = []; foreach ($edges as $testFile => $sources) { $out[$testFile] = array_keys($sources); } return $out; } public function reset(): void { $this->classFileCache = []; } private function testIdToFile(string $testId): ?string { $hash = strpos($testId, '#'); $identifier = $hash === false ? $testId : substr($testId, 0, $hash); if (! str_contains($identifier, '::')) { return null; } [$className] = explode('::', $identifier, 2); if (array_key_exists($className, $this->classFileCache)) { return $this->classFileCache[$className]; } $file = $this->resolveClassFile($className); $this->classFileCache[$className] = $file; return $file; } private function resolveClassFile(string $className): ?string { if (! class_exists($className, false)) { return null; } $reflection = new ReflectionClass($className); if ($reflection->hasProperty('__filename')) { $property = $reflection->getProperty('__filename'); if ($property->isStatic()) { $value = $property->getValue(); if (is_string($value)) { return $value; } } } $file = $reflection->getFileName(); return is_string($file) ? $file : null; } }