NodeLocAnalyzer::linesOfCode()   F
last analyzed

Complexity

Conditions 23
Paths 244

Size

Total Lines 66

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 23

Importance

Changes 0
Metric Value
cc 23
nc 244
nop 2
dl 0
loc 66
ccs 28
cts 28
cp 1
crap 23
rs 2.7833
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\AbstractCachingAnalyzer;
46
use PDepend\Metrics\AnalyzerFilterAware;
47
use PDepend\Metrics\AnalyzerNodeAware;
48
use PDepend\Metrics\AnalyzerProjectAware;
49
use PDepend\Source\AST\ASTArtifact;
50
use PDepend\Source\AST\ASTArtifactList;
51
use PDepend\Source\AST\ASTClass;
52
use PDepend\Source\AST\ASTCompilationUnit;
53
use PDepend\Source\AST\ASTFunction;
54
use PDepend\Source\AST\ASTInterface;
55
use PDepend\Source\AST\ASTMethod;
56
use PDepend\Source\Tokenizer\Tokens;
57
58
/**
59
 * This analyzer collects different lines of code metrics.
60
 *
61
 * It collects the total Lines Of Code(<b>loc</b>), the None Comment Lines Of
62
 * Code(<b>ncloc</b>), the Comment Lines Of Code(<b>cloc</b>) and a approximated
63
 * Executable Lines Of Code(<b>eloc</b>) for files, classes, interfaces,
64
 * methods, properties and function.
65
 *
66
 * The current implementation has a limitation, that affects inline comments.
67
 * The following code will suppress one line of code.
68
 *
69
 * <code>
70
 * function foo() {
71
 *     foobar(); // Bad behaviour...
72
 * }
73
 * </code>
74
 *
75
 * The same rule applies to class methods. mapi, <b>PLEASE, FIX THIS ISSUE.</b>
76
 *
77
 * @copyright 2008-2017 Manuel Pichler. All rights reserved.
78
 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
79
 */
80
class NodeLocAnalyzer extends AbstractCachingAnalyzer implements
81
    AnalyzerNodeAware,
82
    AnalyzerFilterAware,
83
    AnalyzerProjectAware
84
{
85
    /**
86
     * Metrics provided by the analyzer implementation.
87
     */
88
    const M_LINES_OF_CODE             = 'loc',
89
          M_COMMENT_LINES_OF_CODE     = 'cloc',
90
          M_EXECUTABLE_LINES_OF_CODE  = 'eloc',
91
          M_LOGICAL_LINES_OF_CODE     = 'lloc',
92
          M_NON_COMMENT_LINES_OF_CODE = 'ncloc';
93
94
    /**
95
     * Collected project metrics.
96
     *
97
     * @var array<string, integer>
98
     */
99
    private $projectMetrics = array(
100
        self::M_LINES_OF_CODE              =>  0,
101
        self::M_COMMENT_LINES_OF_CODE      =>  0,
102
        self::M_EXECUTABLE_LINES_OF_CODE   =>  0,
103
        self::M_LOGICAL_LINES_OF_CODE      =>  0,
104
        self::M_NON_COMMENT_LINES_OF_CODE  =>  0
105
    );
106
107
    /**
108
     * Executable lines of code in a class. The method calculation increases
109
     * this property with each method's ELOC value.
110
     *
111
     * @var   integer
112
     * @since 0.9.12
113
     */
114
    private $classExecutableLines = 0;
115
116
    /**
117
     * Logical lines of code in a class. The method calculation increases this
118
     * property with each method's LLOC value.
119
     *
120
     * @var   integer
121
     * @since 0.9.13
122
     */
123
    private $classLogicalLines = 0;
124
125
    /**
126
     * This method will return an <b>array</b> with all generated metric values
127
     * for the given <b>$node</b> instance. If there are no metrics for the
128
     * requested node, this method will return an empty <b>array</b>.
129
     *
130
     * <code>
131
     * array(
132
     *     'loc'    =>  23,
133
     *     'cloc'   =>  17,
134
     *     'eloc'   =>  17,
135
     *     'ncloc'  =>  42
136
     * )
137
     * </code>
138
     *
139
     * @param  \PDepend\Source\AST\ASTArtifact $artifact
140
     * @return array
141
     */
142 54 View Code Duplication
    public function getNodeMetrics(ASTArtifact $artifact)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
143
    {
144 54
        $metrics = array();
145 54
        if (isset($this->metrics[$artifact->getId()])) {
146 52
            $metrics = $this->metrics[$artifact->getId()];
147
        }
148 54
        return $metrics;
149
    }
150
151
    /**
152
     * Provides the project summary as an <b>array</b>.
153
     *
154
     * <code>
155
     * array(
156
     *     'loc'    =>  23,
157
     *     'cloc'   =>  17,
158
     *     'ncloc'  =>  42
159
     * )
160
     * </code>
161
     *
162
     * @return array
163
     */
164 6
    public function getProjectMetrics()
165
    {
166 6
        return $this->projectMetrics;
167
    }
168
169
    /**
170
     * Processes all {@link \PDepend\Source\AST\ASTNamespace} code nodes.
171
     *
172
     * @param  \PDepend\Source\AST\ASTNamespace[] $namespaces
173
     * @return void
174
     */
175 58 View Code Duplication
    public function analyze($namespaces)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176
    {
177 58
        if ($this->metrics === null) {
178 58
            $this->loadCache();
179 58
            $this->fireStartAnalyzer();
180
181 58
            $this->metrics = array();
182 58
            foreach ($namespaces as $namespace) {
183 58
                $namespace->accept($this);
184
            }
185
186 58
            $this->fireEndAnalyzer();
187 58
            $this->unloadCache();
188
        }
189
    }
190
191
    /**
192
     * Visits a class node.
193
     *
194
     * @param  \PDepend\Source\AST\ASTClass $class
195
     * @return void
196
     */
197 26
    public function visitClass(ASTClass $class)
198
    {
199 26
        $this->fireStartClass($class);
200
201 26
        $class->getCompilationUnit()->accept($this);
202
203 26
        $this->classExecutableLines = 0;
204 26
        $this->classLogicalLines    = 0;
205
206 26
        foreach ($class->getMethods() as $method) {
207 16
            $method->accept($this);
208
        }
209
210 26
        if ($this->restoreFromCache($class)) {
211
            $this->fireEndClass($class);
212
            return;
213
        }
214
215 26
        list($cloc) = $this->linesOfCode($class->getTokens(), true);
216
217 26
        $loc   = $class->getEndLine() - $class->getStartLine() + 1;
218 26
        $ncloc = $loc - $cloc;
219
220 26
        $this->metrics[$class->getId()] = array(
221 26
            self::M_LINES_OF_CODE              =>  $loc,
222 26
            self::M_COMMENT_LINES_OF_CODE      =>  $cloc,
223 26
            self::M_EXECUTABLE_LINES_OF_CODE   =>  $this->classExecutableLines,
224 26
            self::M_LOGICAL_LINES_OF_CODE      =>  $this->classLogicalLines,
225 26
            self::M_NON_COMMENT_LINES_OF_CODE  =>  $ncloc,
226
        );
227
228 26
        $this->fireEndClass($class);
229
    }
230
231
    /**
232
     * Visits a file node.
233
     *
234
     * @param  \PDepend\Source\AST\ASTCompilationUnit $compilationUnit
235
     * @return void
236
     */
237 60
    public function visitCompilationUnit(ASTCompilationUnit $compilationUnit)
238
    {
239
        // Skip for dummy files
240 60
        if ($compilationUnit->getFileName() === null) {
241 2
            return;
242
        }
243
        // Check for initial file
244 58
        $id = $compilationUnit->getId();
245 58
        if (isset($this->metrics[$id])) {
246 10
            return;
247
        }
248
249 58
        $this->fireStartFile($compilationUnit);
250
251 58
        if ($this->restoreFromCache($compilationUnit)) {
252
            $this->updateProjectMetrics($id);
253
            $this->fireEndFile($compilationUnit);
254
            return;
255
        }
256
257 58
        list($cloc, $eloc, $lloc) = $this->linesOfCode($compilationUnit->getTokens());
258
259 58
        $loc   = $compilationUnit->getEndLine();
260 58
        $ncloc = $loc - $cloc;
261
262 58
        $this->metrics[$id] = array(
263 58
            self::M_LINES_OF_CODE              =>  $loc,
264 58
            self::M_COMMENT_LINES_OF_CODE      =>  $cloc,
265 58
            self::M_EXECUTABLE_LINES_OF_CODE   =>  $eloc,
266 58
            self::M_LOGICAL_LINES_OF_CODE      =>  $lloc,
267 58
            self::M_NON_COMMENT_LINES_OF_CODE  =>  $ncloc
268
        );
269
270 58
        $this->updateProjectMetrics($id);
271
272 58
        $this->fireEndFile($compilationUnit);
273
    }
274
275
    /**
276
     * Visits a function node.
277
     *
278
     * @param  \PDepend\Source\AST\ASTFunction $function
279
     * @return void
280
     */
281 26
    public function visitFunction(ASTFunction $function)
282
    {
283 26
        $this->fireStartFunction($function);
284
285 26
        $function->getCompilationUnit()->accept($this);
286
287 26
        if ($this->restoreFromCache($function)) {
288
            $this->fireEndFunction($function);
289
            return;
290
        }
291
292 26
        list($cloc, $eloc, $lloc) = $this->linesOfCode(
293 26
            $function->getTokens(),
294 26
            true
295
        );
296
297 26
        $loc   = $function->getEndLine() - $function->getStartLine() + 1;
298 26
        $ncloc = $loc - $cloc;
299
300 26
        $this->metrics[$function->getId()] = array(
301 26
            self::M_LINES_OF_CODE              =>  $loc,
302 26
            self::M_COMMENT_LINES_OF_CODE      =>  $cloc,
303 26
            self::M_EXECUTABLE_LINES_OF_CODE   =>  $eloc,
304 26
            self::M_LOGICAL_LINES_OF_CODE      =>  $lloc,
305 26
            self::M_NON_COMMENT_LINES_OF_CODE  =>  $ncloc
306
        );
307
308 26
        $this->fireEndFunction($function);
309
    }
310
311
    /**
312
     * Visits a code interface object.
313
     *
314
     * @param  \PDepend\Source\AST\ASTInterface $interface
315
     * @return void
316
     */
317 14
    public function visitInterface(ASTInterface $interface)
318
    {
319 14
        $this->fireStartInterface($interface);
320
321 14
        $interface->getCompilationUnit()->accept($this);
322
323 14
        foreach ($interface->getMethods() as $method) {
324 8
            $method->accept($this);
325
        }
326
327 14
        if ($this->restoreFromCache($interface)) {
328
            $this->fireEndInterface($interface);
329
            return;
330
        }
331
332 14
        list($cloc) = $this->linesOfCode($interface->getTokens(), true);
333
334 14
        $loc   = $interface->getEndLine() - $interface->getStartLine() + 1;
335 14
        $ncloc = $loc - $cloc;
336
337 14
        $this->metrics[$interface->getId()] = array(
338 14
            self::M_LINES_OF_CODE              =>  $loc,
339 14
            self::M_COMMENT_LINES_OF_CODE      =>  $cloc,
340 14
            self::M_EXECUTABLE_LINES_OF_CODE   =>  0,
341 14
            self::M_LOGICAL_LINES_OF_CODE      =>  0,
342 14
            self::M_NON_COMMENT_LINES_OF_CODE  =>  $ncloc
343
        );
344
345 14
        $this->fireEndInterface($interface);
346
    }
347
348
    /**
349
     * Visits a method node.
350
     *
351
     * @param  \PDepend\Source\AST\ASTMethod $method
352
     * @return void
353
     */
354 20
    public function visitMethod(ASTMethod $method)
355
    {
356 20
        $this->fireStartMethod($method);
357
358 20
        if ($this->restoreFromCache($method)) {
359
            $this->fireEndMethod($method);
360
            return;
361
        }
362
        
363 20
        if ($method->isAbstract()) {
364 10
            $cloc = 0;
365 10
            $eloc = 0;
366 10
            $lloc = 0;
367
        } else {
368 14
            list($cloc, $eloc, $lloc) = $this->linesOfCode(
369 14
                $method->getTokens(),
370 14
                true
371
            );
372
        }
373 20
        $loc   = $method->getEndLine() - $method->getStartLine() + 1;
374 20
        $ncloc = $loc - $cloc;
375
376 20
        $this->metrics[$method->getId()] = array(
377 20
            self::M_LINES_OF_CODE              =>  $loc,
378 20
            self::M_COMMENT_LINES_OF_CODE      =>  $cloc,
379 20
            self::M_EXECUTABLE_LINES_OF_CODE   =>  $eloc,
380 20
            self::M_LOGICAL_LINES_OF_CODE      =>  $lloc,
381 20
            self::M_NON_COMMENT_LINES_OF_CODE  =>  $ncloc
382
        );
383
384 20
        $this->classExecutableLines += $eloc;
385 20
        $this->classLogicalLines    += $lloc;
386
387 20
        $this->fireEndMethod($method);
388
    }
389
390
    /**
391
     * Updates the project metrics based on the node metrics identifier by the
392
     * given <b>$id</b>.
393
     *
394
     * @param  string $id The unique identifier of a node.
395
     * @return void
396
     */
397 58
    private function updateProjectMetrics($id)
398
    {
399 58
        foreach ($this->metrics[$id] as $metric => $value) {
400 58
            $this->projectMetrics[$metric] += $value;
401
        }
402
    }
403
404
    /**
405
     * Counts the Comment Lines Of Code (CLOC) and a pseudo Executable Lines Of
406
     * Code (ELOC) values.
407
     *
408
     * ELOC = Non Whitespace Lines + Non Comment Lines
409
     *
410
     * <code>
411
     * array(
412
     *     0  =>  23,  // Comment Lines Of Code
413
     *     1  =>  42   // Executable Lines Of Code
414
     * )
415
     * </code>
416
     *
417
     * @param  array   $tokens The raw token stream.
418
     * @param  boolean $search Optional boolean flag, search start.
419
     * @return array
420
     */
421 58
    private function linesOfCode(array $tokens, $search = false)
422
    {
423 58
        $clines = array();
424 58
        $elines = array();
425 58
        $llines = 0;
426
427 58
        $count = count($tokens);
428 58
        if ($search === true) {
429 58
            for ($i = 0; $i < $count; ++$i) {
430 58
                $token = $tokens[$i];
431
432 58
                if ($token->type === Tokens::T_CURLY_BRACE_OPEN) {
433 58
                    break;
434
                }
435
            }
436
        } else {
437 58
            $i = 0;
438
        }
439
440 58
        for (; $i < $count; ++$i) {
441 58
            $token = $tokens[$i];
442
443 58
            if ($token->type === Tokens::T_COMMENT
444 58
                || $token->type === Tokens::T_DOC_COMMENT
445
            ) {
446 20
                $lines =& $clines;
447
            } else {
448 58
                $lines =& $elines;
449
            }
450
451 58
            switch ($token->type) {
452
                // These statement are terminated by a semicolon
453
                //case \PDepend\Source\Tokenizer\Tokens::T_RETURN:
454
                //case \PDepend\Source\Tokenizer\Tokens::T_THROW:
455
                case Tokens::T_IF:
456
                case Tokens::T_TRY:
457
                case Tokens::T_CASE:
458
                case Tokens::T_GOTO:
459
                case Tokens::T_CATCH:
460
                case Tokens::T_WHILE:
461
                case Tokens::T_ELSEIF:
462
                case Tokens::T_SWITCH:
463
                case Tokens::T_DEFAULT:
464
                case Tokens::T_FOREACH:
465
                case Tokens::T_FUNCTION:
466
                case Tokens::T_SEMICOLON:
467 58
                    ++$llines;
468 58
                    break;
469
                case Tokens::T_DO:
470
                case Tokens::T_FOR:
471
                    // Because statements at least require one semicolon
472 10
                    --$llines;
473 10
                    break;
474
            }
475
476 58
            if ($token->startLine === $token->endLine) {
477 58
                $lines[$token->startLine] = true;
478
            } else {
479 20
                for ($j = $token->startLine; $j <= $token->endLine; ++$j) {
480 20
                    $lines[$j] = true;
481
                }
482
            }
483 58
            unset($lines);
484
        }
485 58
        return array(count($clines), count($elines), $llines);
486
    }
487
}
488