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