Completed
Push — master ( 064bf6...127bad )
by Matthias
02:16
created

JsonGenerator::getProjectMetrics()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 2
eloc 5
nc 2
nop 0
1
<?php
2
3
namespace Cauditor\Analyzers\PDepend;
4
5
use PDepend\Metrics\Analyzer as PDependAnalyzer;
6
use PDepend\Metrics\AnalyzerNodeAware;
7
use PDepend\Metrics\AnalyzerProjectAware;
8
use PDepend\Report\CodeAwareGenerator;
9
use PDepend\Report\FileAwareGenerator;
10
use PDepend\Source\AST\AbstractASTClassOrInterface;
11
use PDepend\Source\AST\ASTArtifact;
12
use PDepend\Source\AST\ASTArtifactList;
13
use PDepend\Source\AST\ASTClass;
14
use PDepend\Source\AST\ASTFunction;
15
use PDepend\Source\AST\ASTInterface;
16
use PDepend\Source\AST\ASTMethod;
17
use PDepend\Source\AST\ASTNamespace;
18
use PDepend\Source\AST\ASTTrait;
19
use PDepend\Source\ASTVisitor\AbstractASTVisitor;
20
21
class JsonGenerator extends AbstractASTVisitor implements CodeAwareGenerator, FileAwareGenerator
22
{
23
    /**
24
     * @var string
25
     */
26
    protected $logFile;
27
28
    /**
29
     * @var AnalyzerProjectAware[]
30
     */
31
    protected $projectAnalyzers = array();
32
33
    /**
34
     * @var AnalyzerNodeAware[]
35
     */
36
    protected $nodeAnalyzers = array();
37
38
    /**
39
     * @var ASTArtifactList
40
     */
41
    protected $artifacts;
42
43
    /**
44
     * @var array
45
     */
46
    protected $data = array();
47
48
    /**
49
     * Project-wide totals for all node analyzers.
50
     *
51
     * @var array
52
     */
53
    protected $projectMetrics = array();
54
55
    /**
56
     * List of metrics to include and what to map them to.
57
     *
58
     * @var string[]
59
     */
60
    protected $metrics = array(
61
        'eloc' => 'loc',
62
        'noc' => 'noc',
63
        'nom' => 'nom',
64
        'ca' => 'ca',
65
        'ce' => 'ce',
66
        'i' => 'i',
67
        'dit' => 'dit',
68
        'ccn2' => 'ccn',
69
        'npath' => 'npath',
70
        'he' => 'he',
71
        'hi' => 'hi',
72
        'mi' => 'mi',
73
    );
74
75
    /**
76
     * {@inheritdoc}
77
     */
78
    public function log(PDependAnalyzer $analyzer)
79
    {
80
        $accept = true;
81
82
        if ($analyzer instanceof AnalyzerProjectAware) {
83
            $this->projectAnalyzers[] = $analyzer;
84
            // don't return just yet, it may also be a node analyzer ;)
85
            $accept = true;
86
        }
87
88
        if ($analyzer instanceof AnalyzerNodeAware) {
89
            $this->nodeAnalyzers[] = $analyzer;
90
            $accept = true;
91
        }
92
93
        return $accept;
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    public function close()
100
    {
101
        foreach ($this->artifacts as $artifact) {
102
            $artifact->accept($this);
103
        }
104
105
        $data = $this->getProjectMetrics() + $this->projectMetrics;
106
        $data = $this->addInstability($data);
107
        $data += array('children' => $this->data);
108
109
        $json = json_encode($data);
110
        file_put_contents($this->logFile, $json);
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116
    public function getAcceptedAnalyzers()
117
    {
118
        return array(
119
            'pdepend.analyzer.cyclomatic_complexity',
120
            'pdepend.analyzer.node_loc',
121
            'pdepend.analyzer.npath_complexity',
122
            'pdepend.analyzer.inheritance',
123
            'pdepend.analyzer.node_count',
124
            'pdepend.analyzer.coupling',
125
            'pdepend.analyzer.halstead',
126
            'pdepend.analyzer.maintainability',
127
        );
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function setLogFile($logFile)
134
    {
135
        $this->logFile = $logFile;
136
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141
    public function setArtifacts(ASTArtifactList $artifacts)
142
    {
143
        $this->artifacts = $artifacts;
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function visitNamespace(ASTNamespace $node)
150
    {
151
        $data = array();
152
        $data['name'] = $node->getName();
153
        $data += $this->getNodeMetrics($node);
154
        $data['children'] = array();
155
156
        $this->data[] = $data;
157
158
        // process classes, traits, interfaces in this namespace
159
        foreach ($node->getTypes() as $type) {
160
            $type->accept($this);
161
        }
162
163
        // process functions in this namespace
164
        foreach ($node->getFunctions() as $function) {
165
            $function->accept($this);
166
        }
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     */
172
    public function visitClass(ASTClass $node)
173
    {
174
        $this->visitType($node);
175
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180
    public function visitTrait(ASTTrait $node)
181
    {
182
        $this->visitType($node);
183
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188
    public function visitInterface(ASTInterface $node)
189
    {
190
        // skip interfaces, they have no code...
191
    }
192
193
    /**
194
     * @param AbstractASTClassOrInterface $node
195
     */
196
    protected function visitType(AbstractASTClassOrInterface $node)
197
    {
198
        $data = array();
199
        $data['name'] = $node->getName();
200
        $data += $this->getNodeMetrics($node);
201
        $data['children'] = array();
202
203
        $namespace = count($this->data) - 1;
204
        $this->data[$namespace]['children'][] = $data;
205
206
        // process methods in this class/trait/interface
207
        foreach ($node->getMethods() as $method) {
208
            $method->accept($this);
209
        }
210
    }
211
212
    /**
213
     * {@inheritdoc}
214
     */
215
    public function visitFunction(ASTFunction $node)
216
    {
217
        $data = array();
218
        $data['name'] = $node->getName();
219
        $data += $this->getNodeMetrics($node);
220
221
        $namespace = count($this->data) - 1;
222
        $this->data[$namespace]['children'][] = $data;
223
    }
224
225
    /**
226
     * {@inheritdoc}
227
     */
228
    public function visitMethod(ASTMethod $node)
229
    {
230
        $data = array();
231
        $data['name'] = $node->getName();
232
        $data += $this->getNodeMetrics($node);
233
234
        $namespace = count($this->data) - 1;
235
        $class = count($this->data[$namespace]['children']) - 1;
236
        $this->data[$namespace]['children'][$class]['children'][] = $data;
237
    }
238
239
    /**
240
     * @return int[]
241
     */
242
    protected function getProjectMetrics()
243
    {
244
        $metrics = array();
245
        foreach ($this->projectAnalyzers as $analyzer) {
246
            $metrics += $analyzer->getProjectMetrics();
247
        }
248
249
        return $this->normalizeMetrics($metrics);
250
    }
251
252
    /**
253
     * @param ASTArtifact $node
254
     *
255
     * @return int[]
256
     */
257
    protected function getNodeMetrics(ASTArtifact $node)
258
    {
259
        $metrics = array();
260
        foreach ($this->nodeAnalyzers as $analyzer) {
261
            $metrics += $analyzer->getNodeMetrics($node);
262
        }
263
264
        $metrics = $this->normalizeMetrics($metrics);
265
266
        // add node metric to project-wide totals
267
        foreach ($metrics as $metric => $value) {
268
            if (!isset($this->projectMetrics[$metric])) {
269
                $this->projectMetrics[$metric] = 0;
270
            }
271
272
            $this->projectMetrics[$metric] += $value;
273
        }
274
275
        return $metrics;
276
    }
277
278
    /**
279
     * @param array $metrics
280
     *
281
     * @return array
282
     */
283
    protected function normalizeMetrics(array $metrics)
284
    {
285
        $result = array();
286
287
        foreach ($this->metrics as $metric => $replacement) {
288
            if (isset($metrics[$metric])) {
289
                $result[$replacement] = $metrics[$metric];
290
            }
291
        }
292
293
        foreach ($result as $metric => $value) {
294
            if (is_float($value)) {
295
                $result[$metric] = number_format($value, 2, '.', '');
296
            }
297
298
            // cast to float so the JSON value is numeric (int will be
299
            // encapsulated in quotes...)
300
            $result[$metric] = (float) $result[$metric];
301
        }
302
303
        $result = $this->addInstability($result);
304
305
        return $result;
306
    }
307
308
    /**
309
     * pdepend.analyzer.dependency also calculates instability, but not on a
310
     * per-class level, and defauling to 0 instead of 1.
311
     *
312
     * @param array $metrics
313
     *
314
     * @return array
315
     */
316
    protected function addInstability(array $metrics)
317
    {
318
        if (isset($metrics['ca']) && isset($metrics['ce'])) {
319
            $metrics['i'] = (float) number_format($metrics['ce'] / (($metrics['ce'] + $metrics['ca']) ?: 1), 2);
320
        }
321
322
        return $metrics;
323
    }
324
}
325