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; 12 13use SebastianBergmann\CodeCoverage\CodeCoverage; 14use SebastianBergmann\CodeCoverage\Node\File; 15use SebastianBergmann\CodeCoverage\InvalidArgumentException; 16 17class Crap4j 18{ 19 /** 20 * @var int 21 */ 22 private $threshold; 23 24 /** 25 * @param int $threshold 26 */ 27 public function __construct($threshold = 30) 28 { 29 if (!is_int($threshold)) { 30 throw InvalidArgumentException::create( 31 1, 32 'integer' 33 ); 34 } 35 36 $this->threshold = $threshold; 37 } 38 39 /** 40 * @param CodeCoverage $coverage 41 * @param string $target 42 * @param string $name 43 * 44 * @return string 45 */ 46 public function process(CodeCoverage $coverage, $target = null, $name = null) 47 { 48 $document = new \DOMDocument('1.0', 'UTF-8'); 49 $document->formatOutput = true; 50 51 $root = $document->createElement('crap_result'); 52 $document->appendChild($root); 53 54 $project = $document->createElement('project', is_string($name) ? $name : ''); 55 $root->appendChild($project); 56 $root->appendChild($document->createElement('timestamp', date('Y-m-d H:i:s', (int) $_SERVER['REQUEST_TIME']))); 57 58 $stats = $document->createElement('stats'); 59 $methodsNode = $document->createElement('methods'); 60 61 $report = $coverage->getReport(); 62 unset($coverage); 63 64 $fullMethodCount = 0; 65 $fullCrapMethodCount = 0; 66 $fullCrapLoad = 0; 67 $fullCrap = 0; 68 69 foreach ($report as $item) { 70 $namespace = 'global'; 71 72 if (!$item instanceof File) { 73 continue; 74 } 75 76 $file = $document->createElement('file'); 77 $file->setAttribute('name', $item->getPath()); 78 79 $classes = $item->getClassesAndTraits(); 80 81 foreach ($classes as $className => $class) { 82 foreach ($class['methods'] as $methodName => $method) { 83 $crapLoad = $this->getCrapLoad($method['crap'], $method['ccn'], $method['coverage']); 84 85 $fullCrap += $method['crap']; 86 $fullCrapLoad += $crapLoad; 87 $fullMethodCount++; 88 89 if ($method['crap'] >= $this->threshold) { 90 $fullCrapMethodCount++; 91 } 92 93 $methodNode = $document->createElement('method'); 94 95 if (!empty($class['package']['namespace'])) { 96 $namespace = $class['package']['namespace']; 97 } 98 99 $methodNode->appendChild($document->createElement('package', $namespace)); 100 $methodNode->appendChild($document->createElement('className', $className)); 101 $methodNode->appendChild($document->createElement('methodName', $methodName)); 102 $methodNode->appendChild($document->createElement('methodSignature', htmlspecialchars($method['signature']))); 103 $methodNode->appendChild($document->createElement('fullMethod', htmlspecialchars($method['signature']))); 104 $methodNode->appendChild($document->createElement('crap', $this->roundValue($method['crap']))); 105 $methodNode->appendChild($document->createElement('complexity', $method['ccn'])); 106 $methodNode->appendChild($document->createElement('coverage', $this->roundValue($method['coverage']))); 107 $methodNode->appendChild($document->createElement('crapLoad', round($crapLoad))); 108 109 $methodsNode->appendChild($methodNode); 110 } 111 } 112 } 113 114 $stats->appendChild($document->createElement('name', 'Method Crap Stats')); 115 $stats->appendChild($document->createElement('methodCount', $fullMethodCount)); 116 $stats->appendChild($document->createElement('crapMethodCount', $fullCrapMethodCount)); 117 $stats->appendChild($document->createElement('crapLoad', round($fullCrapLoad))); 118 $stats->appendChild($document->createElement('totalCrap', $fullCrap)); 119 120 if ($fullMethodCount > 0) { 121 $crapMethodPercent = $this->roundValue((100 * $fullCrapMethodCount) / $fullMethodCount); 122 } else { 123 $crapMethodPercent = 0; 124 } 125 126 $stats->appendChild($document->createElement('crapMethodPercent', $crapMethodPercent)); 127 128 $root->appendChild($stats); 129 $root->appendChild($methodsNode); 130 131 $buffer = $document->saveXML(); 132 133 if ($target !== null) { 134 if (!is_dir(dirname($target))) { 135 mkdir(dirname($target), 0777, true); 136 } 137 138 file_put_contents($target, $buffer); 139 } 140 141 return $buffer; 142 } 143 144 /** 145 * @param float $crapValue 146 * @param int $cyclomaticComplexity 147 * @param float $coveragePercent 148 * 149 * @return float 150 */ 151 private function getCrapLoad($crapValue, $cyclomaticComplexity, $coveragePercent) 152 { 153 $crapLoad = 0; 154 155 if ($crapValue >= $this->threshold) { 156 $crapLoad += $cyclomaticComplexity * (1.0 - $coveragePercent / 100); 157 $crapLoad += $cyclomaticComplexity / $this->threshold; 158 } 159 160 return $crapLoad; 161 } 162 163 /** 164 * @param float $value 165 * 166 * @return float 167 */ 168 private function roundValue($value) 169 { 170 return round($value, 2); 171 } 172} 173