diff --git a/composer.json b/composer.json
index 744507de0..259a58caa 100644
--- a/composer.json
+++ b/composer.json
@@ -1,67 +1,73 @@
{
- "name": "phpunit/php-code-coverage",
- "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
- "type": "library",
- "keywords": [
- "coverage",
- "testing",
- "xunit"
- ],
- "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
- "license": "BSD-3-Clause",
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
- }
- ],
- "support": {
- "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
- "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy"
- },
- "config": {
- "platform": {
- "php": "8.3.0"
- },
- "optimize-autoloader": true,
- "sort-packages": true
- },
- "prefer-stable": true,
- "require": {
- "php": ">=8.3",
- "ext-dom": "*",
- "ext-libxml": "*",
- "ext-xmlwriter": "*",
- "nikic/php-parser": "^5.6.1",
- "phpunit/php-file-iterator": "^6.0",
- "phpunit/php-text-template": "^5.0",
- "sebastian/complexity": "^5.0",
- "sebastian/environment": "^8.0.3",
- "sebastian/lines-of-code": "^4.0",
- "sebastian/version": "^6.0",
- "theseer/tokenizer": "^1.2.3"
- },
- "require-dev": {
- "phpunit/phpunit": "^12.3.7"
- },
- "suggest": {
- "ext-pcov": "PHP extension that provides line coverage",
- "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "autoload-dev": {
- "classmap": [
- "tests/"
- ]
+ "name": "phpunit/php-code-coverage",
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "type": "library",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "license": "BSD-3-Clause",
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy"
+ },
+ "config": {
+ "platform": {
+ "php": "8.3.0"
},
- "extra": {
- "branch-alias": {
- "dev-main": "12.4.x-dev"
- }
+ "optimize-autoloader": true,
+ "sort-packages": true
+ },
+ "prefer-stable": true,
+ "require": {
+ "php": ">=8.3",
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^5.6.1",
+ "phpunit/php-file-iterator": "^6.0",
+ "phpunit/php-text-template": "^5.0",
+ "sebastian/complexity": "^5.0",
+ "sebastian/environment": "^8.0.3",
+ "sebastian/lines-of-code": "^4.0",
+ "sebastian/version": "^6.0",
+ "theseer/tokenizer": "dev-writer as 1.3-dev"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.3.7"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "autoload-dev": {
+ "classmap": [
+ "tests/"
+ ]
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-main": "12.4.x-dev"
+ }
+ },
+ "repositories": [
+ {
+ "type": "vcs",
+ "url": "https://github.com/staabm/tokenizer"
}
+ ]
}
diff --git a/src/Report/Xml/BuildInformation.php b/src/Report/Xml/BuildInformation.php
index 654eecb31..b9375f228 100644
--- a/src/Report/Xml/BuildInformation.php
+++ b/src/Report/Xml/BuildInformation.php
@@ -9,63 +9,47 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
use function phpversion;
use DateTimeImmutable;
-use DOMElement;
use SebastianBergmann\Environment\Runtime;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class BuildInformation
{
- private DOMElement $contextNode;
-
public function __construct(
- DOMElement $contextNode,
+ XMLWriter $xmlWriter,
Runtime $runtime,
DateTimeImmutable $buildDate,
string $phpUnitVersion,
string $coverageVersion
) {
- $this->contextNode = $contextNode;
-
- $runtimeNode = $this->nodeByName('runtime');
+ $xmlWriter->startElement('build');
+ $xmlWriter->writeAttribute('time', $buildDate->format('D M j G:i:s T Y'));
+ $xmlWriter->writeAttribute('phpunit', $phpUnitVersion);
+ $xmlWriter->writeAttribute('coverage', $coverageVersion);
- $runtimeNode->setAttribute('name', $runtime->getName());
- $runtimeNode->setAttribute('version', $runtime->getVersion());
- $runtimeNode->setAttribute('url', $runtime->getVendorUrl());
+ $xmlWriter->startElement('runtime');
+ $xmlWriter->writeAttribute('name', $runtime->getName());
+ $xmlWriter->writeAttribute('version', $runtime->getVersion());
+ $xmlWriter->writeAttribute('url', $runtime->getVendorUrl());
+ $xmlWriter->endElement();
- $driverNode = $this->nodeByName('driver');
+ $xmlWriter->startElement('driver');
if ($runtime->hasXdebug()) {
- $driverNode->setAttribute('name', 'xdebug');
- $driverNode->setAttribute('version', phpversion('xdebug'));
+ $xmlWriter->writeAttribute('name', 'xdebug');
+ $xmlWriter->writeAttribute('version', phpversion('xdebug'));
}
if ($runtime->hasPCOV()) {
- $driverNode->setAttribute('name', 'pcov');
- $driverNode->setAttribute('version', phpversion('pcov'));
+ $xmlWriter->writeAttribute('name', 'pcov');
+ $xmlWriter->writeAttribute('version', phpversion('pcov'));
}
+ $xmlWriter->endElement();
- $this->contextNode->setAttribute('time', $buildDate->format('D M j G:i:s T Y'));
-
- $this->contextNode->setAttribute('phpunit', $phpUnitVersion);
- $this->contextNode->setAttribute('coverage', $coverageVersion);
- }
-
- private function nodeByName(string $name): DOMElement
- {
- $node = $this->contextNode->appendChild(
- $this->contextNode->ownerDocument->createElementNS(
- Facade::XML_NAMESPACE,
- $name,
- ),
- );
-
- assert($node instanceof DOMElement);
-
- return $node;
+ $xmlWriter->endElement();
}
}
diff --git a/src/Report/Xml/Coverage.php b/src/Report/Xml/Coverage.php
index 9462780be..3038eb143 100644
--- a/src/Report/Xml/Coverage.php
+++ b/src/Report/Xml/Coverage.php
@@ -9,7 +9,6 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use DOMElement;
use XMLWriter;
/**
@@ -17,20 +16,21 @@
*/
final class Coverage
{
- private readonly DOMElement $contextNode;
+ private readonly XMLWriter $xmlWriter;
private readonly string $line;
- public function __construct(DOMElement $context, string $line)
- {
- $this->contextNode = $context;
- $this->line = $line;
+ public function __construct(
+ XMLWriter $xmlWriter,
+ string $line
+ ) {
+ $this->xmlWriter = $xmlWriter;
+ $this->line = $line;
}
public function finalize(array $tests): void
{
- $writer = new XMLWriter;
- $writer->openMemory();
- $writer->startElementNs(null, $this->contextNode->nodeName, Facade::XML_NAMESPACE);
+ $writer = $this->xmlWriter;
+ $writer->startElement('line');
$writer->writeAttribute('nr', $this->line);
foreach ($tests as $test) {
@@ -39,13 +39,5 @@ public function finalize(array $tests): void
$writer->endElement();
}
$writer->endElement();
-
- $fragment = $this->contextNode->ownerDocument->createDocumentFragment();
- $fragment->appendXML($writer->outputMemory());
-
- $this->contextNode->parentNode->replaceChild(
- $fragment,
- $this->contextNode,
- );
}
}
diff --git a/src/Report/Xml/Facade.php b/src/Report/Xml/Facade.php
index c3767088e..cba61f0c2 100644
--- a/src/Report/Xml/Facade.php
+++ b/src/Report/Xml/Facade.php
@@ -21,7 +21,6 @@
use function strlen;
use function substr;
use DateTimeImmutable;
-use DOMDocument;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Data\ProcessedClassType;
use SebastianBergmann\CodeCoverage\Data\ProcessedFunctionType;
@@ -31,11 +30,11 @@
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
use SebastianBergmann\CodeCoverage\PathExistsButIsNotDirectoryException;
use SebastianBergmann\CodeCoverage\Util\Filesystem;
-use SebastianBergmann\CodeCoverage\Util\Xml;
use SebastianBergmann\CodeCoverage\Version;
use SebastianBergmann\CodeCoverage\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\XmlException;
use SebastianBergmann\Environment\Runtime;
+use XMLWriter;
/**
* @phpstan-import-type TestType from CodeCoverage
@@ -66,15 +65,21 @@ public function process(CodeCoverage $coverage, string $target): void
$report = $coverage->getReport();
+ $writer = new XMLWriter;
+ $writer->openUri($this->targetFilePath('index'));
+ $writer->setIndent(true);
+ $writer->setIndentString(' ');
$this->project = new Project(
+ $writer,
$coverage->getReport()->name(),
);
$this->setBuildInformation();
+
+ $this->project->startProject();
$this->processTests($coverage->getTests());
$this->processDirectory($report, $this->project);
-
- $this->saveDocument($this->project->asDom(), 'index');
+ $this->project->finalize();
}
private function setBuildInformation(): void
@@ -119,7 +124,10 @@ private function processDirectory(DirectoryNode $directory, Node $context): void
$directoryName = '/';
}
- $directoryObject = $context->addDirectory($directoryName);
+ $writer = $this->project->getWriter();
+ $writer->startElement('directory');
+ $writer->writeAttribute('name', $directoryName);
+ $directoryObject = $context->addDirectory();
$this->setTotals($directory, $directoryObject->totals());
@@ -130,6 +138,7 @@ private function processDirectory(DirectoryNode $directory, Node $context): void
foreach ($directory->files() as $node) {
$this->processFile($node, $directoryObject);
}
+ $writer->endElement();
}
/**
@@ -137,19 +146,26 @@ private function processDirectory(DirectoryNode $directory, Node $context): void
*/
private function processFile(FileNode $file, Directory $context): void
{
- $fileObject = $context->addFile(
- $file->name(),
- $file->id() . '.xml',
- );
+ $context->getWriter()->startElement('file');
+ $context->getWriter()->writeAttribute('name', $file->name());
+ $context->getWriter()->writeAttribute('href', $file->id() . '.xml');
+
+ $fileObject = $context->addFile();
$this->setTotals($file, $fileObject->totals());
+ $context->getWriter()->endElement();
+
$path = substr(
$file->pathAsString(),
strlen($this->project->projectSourceDirectory()),
);
- $fileReport = new Report($path);
+ $writer = new XMLWriter;
+ $writer->openUri($this->targetFilePath($file->id()));
+ $writer->setIndent(true);
+ $writer->setIndentString(' ');
+ $fileReport = new Report($writer, $path);
$this->setTotals($file, $fileReport->totals());
@@ -161,6 +177,8 @@ private function processFile(FileNode $file, Directory $context): void
$this->processFunction($function, $fileReport);
}
+ $fileReport->getWriter()->startElement('coverage');
+
foreach ($file->lineCoverageData() as $line => $tests) {
if (!is_array($tests) || count($tests) === 0) {
continue;
@@ -169,17 +187,20 @@ private function processFile(FileNode $file, Directory $context): void
$coverage = $fileReport->lineCoverage((string) $line);
$coverage->finalize($tests);
}
+ $fileReport->getWriter()->endElement();
$fileReport->source()->setSourceCode(
file_get_contents($file->pathAsString()),
);
- $this->saveDocument($fileReport->asDom(), $file->id());
+ $fileReport->finalize();
}
private function processUnit(ProcessedClassType|ProcessedTraitType $unit, Report $report): void
{
if ($unit instanceof ProcessedClassType) {
+ $report->getWriter()->startElement('class');
+
$unitObject = $report->classObject(
$unit->className,
$unit->namespace,
@@ -189,6 +210,8 @@ private function processUnit(ProcessedClassType|ProcessedTraitType $unit, Report
(float) $unit->crap,
);
} else {
+ $report->getWriter()->startElement('trait');
+
$unitObject = $report->traitObject(
$unit->traitName,
$unit->namespace,
@@ -200,6 +223,8 @@ private function processUnit(ProcessedClassType|ProcessedTraitType $unit, Report
}
foreach ($unit->methods as $method) {
+ $report->getWriter()->startElement('method');
+
$unitObject->addMethod(
$method->methodName,
$method->signature,
@@ -210,11 +235,17 @@ private function processUnit(ProcessedClassType|ProcessedTraitType $unit, Report
(string) $method->coverage,
$method->crap,
);
+
+ $report->getWriter()->endElement();
}
+
+ $report->getWriter()->endElement();
}
private function processFunction(ProcessedFunctionType $function, Report $report): void
{
+ $report->getWriter()->startElement('function');
+
$report->functionObject(
$function->functionName,
$function->signature,
@@ -225,6 +256,8 @@ private function processFunction(ProcessedFunctionType $function, Report $report
(string) $function->coverage,
$function->crap,
);
+
+ $report->getWriter()->endElement();
}
/**
@@ -232,15 +265,21 @@ private function processFunction(ProcessedFunctionType $function, Report $report
*/
private function processTests(array $tests): void
{
+ $this->project->getWriter()->startElement('tests');
+
$testsObject = $this->project->tests();
foreach ($tests as $test => $result) {
$testsObject->addTest($test, $result);
}
+
+ $this->project->getWriter()->endElement();
}
private function setTotals(AbstractNode $node, Totals $totals): void
{
+ $totals->getWriter()->startElement('totals');
+
$loc = $node->linesOfCode();
$totals->setNumLines(
@@ -251,6 +290,16 @@ private function setTotals(AbstractNode $node, Totals $totals): void
$node->numberOfExecutedLines(),
);
+ $totals->setNumMethods(
+ $node->numberOfMethods(),
+ $node->numberOfTestedMethods(),
+ );
+
+ $totals->setNumFunctions(
+ $node->numberOfFunctions(),
+ $node->numberOfTestedFunctions(),
+ );
+
$totals->setNumClasses(
$node->numberOfClasses(),
$node->numberOfTestedClasses(),
@@ -261,15 +310,7 @@ private function setTotals(AbstractNode $node, Totals $totals): void
$node->numberOfTestedTraits(),
);
- $totals->setNumMethods(
- $node->numberOfMethods(),
- $node->numberOfTestedMethods(),
- );
-
- $totals->setNumFunctions(
- $node->numberOfFunctions(),
- $node->numberOfTestedFunctions(),
- );
+ $totals->getWriter()->endElement();
}
private function targetDirectory(): string
@@ -285,12 +326,4 @@ private function targetFilePath(string $name): string
return $filename;
}
-
- /**
- * @throws XmlException
- */
- private function saveDocument(DOMDocument $document, string $name): void
- {
- Filesystem::write($this->targetFilePath($name), Xml::asString($document));
- }
}
diff --git a/src/Report/Xml/File.php b/src/Report/Xml/File.php
index e6dd5c4ba..2d35582a8 100644
--- a/src/Report/Xml/File.php
+++ b/src/Report/Xml/File.php
@@ -9,66 +9,32 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
-use DOMDocument;
-use DOMElement;
-use DOMNode;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
class File
{
- protected readonly DOMDocument $dom;
- private readonly DOMElement $contextNode;
- private ?DOMNode $lineCoverage = null;
+ protected XMLWriter $xmlWriter;
- public function __construct(DOMElement $context)
+ public function __construct(XMLWriter $xmlWriter)
{
- $this->dom = $context->ownerDocument;
- $this->contextNode = $context;
+ $this->xmlWriter = $xmlWriter;
}
- public function totals(): Totals
+ public function getWriter(): XMLWriter
{
- $totalsContainer = $this->contextNode->appendChild(
- $this->dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'totals',
- ),
- );
-
- assert($totalsContainer instanceof DOMElement);
-
- return new Totals($totalsContainer);
+ return $this->xmlWriter;
}
- public function lineCoverage(string $line): Coverage
+ public function totals(): Totals
{
- if ($this->lineCoverage === null) {
- $this->lineCoverage = $this->contextNode->appendChild(
- $this->dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'coverage',
- ),
- );
- }
- assert($this->lineCoverage instanceof DOMElement);
-
- $lineNode = $this->lineCoverage->appendChild(
- $this->dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'line',
- ),
- );
-
- assert($lineNode instanceof DOMElement);
-
- return new Coverage($lineNode, $line);
+ return new Totals($this->xmlWriter);
}
- protected function contextNode(): DOMElement
+ public function lineCoverage(string $line): Coverage
{
- return $this->contextNode;
+ return new Coverage($this->xmlWriter, $line);
}
}
diff --git a/src/Report/Xml/Method.php b/src/Report/Xml/Method.php
index 1b5bdb28f..965ad5259 100644
--- a/src/Report/Xml/Method.php
+++ b/src/Report/Xml/Method.php
@@ -9,17 +9,17 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use DOMElement;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Method
{
- private DOMElement $contextNode;
+ private XMLWriter $xmlWriter;
public function __construct(
- DOMElement $context,
+ XMLWriter $xmlWriter,
string $name,
string $signature,
string $start,
@@ -29,21 +29,21 @@ public function __construct(
string $coverage,
string $crap
) {
- $this->contextNode = $context;
+ $this->xmlWriter = $xmlWriter;
- $this->contextNode->setAttribute('name', $name);
- $this->contextNode->setAttribute('signature', $signature);
+ $this->xmlWriter->writeAttribute('name', $name);
+ $this->xmlWriter->writeAttribute('signature', $signature);
- $this->contextNode->setAttribute('start', $start);
+ $this->xmlWriter->writeAttribute('start', $start);
if ($end !== null) {
- $this->contextNode->setAttribute('end', $end);
+ $this->xmlWriter->writeAttribute('end', $end);
}
- $this->contextNode->setAttribute('crap', $crap);
+ $this->xmlWriter->writeAttribute('crap', $crap);
- $this->contextNode->setAttribute('executable', $executable);
- $this->contextNode->setAttribute('executed', $executed);
- $this->contextNode->setAttribute('coverage', $coverage);
+ $this->xmlWriter->writeAttribute('executable', $executable);
+ $this->xmlWriter->writeAttribute('executed', $executed);
+ $this->xmlWriter->writeAttribute('coverage', $coverage);
}
}
diff --git a/src/Report/Xml/Node.php b/src/Report/Xml/Node.php
index 86fe70df4..36b75bcfe 100644
--- a/src/Report/Xml/Node.php
+++ b/src/Report/Xml/Node.php
@@ -9,71 +9,37 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
-use DOMDocument;
-use DOMElement;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
abstract class Node
{
- protected readonly DOMDocument $dom;
- private readonly DOMElement $contextNode;
+ protected readonly XMLWriter $xmlWriter;
- public function __construct(DOMElement $context)
+ public function __construct(XMLWriter $xmlWriter)
{
- $this->dom = $context->ownerDocument;
- $this->contextNode = $context;
+ $this->xmlWriter = $xmlWriter;
}
public function totals(): Totals
{
- $totalsContainer = $this->contextNode()->firstChild;
-
- if ($totalsContainer === null) {
- $totalsContainer = $this->contextNode()->appendChild(
- $this->dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'totals',
- ),
- );
- }
-
- assert($totalsContainer instanceof DOMElement);
-
- return new Totals($totalsContainer);
+ return new Totals($this->xmlWriter);
}
- public function addDirectory(string $name): Directory
+ public function addDirectory(): Directory
{
- $dirNode = $this->dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'directory',
- );
-
- $dirNode->setAttribute('name', $name);
- $this->contextNode()->appendChild($dirNode);
-
- return new Directory($dirNode);
+ return new Directory($this->xmlWriter);
}
- public function addFile(string $name, string $href): File
+ public function addFile(): File
{
- $fileNode = $this->dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'file',
- );
-
- $fileNode->setAttribute('name', $name);
- $fileNode->setAttribute('href', $href);
- $this->contextNode()->appendChild($fileNode);
-
- return new File($fileNode);
+ return new File($this->xmlWriter);
}
- protected function contextNode(): DOMElement
+ public function getWriter(): XMLWriter
{
- return $this->contextNode;
+ return $this->xmlWriter;
}
}
diff --git a/src/Report/Xml/Project.php b/src/Report/Xml/Project.php
index c81a6a933..750908c75 100644
--- a/src/Report/Xml/Project.php
+++ b/src/Report/Xml/Project.php
@@ -9,11 +9,9 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
use DateTimeImmutable;
-use DOMDocument;
-use DOMElement;
use SebastianBergmann\Environment\Runtime;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@@ -22,19 +20,16 @@ final class Project extends Node
{
private readonly string $directory;
- public function __construct(string $directory)
+ public function __construct(XMLWriter $xmlWriter, string $directory)
{
- $dom = new DOMDocument;
- $dom->loadXML('');
+ $this->directory = $directory;
- parent::__construct(
- $dom->getElementsByTagNameNS(
- Facade::XML_NAMESPACE,
- 'project',
- )->item(0),
- );
+ parent::__construct($xmlWriter);
- $this->directory = $directory;
+ $this->xmlWriter->startDocument();
+
+ $this->xmlWriter->startElement('phpunit');
+ $this->xmlWriter->writeAttribute('xmlns', Facade::XML_NAMESPACE);
}
public function projectSourceDirectory(): string
@@ -48,15 +43,8 @@ public function buildInformation(
string $phpUnitVersion,
string $coverageVersion
): void {
- $buildNode = $this->dom->getElementsByTagNameNS(
- Facade::XML_NAMESPACE,
- 'build',
- )->item(0);
-
- assert($buildNode instanceof DOMElement);
-
new BuildInformation(
- $buildNode,
+ $this->xmlWriter,
$runtime,
$buildDate,
$phpUnitVersion,
@@ -66,22 +54,24 @@ public function buildInformation(
public function tests(): Tests
{
- $testsNode = $this->contextNode()->appendChild(
- $this->dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'tests',
- ),
- );
-
- assert($testsNode instanceof DOMElement);
+ return new Tests($this->xmlWriter);
+ }
- return new Tests($testsNode);
+ public function getWriter(): XMLWriter
+ {
+ return $this->xmlWriter;
}
- public function asDom(): DOMDocument
+ public function startProject(): void
{
- $this->contextNode()->setAttribute('source', $this->directory);
+ $this->xmlWriter->startElement('project');
+ $this->xmlWriter->writeAttribute('source', $this->directory);
+ }
- return $this->dom;
+ public function finalize(): void
+ {
+ $this->xmlWriter->endElement();
+ $this->xmlWriter->endDocument();
+ $this->xmlWriter->flush();
}
}
diff --git a/src/Report/Xml/Report.php b/src/Report/Xml/Report.php
index ee9b401de..8cdc71e44 100644
--- a/src/Report/Xml/Report.php
+++ b/src/Report/Xml/Report.php
@@ -9,11 +9,10 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
use function basename;
use function dirname;
use DOMDocument;
-use DOMElement;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@@ -22,8 +21,9 @@ final class Report extends File
{
private readonly string $name;
- public function __construct(string $name)
+ public function __construct(XMLWriter $xmlWriter, string $name)
{
+ /*
$dom = new DOMDocument;
$dom->loadXML('');
@@ -31,18 +31,26 @@ public function __construct(string $name)
Facade::XML_NAMESPACE,
'file',
)->item(0);
-
- parent::__construct($contextNode);
+*/
+ parent::__construct($xmlWriter);
$this->name = $name;
+
+ $xmlWriter->startDocument();
+ $xmlWriter->startElement('phpunit');
+ $xmlWriter->writeAttribute('xmlns', Facade::XML_NAMESPACE);
+ $xmlWriter->startElement('file');
+ $xmlWriter->writeAttribute('name', basename($this->name));
+ $xmlWriter->writeAttribute('path', dirname($this->name));
}
- public function asDom(): DOMDocument
+ public function finalize(): void
{
- $this->contextNode()->setAttribute('name', basename($this->name));
- $this->contextNode()->setAttribute('path', dirname($this->name));
+ $this->xmlWriter->endElement();
+ $this->xmlWriter->endElement();
- return $this->dom;
+ $this->xmlWriter->endDocument();
+ $this->xmlWriter->flush();
}
public function functionObject(
@@ -55,17 +63,8 @@ public function functionObject(
string $coverage,
string $crap
): void {
- $node = $this->contextNode()->appendChild(
- $this->dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'function',
- ),
- );
-
- assert($node instanceof DOMElement);
-
new Method(
- $node,
+ $this->xmlWriter,
$name,
$signature,
$start,
@@ -85,16 +84,7 @@ public function classObject(
int $executed,
float $crap
): Unit {
- $node = $this->contextNode()->appendChild(
- $this->dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'class',
- ),
- );
-
- assert($node instanceof DOMElement);
-
- return new Unit($node, $name, $namespace, $start, $executable, $executed, $crap);
+ return new Unit($this->xmlWriter, $name, $namespace, $start, $executable, $executed, $crap);
}
public function traitObject(
@@ -105,29 +95,11 @@ public function traitObject(
int $executed,
float $crap
): Unit {
- $node = $this->contextNode()->appendChild(
- $this->dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'trait',
- ),
- );
-
- assert($node instanceof DOMElement);
-
- return new Unit($node, $name, $namespace, $start, $executable, $executed, $crap);
+ return new Unit($this->xmlWriter, $name, $namespace, $start, $executable, $executed, $crap);
}
public function source(): Source
{
- $source = $this->contextNode()->appendChild(
- $this->dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'source',
- ),
- );
-
- assert($source instanceof DOMElement);
-
- return new Source($source);
+ return new Source($this->xmlWriter);
}
}
diff --git a/src/Report/Xml/Source.php b/src/Report/Xml/Source.php
index 698a71b6d..e82b2c382 100644
--- a/src/Report/Xml/Source.php
+++ b/src/Report/Xml/Source.php
@@ -9,33 +9,26 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use DOMElement;
use TheSeer\Tokenizer\NamespaceUri;
use TheSeer\Tokenizer\Tokenizer;
use TheSeer\Tokenizer\XMLSerializer;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Source
{
- private DOMElement $context;
+ private XMLWriter $xmlWriter;
- public function __construct(DOMElement $context)
+ public function __construct(XMLWriter $xmlWriter)
{
- $this->context = $context;
+ $this->xmlWriter = $xmlWriter;
}
public function setSourceCode(string $source): void
{
- $context = $this->context;
-
$tokens = (new Tokenizer)->parse($source);
- $srcDom = (new XMLSerializer(new NamespaceUri(Facade::XML_NAMESPACE)))->toDom($tokens);
-
- $context->parentNode->replaceChild(
- $context->ownerDocument->importNode($srcDom->documentElement, true),
- $context,
- );
+ (new XMLSerializer(new NamespaceUri(Facade::XML_NAMESPACE)))->appendToWriter($this->xmlWriter, $tokens);
}
}
diff --git a/src/Report/Xml/Tests.php b/src/Report/Xml/Tests.php
index 1760fdfa5..3fea6f4d3 100644
--- a/src/Report/Xml/Tests.php
+++ b/src/Report/Xml/Tests.php
@@ -9,9 +9,8 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
-use DOMElement;
use SebastianBergmann\CodeCoverage\CodeCoverage;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@@ -20,11 +19,11 @@
*/
final readonly class Tests
{
- private DOMElement $contextNode;
+ private readonly XMLWriter $xmlWriter;
- public function __construct(DOMElement $context)
+ public function __construct(XMLWriter $xmlWriter)
{
- $this->contextNode = $context;
+ $this->xmlWriter = $xmlWriter;
}
/**
@@ -32,17 +31,12 @@ public function __construct(DOMElement $context)
*/
public function addTest(string $test, array $result): void
{
- $node = $this->contextNode->appendChild(
- $this->contextNode->ownerDocument->createElementNS(
- Facade::XML_NAMESPACE,
- 'test',
- ),
- );
+ $this->xmlWriter->startElement('test');
- assert($node instanceof DOMElement);
+ $this->xmlWriter->writeAttribute('name', $test);
+ $this->xmlWriter->writeAttribute('size', $result['size']);
+ $this->xmlWriter->writeAttribute('status', $result['status']);
- $node->setAttribute('name', $test);
- $node->setAttribute('size', $result['size']);
- $node->setAttribute('status', $result['status']);
+ $this->xmlWriter->endElement();
}
}
diff --git a/src/Report/Xml/Totals.php b/src/Report/Xml/Totals.php
index 28612f7aa..b0c57ec30 100644
--- a/src/Report/Xml/Totals.php
+++ b/src/Report/Xml/Totals.php
@@ -10,106 +10,86 @@
namespace SebastianBergmann\CodeCoverage\Report\Xml;
use function sprintf;
-use DOMElement;
use SebastianBergmann\CodeCoverage\Util\Percentage;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Totals
{
- private DOMElement $linesNode;
- private DOMElement $methodsNode;
- private DOMElement $functionsNode;
- private DOMElement $classesNode;
- private DOMElement $traitsNode;
+ private XMLWriter $xmlWriter;
- public function __construct(DOMElement $container)
+ public function __construct(XMLWriter $xmlWriter)
{
- $dom = $container->ownerDocument;
-
- $this->linesNode = $dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'lines',
- );
-
- $this->methodsNode = $dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'methods',
- );
-
- $this->functionsNode = $dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'functions',
- );
-
- $this->classesNode = $dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'classes',
- );
-
- $this->traitsNode = $dom->createElementNS(
- Facade::XML_NAMESPACE,
- 'traits',
- );
-
- $container->appendChild($this->linesNode);
- $container->appendChild($this->methodsNode);
- $container->appendChild($this->functionsNode);
- $container->appendChild($this->classesNode);
- $container->appendChild($this->traitsNode);
+ $this->xmlWriter = $xmlWriter;
}
public function setNumLines(int $loc, int $cloc, int $ncloc, int $executable, int $executed): void
{
- $this->linesNode->setAttribute('total', (string) $loc);
- $this->linesNode->setAttribute('comments', (string) $cloc);
- $this->linesNode->setAttribute('code', (string) $ncloc);
- $this->linesNode->setAttribute('executable', (string) $executable);
- $this->linesNode->setAttribute('executed', (string) $executed);
- $this->linesNode->setAttribute(
+ $this->xmlWriter->startElement('lines');
+ $this->xmlWriter->writeAttribute('total', (string) $loc);
+ $this->xmlWriter->writeAttribute('comments', (string) $cloc);
+ $this->xmlWriter->writeAttribute('code', (string) $ncloc);
+ $this->xmlWriter->writeAttribute('executable', (string) $executable);
+ $this->xmlWriter->writeAttribute('executed', (string) $executed);
+ $this->xmlWriter->writeAttribute(
'percent',
$executable === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($executed, $executable)->asFloat()),
);
+ $this->xmlWriter->endElement();
}
public function setNumClasses(int $count, int $tested): void
{
- $this->classesNode->setAttribute('count', (string) $count);
- $this->classesNode->setAttribute('tested', (string) $tested);
- $this->classesNode->setAttribute(
+ $this->xmlWriter->startElement('classes');
+ $this->xmlWriter->writeAttribute('count', (string) $count);
+ $this->xmlWriter->writeAttribute('tested', (string) $tested);
+ $this->xmlWriter->writeAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()),
);
+ $this->xmlWriter->endElement();
}
public function setNumTraits(int $count, int $tested): void
{
- $this->traitsNode->setAttribute('count', (string) $count);
- $this->traitsNode->setAttribute('tested', (string) $tested);
- $this->traitsNode->setAttribute(
+ $this->xmlWriter->startElement('traits');
+ $this->xmlWriter->writeAttribute('count', (string) $count);
+ $this->xmlWriter->writeAttribute('tested', (string) $tested);
+ $this->xmlWriter->writeAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()),
);
+ $this->xmlWriter->endElement();
}
public function setNumMethods(int $count, int $tested): void
{
- $this->methodsNode->setAttribute('count', (string) $count);
- $this->methodsNode->setAttribute('tested', (string) $tested);
- $this->methodsNode->setAttribute(
+ $this->xmlWriter->startElement('methods');
+ $this->xmlWriter->writeAttribute('count', (string) $count);
+ $this->xmlWriter->writeAttribute('tested', (string) $tested);
+ $this->xmlWriter->writeAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()),
);
+ $this->xmlWriter->endElement();
}
public function setNumFunctions(int $count, int $tested): void
{
- $this->functionsNode->setAttribute('count', (string) $count);
- $this->functionsNode->setAttribute('tested', (string) $tested);
- $this->functionsNode->setAttribute(
+ $this->xmlWriter->startElement('functions');
+ $this->xmlWriter->writeAttribute('count', (string) $count);
+ $this->xmlWriter->writeAttribute('tested', (string) $tested);
+ $this->xmlWriter->writeAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()),
);
+ $this->xmlWriter->endElement();
+ }
+
+ public function getWriter(): XMLWriter
+ {
+ return $this->xmlWriter;
}
}
diff --git a/src/Report/Xml/Unit.php b/src/Report/Xml/Unit.php
index fa97909c2..bfc5029c4 100644
--- a/src/Report/Xml/Unit.php
+++ b/src/Report/Xml/Unit.php
@@ -9,18 +9,17 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
-use DOMElement;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Unit
{
- private DOMElement $contextNode;
+ private XMLWriter $xmlWriter;
public function __construct(
- DOMElement $context,
+ XMLWriter $xmlWriter,
string $name,
string $namespace,
int $start,
@@ -28,23 +27,17 @@ public function __construct(
int $executed,
float $crap
) {
- $this->contextNode = $context;
+ $this->xmlWriter = $xmlWriter;
- $this->contextNode->setAttribute('name', $name);
- $this->contextNode->setAttribute('start', (string) $start);
- $this->contextNode->setAttribute('executable', (string) $executable);
- $this->contextNode->setAttribute('executed', (string) $executed);
- $this->contextNode->setAttribute('crap', (string) $crap);
+ $this->xmlWriter->writeAttribute('name', $name);
+ $this->xmlWriter->writeAttribute('start', (string) $start);
+ $this->xmlWriter->writeAttribute('executable', (string) $executable);
+ $this->xmlWriter->writeAttribute('executed', (string) $executed);
+ $this->xmlWriter->writeAttribute('crap', (string) $crap);
- $node = $this->contextNode->appendChild(
- $this->contextNode->ownerDocument->createElementNS(
- Facade::XML_NAMESPACE,
- 'namespace',
- ),
- );
- assert($node instanceof DOMElement);
-
- $node->setAttribute('name', $namespace);
+ $this->xmlWriter->startElement('namespace');
+ $this->xmlWriter->writeAttribute('name', $namespace);
+ $this->xmlWriter->endElement();
}
public function addMethod(
@@ -57,17 +50,8 @@ public function addMethod(
string $coverage,
string $crap
): void {
- $node = $this->contextNode->appendChild(
- $this->contextNode->ownerDocument->createElementNS(
- Facade::XML_NAMESPACE,
- 'method',
- ),
- );
-
- assert($node instanceof DOMElement);
-
new Method(
- $node,
+ $this->xmlWriter,
$name,
$signature,
$start,
diff --git a/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml b/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml
index b49cdf8ed..fa5cab570 100644
--- a/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml
+++ b/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml
@@ -35,7 +35,7 @@
-
+
<?php
diff --git a/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.xml b/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.xml
index 95d121dc8..5f488ee82 100644
--- a/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.xml
+++ b/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.xml
@@ -38,7 +38,7 @@
-
+
<?php
diff --git a/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/source_with_ignore.php.xml b/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/source_with_ignore.php.xml
index b862f3d1e..89c105012 100644
--- a/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/source_with_ignore.php.xml
+++ b/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/source_with_ignore.php.xml
@@ -22,7 +22,7 @@
-
+
<?php