getFiles(); \ksort($files); $xmlDocument = new \DOMDocument('1.0', 'UTF-8'); $xmlCoverage = $xmlDocument->createElement('coverage'); $xmlCoverage->setAttribute('generated', (string)\time()); $xmlDocument->appendChild($xmlCoverage); $xmlProject = $xmlDocument->createElement('project'); $xmlProject->setAttribute('timestamp', (string)\time()); $xmlCoverage->appendChild($xmlProject); $projectMetrics = Metrics::makeEmpty(); $packages = []; foreach ($files as $name => $file) { [$xmlFile, $fileMetrics] = $this->renderFile($xmlDocument, $file, $name); $projectMetrics = $projectMetrics->merge($fileMetrics); $packageName = $file->packageName; if ($packageName === null) { $xmlProject->appendChild($xmlFile); continue; } $existedPackage = $packages[$packageName] ?? null; if ($existedPackage !== null) { $existedPackage[0]->appendChild($xmlFile); $existedPackage[1] = $existedPackage[1]->merge($fileMetrics); $packages[$packageName] = $existedPackage; continue; } $xmlPackage = $xmlDocument->createElement('package'); $xmlPackage->setAttribute('name', $packageName); $xmlProject->appendChild($xmlPackage); $xmlPackage->appendChild($xmlFile); $packageMetrics = Metrics::makeEmpty(); $packageMetrics->packageCount = 1; $packageMetrics = $packageMetrics->merge($fileMetrics); $packages[$packageName] = [$xmlPackage, $packageMetrics]; } foreach ($packages as $package) { /** @var Metrics $packageMetrics */ $packageMetrics = $package[1]; $package[0]->appendChild($this->renderMetricsPackage($xmlDocument, $packageMetrics)); } $xmlProject->appendChild($this->renderMetricsProject($xmlDocument, $projectMetrics)); return [(string) $xmlDocument->saveXML(), $projectMetrics]; } /** * Create an XML element to represent these metrics under a file. * @param \DOMDocument $xmlDocument The parent document * @param Metrics $metrics * @return \DOMElement * @throws \DOMException */ private function renderMetrics(\DOMDocument $xmlDocument, Metrics $metrics): \DOMElement { $xmlMetrics = $xmlDocument->createElement('metrics'); // We can't know the complexity, just set 0 // (attribute required by the clover xml schema) $xmlMetrics->setAttribute('complexity', '0'); $xmlMetrics->setAttribute('elements', (string) $metrics->getElementCount()); $xmlMetrics->setAttribute('coveredelements', (string) $metrics->getCoveredElementCount()); $xmlMetrics->setAttribute('conditionals', (string) $metrics->conditionalCount); $xmlMetrics->setAttribute('coveredconditionals', (string) $metrics->coveredConditionalCount); $xmlMetrics->setAttribute('statements', (string) $metrics->statementCount); $xmlMetrics->setAttribute('coveredstatements', (string) $metrics->coveredStatementCount); $xmlMetrics->setAttribute('methods', (string) $metrics->methodCount); $xmlMetrics->setAttribute('coveredmethods', (string) $metrics->coveredMethodCount); $xmlMetrics->setAttribute('classes', (string) $metrics->classCount); return $xmlMetrics; } /** * Create an XML element to represent these metrics under a package. * Contains all the attributes of the file context plus the number of files. * @param \DOMDocument $xmlDocument The parent document. * @param Metrics $metrics * @return \DOMElement * @throws \DOMException */ private function renderMetricsPackage(\DOMDocument $xmlDocument, Metrics $metrics): \DOMElement { $xmlMetrics = $this->renderMetrics($xmlDocument, $metrics); $xmlMetrics->setAttribute('files', (string) $metrics->fileCount); return $xmlMetrics; } /** * Create an XML element to represent these metrics under a project. * Contains all the attributes of the package context plus the number of packages. * @param \DOMDocument $xmlDocument The parent document. * @param Metrics $metrics * @return \DOMElement * @throws \DOMException */ private function renderMetricsProject(\DOMDocument $xmlDocument, Metrics $metrics): \DOMElement { $xmlMetrics = $this->renderMetricsPackage($xmlDocument, $metrics); $xmlMetrics->setAttribute('packages', (string) $metrics->packageCount); return $xmlMetrics; } /** * @param \DOMDocument $document * @param FileDto $fileDto * @param string $name * @return array{0:\DOMElement,1:Metrics} * @throws \DOMException */ private function renderFile(\DOMDocument $document, FileDto $fileDto, string $name): array { $xmlFile = $document->createElement('file'); $xmlFile->setAttribute('name', $name); $classes = $fileDto->getClasses(); $lines = $fileDto->getLines(); // Metric counts $statementCount = 0; $coveredStatementCount = 0; $conditionalCount = 0; $coveredConditionalCount = 0; $methodCount = 0; $coveredMethodCount = 0; $classCount = \count($classes); foreach ($classes as $class) { $xmlFile->appendChild($this->renderClass($document, $class)); } foreach ($lines as $line) { $xmlFile->appendChild($this->renderLine($document, $line)); $properties = $line->getProperties(); $covered = $line->getCount() > 0; $type = $properties['type'] ?? 'stmt'; if ($type === 'method') { $methodCount++; if ($covered) { $coveredMethodCount++; } } elseif ($type === 'stmt') { $statementCount++; if ($covered) { $coveredStatementCount++; } } elseif ($type === 'cond') { $conditionalCount++; if ($covered) { $coveredConditionalCount++; } } } $metrics = new Metrics( $statementCount, $coveredStatementCount, $conditionalCount, $coveredConditionalCount, $methodCount, $coveredMethodCount, $classCount, 1 ); $xmlFile->appendChild($this->renderMetrics($document, $metrics)); return [$xmlFile, $metrics]; } /** * @param \DOMDocument $document * @param ClassDto $class * @return \DOMElement * @throws \DOMException */ private function renderClass(\DOMDocument $document, ClassDto $class): \DOMElement { $xmlClass = $document->createElement('class'); foreach ($class->getProperties() as $key => $value) { $xmlClass->setAttribute($key, $value); } return $xmlClass; } /** * @param \DOMDocument $document * @param LineDto $line * @return \DOMElement * @throws \DOMException */ private function renderLine(\DOMDocument $document, LineDto $line): \DOMElement { $xmlLine = $document->createElement('line'); foreach ($line->getProperties() as $key => $value) { $xmlLine->setAttribute($key, $value); } $xmlLine->setAttribute('count', (string) $line->getCount()); return $xmlLine; } }