JsonGenerator::visitFunction()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

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