Completed
Push — master ( 8418b3...91ec6e )
by
unknown
13s queued 11s
created

PDepend/Metrics/Analyzer/ClassLevelAnalyzer.php (2 issues)

Check for PhpDoc comments which do parse

Documentation Minor

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * This file is part of PDepend.
4
 *
5
 * PHP Version 5
6
 *
7
 * Copyright (c) 2008-2017 Manuel Pichler <[email protected]>.
8
 * All rights reserved.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions
12
 * are met:
13
 *
14
 *   * Redistributions of source code must retain the above copyright
15
 *     notice, this list of conditions and the following disclaimer.
16
 *
17
 *   * Redistributions in binary form must reproduce the above copyright
18
 *     notice, this list of conditions and the following disclaimer in
19
 *     the documentation and/or other materials provided with the
20
 *     distribution.
21
 *
22
 *   * Neither the name of Manuel Pichler nor the names of his
23
 *     contributors may be used to endorse or promote products derived
24
 *     from this software without specific prior written permission.
25
 *
26
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
32
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
34
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
36
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37
 * POSSIBILITY OF SUCH DAMAGE.
38
 *
39
 * @copyright 2008-2017 Manuel Pichler. All rights reserved.
40
 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
41
 */
42
43
namespace PDepend\Metrics\Analyzer;
44
45
use PDepend\Metrics\AbstractAnalyzer;
46
use PDepend\Metrics\AggregateAnalyzer;
47
use PDepend\Metrics\AnalyzerFilterAware;
48
use PDepend\Metrics\AnalyzerNodeAware;
49
use PDepend\Source\AST\AbstractASTType;
50
use PDepend\Source\AST\ASTArtifact;
51
use PDepend\Source\AST\ASTArtifactList;
52
use PDepend\Source\AST\ASTClass;
53
use PDepend\Source\AST\ASTInterface;
54
use PDepend\Source\AST\ASTMethod;
55
use PDepend\Source\AST\ASTProperty;
56
use PDepend\Source\AST\ASTTrait;
57
58
/**
59
 * Generates some class level based metrics. This analyzer is based on the
60
 * metrics specified in the following document.
61
 *
62
 * http://www.aivosto.com/project/help/pm-oo-misc.html
63
 *
64
 * @copyright 2008-2017 Manuel Pichler. All rights reserved.
65
 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
66
 */
67
class ClassLevelAnalyzer extends AbstractAnalyzer implements AggregateAnalyzer, AnalyzerFilterAware, AnalyzerNodeAware
68
{
69
    /**
70
     * Metrics provided by the analyzer implementation.
71
     */
72
    const M_IMPLEMENTED_INTERFACES       = 'impl',
73
          M_CLASS_INTERFACE_SIZE         = 'cis',
74
          M_CLASS_SIZE                   = 'csz',
75
          M_NUMBER_OF_PUBLIC_METHODS     = 'npm',
76
          M_PROPERTIES                   = 'vars',
77
          M_PROPERTIES_INHERIT           = 'varsi',
78
          M_PROPERTIES_NON_PRIVATE       = 'varsnp',
79
          M_WEIGHTED_METHODS             = 'wmc',
80
          M_WEIGHTED_METHODS_INHERIT     = 'wmci',
81
          M_WEIGHTED_METHODS_NON_PRIVATE = 'wmcnp';
82
83
    /**
84
     * Hash with all calculated node metrics.
85
     *
86
     * <code>
87
     * array(
88
     *     '0375e305-885a-4e91-8b5c-e25bda005438'  =>  array(
89
     *         'loc'    =>  42,
90
     *         'ncloc'  =>  17,
91
     *         'cc'     =>  12
92
     *     ),
93
     *     'e60c22f0-1a63-4c40-893e-ed3b35b84d0b'  =>  array(
94
     *         'loc'    =>  42,
95
     *         'ncloc'  =>  17,
96
     *         'cc'     =>  12
97
     *     )
98
     * )
99
     * </code>
100
     *
101
     * @var array(string=>array)
102
     */
103
    private $nodeMetrics = null;
104
105
    /**
106
     * The internal used cyclomatic complexity analyzer.
107
     *
108
     * @var \PDepend\Metrics\Analyzer\CyclomaticComplexityAnalyzer
109
     */
110
    private $cyclomaticAnalyzer = null;
111
112
    /**
113
     * Processes all {@link \PDepend\Source\AST\ASTNamespace} code nodes.
114
     *
115
     * @param  \PDepend\Source\AST\ASTNamespace[] $namespaces
116
     * @return void
117
     */
118 76
    public function analyze($namespaces)
119
    {
120 76
        if ($this->nodeMetrics === null) {
121
            // First check for the require cc analyzer
122 76
            if ($this->cyclomaticAnalyzer === null) {
123 2
                throw new \RuntimeException('Missing required CC analyzer.');
124
            }
125
126 74
            $this->fireStartAnalyzer();
127
128 74
            $this->cyclomaticAnalyzer->analyze($namespaces);
129
130
            // Init node metrics
131 74
            $this->nodeMetrics = array();
132
133
            // Visit all nodes
134 74
            foreach ($namespaces as $namespace) {
135 74
                $namespace->accept($this);
136 74
            }
137
138 74
            $this->fireEndAnalyzer();
139 74
        }
140 74
    }
141
142
    /**
143
     * This method must return an <b>array</b> of class names for required
144
     * analyzers.
145
     *
146
     * @return array(string)
0 ignored issues
show
The doc-type array(string) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
147
     */
148 2
    public function getRequiredAnalyzers()
149
    {
150 2
        return array('PDepend\\Metrics\\Analyzer\\CyclomaticComplexityAnalyzer');
151
    }
152
153
    /**
154
     * Adds a required sub analyzer.
155
     *
156
     * @param  \PDepend\Metrics\Analyzer $analyzer The sub analyzer instance.
157
     * @return void
158
     */
159 76
    public function addAnalyzer(\PDepend\Metrics\Analyzer $analyzer)
160
    {
161 76
        if ($analyzer instanceof \PDepend\Metrics\Analyzer\CyclomaticComplexityAnalyzer) {
162 74
            $this->cyclomaticAnalyzer = $analyzer;
163 74
        } else {
164 2
            throw new \InvalidArgumentException('CC Analyzer required.');
165
        }
166 74
    }
167
168
    /**
169
     * This method will return an <b>array</b> with all generated metric values
170
     * for the given <b>$node</b>. If there are no metrics for the requested
171
     * node, this method will return an empty <b>array</b>.
172
     *
173
     * @param  \PDepend\Source\AST\ASTArtifact $artifact
174
     * @return array(string=>mixed)
0 ignored issues
show
The doc-type array(string=>mixed) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
175
     */
176 74 View Code Duplication
    public function getNodeMetrics(ASTArtifact $artifact)
177
    {
178 74
        $metrics = array();
179 74
        if (isset($this->nodeMetrics[$artifact->getId()])) {
180 74
            $metrics = $this->nodeMetrics[$artifact->getId()];
181 74
        }
182 74
        return $metrics;
183
    }
184
185
    /**
186
     * Visits a class node.
187
     *
188
     * @param  \PDepend\Source\AST\ASTClass $class
189
     * @return void
190
     */
191 72
    public function visitClass(ASTClass $class)
192
    {
193 72
        $this->fireStartClass($class);
194
195 72
        $impl  = $class->getInterfaces()->count();
196 72
        $varsi = $this->calculateVarsi($class);
197 72
        $wmci  = $this->calculateWmciForClass($class);
198
199 72
        $this->nodeMetrics[$class->getId()] = array(
200 72
            self::M_IMPLEMENTED_INTERFACES       => $impl,
201 72
            self::M_CLASS_INTERFACE_SIZE         => 0,
202 72
            self::M_CLASS_SIZE                   => 0,
203 72
            self::M_NUMBER_OF_PUBLIC_METHODS     => 0,
204 72
            self::M_PROPERTIES                   => 0,
205 72
            self::M_PROPERTIES_INHERIT           => $varsi,
206 72
            self::M_PROPERTIES_NON_PRIVATE       => 0,
207 72
            self::M_WEIGHTED_METHODS             => 0,
208 72
            self::M_WEIGHTED_METHODS_INHERIT     => $wmci,
209 72
            self::M_WEIGHTED_METHODS_NON_PRIVATE => 0
210 72
        );
211
212 72
        foreach ($class->getProperties() as $property) {
213 40
            $property->accept($this);
214 72
        }
215 72
        foreach ($class->getMethods() as $method) {
216 50
            $method->accept($this);
217 72
        }
218
219 72
        $this->fireEndClass($class);
220 72
    }
221
222
    /**
223
     * Visits a code interface object.
224
     *
225
     * @param  \PDepend\Source\AST\ASTInterface $interface
226
     * @return void
227
     */
228 6
    public function visitInterface(ASTInterface $interface)
229
    {
230
        // Empty visit method, we don't want interface metrics
231 6
    }
232
233
    /**
234
     * Visits a trait node.
235
     *
236
     * @param  \PDepend\Source\AST\ASTTrait $trait
237
     * @return void
238
     * @since  1.0.0
239
     */
240 2
    public function visitTrait(ASTTrait $trait)
241
    {
242 2
        $this->fireStartTrait($trait);
243
244 2
        $wmci = $this->calculateWmciForTrait($trait);
245
246 2
        $this->nodeMetrics[$trait->getId()] = array(
247 2
            self::M_IMPLEMENTED_INTERFACES       => 0,
248 2
            self::M_CLASS_INTERFACE_SIZE         => 0,
249 2
            self::M_CLASS_SIZE                   => 0,
250 2
            self::M_NUMBER_OF_PUBLIC_METHODS     => 0,
251 2
            self::M_PROPERTIES                   => 0,
252 2
            self::M_PROPERTIES_INHERIT           => 0,
253 2
            self::M_PROPERTIES_NON_PRIVATE       => 0,
254 2
            self::M_WEIGHTED_METHODS             => 0,
255 2
            self::M_WEIGHTED_METHODS_INHERIT     => $wmci,
256 2
            self::M_WEIGHTED_METHODS_NON_PRIVATE => 0
257 2
        );
258
259 2
        foreach ($trait->getProperties() as $property) {
260
            $property->accept($this);
261 2
        }
262 2
        foreach ($trait->getMethods() as $method) {
263 2
            $method->accept($this);
264 2
        }
265
266 2
        $this->fireEndTrait($trait);
267 2
    }
268
269
    /**
270
     * Visits a method node.
271
     *
272
     * @param  \PDepend\Source\AST\ASTMethod $method
273
     * @return void
274
     */
275 52
    public function visitMethod(ASTMethod $method)
276
    {
277 52
        $this->fireStartMethod($method);
278
279 52
        $id = $method->getParent()->getId();
280
281 52
        $ccn = $this->cyclomaticAnalyzer->getCcn2($method);
282
283
        // Increment Weighted Methods Per Class(WMC) value
284 52
        $this->nodeMetrics[$id][self::M_WEIGHTED_METHODS] += $ccn;
285
        // Increment Class Size(CSZ) value
286 52
        ++$this->nodeMetrics[$id][self::M_CLASS_SIZE];
287
288
        // Increment Non Private values
289 52
        if ($method->isPublic()) {
290 48
            ++$this->nodeMetrics[$id][self::M_NUMBER_OF_PUBLIC_METHODS];
291
            // Increment Non Private WMC value
292 48
            $this->nodeMetrics[$id][self::M_WEIGHTED_METHODS_NON_PRIVATE] += $ccn;
293
            // Increment Class Interface Size(CIS) value
294 48
            ++$this->nodeMetrics[$id][self::M_CLASS_INTERFACE_SIZE];
295 48
        }
296
297 52
        $this->fireEndMethod($method);
298 52
    }
299
300
    /**
301
     * Visits a property node.
302
     *
303
     * @param  \PDepend\Source\AST\ASTProperty $property
304
     * @return void
305
     */
306 40
    public function visitProperty(ASTProperty $property)
307
    {
308 40
        $this->fireStartProperty($property);
309
310 40
        $id = $property->getDeclaringClass()->getId();
311
312
        // Increment VARS value
313 40
        ++$this->nodeMetrics[$id][self::M_PROPERTIES];
314
        // Increment Class Size(CSZ) value
315 40
        ++$this->nodeMetrics[$id][self::M_CLASS_SIZE];
316
317
        // Increment Non Private values
318 40
        if ($property->isPublic()) {
319
            // Increment Non Private VARS value
320 40
            ++$this->nodeMetrics[$id][self::M_PROPERTIES_NON_PRIVATE];
321
            // Increment Class Interface Size(CIS) value
322 40
            ++$this->nodeMetrics[$id][self::M_CLASS_INTERFACE_SIZE];
323 40
        }
324
325 40
        $this->fireEndProperty($property);
326 40
    }
327
328
    /**
329
     * Calculates the Variables Inheritance of a class metric, this method only
330
     * counts protected and public properties of parent classes.
331
     *
332
     * @param  \PDepend\Source\AST\ASTClass $class The context class instance.
333
     * @return integer
334
     */
335 72
    private function calculateVarsi(ASTClass $class)
336
    {
337
        // List of properties, this method only counts not overwritten properties
338 72
        $properties = array();
339
        // Collect all properties of the context class
340 72
        foreach ($class->getProperties() as $prop) {
341 40
            $properties[$prop->getName()] = true;
342 72
        }
343
344 72
        foreach ($class->getParentClasses() as $parent) {
345 32
            foreach ($parent->getProperties() as $prop) {
346 24
                if (!$prop->isPrivate() && !isset($properties[$prop->getName()])) {
347 20
                    $properties[$prop->getName()] = true;
348 20
                }
349 32
            }
350 72
        }
351 72
        return count($properties);
352
    }
353
354
    /**
355
     * Calculates the Weight Method Per Class metric, this method only counts
356
     * protected and public methods of parent classes.
357
     *
358
     * @param  \PDepend\Source\AST\ASTClass $class The context class instance.
359
     * @return integer
360
     */
361 72
    private function calculateWmciForClass(ASTClass $class)
362
    {
363 72
        $ccn = $this->calculateWmci($class);
364
365 72
        foreach ($class->getParentClasses() as $parent) {
366 32
            foreach ($parent->getMethods() as $method) {
367 18
                if ($method->isPrivate()) {
368 18
                    continue;
369
                }
370 18
                if (isset($ccn[($name = $method->getName())])) {
371 10
                    continue;
372
                }
373 12
                $ccn[$name] = $this->cyclomaticAnalyzer->getCcn2($method);
374 32
            }
375 72
        }
376
377 72
        return array_sum($ccn);
378
    }
379
380
    /**
381
     * Calculates the Weight Method Per Class metric for a trait.
382
     *
383
     * @param  \PDepend\Source\AST\ASTTrait $trait
384
     * @return integer
385
     * @since  1.0.6
386
     */
387 2
    private function calculateWmciForTrait(ASTTrait $trait)
388
    {
389 2
        return array_sum($this->calculateWmci($trait));
390
    }
391
392
    /**
393
     * Calculates the Weight Method Per Class metric.
394
     *
395
     * @param  \PDepend\Source\AST\AbstractASTType $type
396
     * @return integer[]
397
     * @since  1.0.6
398
     */
399 74
    private function calculateWmci(AbstractASTType $type)
400
    {
401 74
        $ccn = array();
402
403 74
        foreach ($type->getMethods() as $method) {
404 52
            $ccn[$method->getName()] = $this->cyclomaticAnalyzer->getCcn2($method);
405 74
        }
406
407 74
        return $ccn;
408
    }
409
}
410