Chris@14: Chris@14: * Chris@14: * For the full copyright and license information, please view the LICENSE Chris@14: * file that was distributed with this source code. Chris@14: */ Chris@14: Chris@14: namespace SebastianBergmann\CodeCoverage\Report; Chris@14: Chris@14: use SebastianBergmann\CodeCoverage\CodeCoverage; Chris@14: use SebastianBergmann\CodeCoverage\InvalidArgumentException; Chris@14: use SebastianBergmann\CodeCoverage\Node\File; Chris@14: use SebastianBergmann\CodeCoverage\RuntimeException; Chris@14: Chris@14: class Crap4j Chris@14: { Chris@14: /** Chris@14: * @var int Chris@14: */ Chris@14: private $threshold; Chris@14: Chris@14: /** Chris@14: * @param int $threshold Chris@14: */ Chris@14: public function __construct($threshold = 30) Chris@14: { Chris@14: if (!\is_int($threshold)) { Chris@14: throw InvalidArgumentException::create( Chris@14: 1, Chris@14: 'integer' Chris@14: ); Chris@14: } Chris@14: Chris@14: $this->threshold = $threshold; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param CodeCoverage $coverage Chris@14: * @param string $target Chris@14: * @param string $name Chris@14: * Chris@14: * @return string Chris@14: * Chris@14: * @throws \SebastianBergmann\CodeCoverage\RuntimeException Chris@14: */ Chris@14: public function process(CodeCoverage $coverage, $target = null, $name = null) Chris@14: { Chris@14: $document = new \DOMDocument('1.0', 'UTF-8'); Chris@14: $document->formatOutput = true; Chris@14: Chris@14: $root = $document->createElement('crap_result'); Chris@14: $document->appendChild($root); Chris@14: Chris@14: $project = $document->createElement('project', \is_string($name) ? $name : ''); Chris@14: $root->appendChild($project); Chris@14: $root->appendChild($document->createElement('timestamp', \date('Y-m-d H:i:s', (int) $_SERVER['REQUEST_TIME']))); Chris@14: Chris@14: $stats = $document->createElement('stats'); Chris@14: $methodsNode = $document->createElement('methods'); Chris@14: Chris@14: $report = $coverage->getReport(); Chris@14: unset($coverage); Chris@14: Chris@14: $fullMethodCount = 0; Chris@14: $fullCrapMethodCount = 0; Chris@14: $fullCrapLoad = 0; Chris@14: $fullCrap = 0; Chris@14: Chris@14: foreach ($report as $item) { Chris@14: $namespace = 'global'; Chris@14: Chris@14: if (!$item instanceof File) { Chris@14: continue; Chris@14: } Chris@14: Chris@14: $file = $document->createElement('file'); Chris@14: $file->setAttribute('name', $item->getPath()); Chris@14: Chris@14: $classes = $item->getClassesAndTraits(); Chris@14: Chris@14: foreach ($classes as $className => $class) { Chris@14: foreach ($class['methods'] as $methodName => $method) { Chris@14: $crapLoad = $this->getCrapLoad($method['crap'], $method['ccn'], $method['coverage']); Chris@14: Chris@14: $fullCrap += $method['crap']; Chris@14: $fullCrapLoad += $crapLoad; Chris@14: $fullMethodCount++; Chris@14: Chris@14: if ($method['crap'] >= $this->threshold) { Chris@14: $fullCrapMethodCount++; Chris@14: } Chris@14: Chris@14: $methodNode = $document->createElement('method'); Chris@14: Chris@14: if (!empty($class['package']['namespace'])) { Chris@14: $namespace = $class['package']['namespace']; Chris@14: } Chris@14: Chris@14: $methodNode->appendChild($document->createElement('package', $namespace)); Chris@14: $methodNode->appendChild($document->createElement('className', $className)); Chris@14: $methodNode->appendChild($document->createElement('methodName', $methodName)); Chris@14: $methodNode->appendChild($document->createElement('methodSignature', \htmlspecialchars($method['signature']))); Chris@14: $methodNode->appendChild($document->createElement('fullMethod', \htmlspecialchars($method['signature']))); Chris@14: $methodNode->appendChild($document->createElement('crap', $this->roundValue($method['crap']))); Chris@14: $methodNode->appendChild($document->createElement('complexity', $method['ccn'])); Chris@14: $methodNode->appendChild($document->createElement('coverage', $this->roundValue($method['coverage']))); Chris@14: $methodNode->appendChild($document->createElement('crapLoad', \round($crapLoad))); Chris@14: Chris@14: $methodsNode->appendChild($methodNode); Chris@14: } Chris@14: } Chris@14: } Chris@14: Chris@14: $stats->appendChild($document->createElement('name', 'Method Crap Stats')); Chris@14: $stats->appendChild($document->createElement('methodCount', $fullMethodCount)); Chris@14: $stats->appendChild($document->createElement('crapMethodCount', $fullCrapMethodCount)); Chris@14: $stats->appendChild($document->createElement('crapLoad', \round($fullCrapLoad))); Chris@14: $stats->appendChild($document->createElement('totalCrap', $fullCrap)); Chris@14: Chris@14: if ($fullMethodCount > 0) { Chris@14: $crapMethodPercent = $this->roundValue((100 * $fullCrapMethodCount) / $fullMethodCount); Chris@14: } else { Chris@14: $crapMethodPercent = 0; Chris@14: } Chris@14: Chris@14: $stats->appendChild($document->createElement('crapMethodPercent', $crapMethodPercent)); Chris@14: Chris@14: $root->appendChild($stats); Chris@14: $root->appendChild($methodsNode); Chris@14: Chris@14: $buffer = $document->saveXML(); Chris@14: Chris@14: if ($target !== null) { Chris@14: if (!\is_dir(\dirname($target))) { Chris@14: \mkdir(\dirname($target), 0777, true); Chris@14: } Chris@14: Chris@14: if (@\file_put_contents($target, $buffer) === false) { Chris@14: throw new RuntimeException( Chris@14: \sprintf( Chris@14: 'Could not write to "%s', Chris@14: $target Chris@14: ) Chris@14: ); Chris@14: } Chris@14: } Chris@14: Chris@14: return $buffer; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param float $crapValue Chris@14: * @param int $cyclomaticComplexity Chris@14: * @param float $coveragePercent Chris@14: * Chris@14: * @return float Chris@14: */ Chris@14: private function getCrapLoad($crapValue, $cyclomaticComplexity, $coveragePercent) Chris@14: { Chris@14: $crapLoad = 0; Chris@14: Chris@14: if ($crapValue >= $this->threshold) { Chris@14: $crapLoad += $cyclomaticComplexity * (1.0 - $coveragePercent / 100); Chris@14: $crapLoad += $cyclomaticComplexity / $this->threshold; Chris@14: } Chris@14: Chris@14: return $crapLoad; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param float $value Chris@14: * Chris@14: * @return float Chris@14: */ Chris@14: private function roundValue($value) Chris@14: { Chris@14: return \round($value, 2); Chris@14: } Chris@14: }