1<?php 2/* 3 * This file is part of the php-code-coverage package. 4 * 5 * (c) Sebastian Bergmann <sebastian@phpunit.de> 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11namespace SebastianBergmann\CodeCoverage\Report\Xml; 12 13use SebastianBergmann\CodeCoverage\CodeCoverage; 14use SebastianBergmann\CodeCoverage\Node\AbstractNode; 15use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; 16use SebastianBergmann\CodeCoverage\Node\File as FileNode; 17use SebastianBergmann\CodeCoverage\RuntimeException; 18 19class Facade 20{ 21 /** 22 * @var string 23 */ 24 private $target; 25 26 /** 27 * @var Project 28 */ 29 private $project; 30 31 /** 32 * @param CodeCoverage $coverage 33 * @param string $target 34 * 35 * @throws RuntimeException 36 */ 37 public function process(CodeCoverage $coverage, $target) 38 { 39 if (substr($target, -1, 1) != DIRECTORY_SEPARATOR) { 40 $target .= DIRECTORY_SEPARATOR; 41 } 42 43 $this->target = $target; 44 $this->initTargetDirectory($target); 45 46 $report = $coverage->getReport(); 47 48 $this->project = new Project( 49 $coverage->getReport()->getName() 50 ); 51 52 $this->processTests($coverage->getTests()); 53 $this->processDirectory($report, $this->project); 54 55 $index = $this->project->asDom(); 56 $index->formatOutput = true; 57 $index->preserveWhiteSpace = false; 58 $index->save($target . '/index.xml'); 59 } 60 61 /** 62 * @param string $directory 63 */ 64 private function initTargetDirectory($directory) 65 { 66 if (file_exists($directory)) { 67 if (!is_dir($directory)) { 68 throw new RuntimeException( 69 "'$directory' exists but is not a directory." 70 ); 71 } 72 73 if (!is_writable($directory)) { 74 throw new RuntimeException( 75 "'$directory' exists but is not writable." 76 ); 77 } 78 } elseif (!@mkdir($directory, 0777, true)) { 79 throw new RuntimeException( 80 "'$directory' could not be created." 81 ); 82 } 83 } 84 85 private function processDirectory(DirectoryNode $directory, Node $context) 86 { 87 $dirObject = $context->addDirectory($directory->getName()); 88 89 $this->setTotals($directory, $dirObject->getTotals()); 90 91 foreach ($directory->getDirectories() as $node) { 92 $this->processDirectory($node, $dirObject); 93 } 94 95 foreach ($directory->getFiles() as $node) { 96 $this->processFile($node, $dirObject); 97 } 98 } 99 100 private function processFile(FileNode $file, Directory $context) 101 { 102 $fileObject = $context->addFile( 103 $file->getName(), 104 $file->getId() . '.xml' 105 ); 106 107 $this->setTotals($file, $fileObject->getTotals()); 108 109 $fileReport = new Report($file->getName()); 110 111 $this->setTotals($file, $fileReport->getTotals()); 112 113 foreach ($file->getClassesAndTraits() as $unit) { 114 $this->processUnit($unit, $fileReport); 115 } 116 117 foreach ($file->getFunctions() as $function) { 118 $this->processFunction($function, $fileReport); 119 } 120 121 foreach ($file->getCoverageData() as $line => $tests) { 122 if (!is_array($tests) || count($tests) == 0) { 123 continue; 124 } 125 126 $coverage = $fileReport->getLineCoverage($line); 127 128 foreach ($tests as $test) { 129 $coverage->addTest($test); 130 } 131 132 $coverage->finalize(); 133 } 134 135 $this->initTargetDirectory( 136 $this->target . dirname($file->getId()) . '/' 137 ); 138 139 $fileDom = $fileReport->asDom(); 140 $fileDom->formatOutput = true; 141 $fileDom->preserveWhiteSpace = false; 142 $fileDom->save($this->target . $file->getId() . '.xml'); 143 } 144 145 private function processUnit($unit, Report $report) 146 { 147 if (isset($unit['className'])) { 148 $unitObject = $report->getClassObject($unit['className']); 149 } else { 150 $unitObject = $report->getTraitObject($unit['traitName']); 151 } 152 153 $unitObject->setLines( 154 $unit['startLine'], 155 $unit['executableLines'], 156 $unit['executedLines'] 157 ); 158 159 $unitObject->setCrap($unit['crap']); 160 161 $unitObject->setPackage( 162 $unit['package']['fullPackage'], 163 $unit['package']['package'], 164 $unit['package']['subpackage'], 165 $unit['package']['category'] 166 ); 167 168 $unitObject->setNamespace($unit['package']['namespace']); 169 170 foreach ($unit['methods'] as $method) { 171 $methodObject = $unitObject->addMethod($method['methodName']); 172 $methodObject->setSignature($method['signature']); 173 $methodObject->setLines($method['startLine'], $method['endLine']); 174 $methodObject->setCrap($method['crap']); 175 $methodObject->setTotals( 176 $method['executableLines'], 177 $method['executedLines'], 178 $method['coverage'] 179 ); 180 } 181 } 182 183 private function processFunction($function, Report $report) 184 { 185 $functionObject = $report->getFunctionObject($function['functionName']); 186 187 $functionObject->setSignature($function['signature']); 188 $functionObject->setLines($function['startLine']); 189 $functionObject->setCrap($function['crap']); 190 $functionObject->setTotals($function['executableLines'], $function['executedLines'], $function['coverage']); 191 } 192 193 private function processTests(array $tests) 194 { 195 $testsObject = $this->project->getTests(); 196 197 foreach ($tests as $test => $result) { 198 if ($test == 'UNCOVERED_FILES_FROM_WHITELIST') { 199 continue; 200 } 201 202 $testsObject->addTest($test, $result); 203 } 204 } 205 206 private function setTotals(AbstractNode $node, Totals $totals) 207 { 208 $loc = $node->getLinesOfCode(); 209 210 $totals->setNumLines( 211 $loc['loc'], 212 $loc['cloc'], 213 $loc['ncloc'], 214 $node->getNumExecutableLines(), 215 $node->getNumExecutedLines() 216 ); 217 218 $totals->setNumClasses( 219 $node->getNumClasses(), 220 $node->getNumTestedClasses() 221 ); 222 223 $totals->setNumTraits( 224 $node->getNumTraits(), 225 $node->getNumTestedTraits() 226 ); 227 228 $totals->setNumMethods( 229 $node->getNumMethods(), 230 $node->getNumTestedMethods() 231 ); 232 233 $totals->setNumFunctions( 234 $node->getNumFunctions(), 235 $node->getNumTestedFunctions() 236 ); 237 } 238} 239