Completed
Push — master ( 96df1a...267f86 )
by Naveen
09:52
created

calculateStatistics()   F

Complexity

Conditions 37
Paths > 20000

Size

Total Lines 180
Code Lines 106

Duplication

Lines 66
Ratio 36.67 %
Metric Value
dl 66
loc 180
rs 2
cc 37
eloc 106
nc 429496.7295
nop 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 the PHP_CodeCoverage package.
4
 *
5
 * (c) Sebastian Bergmann <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
/**
12
 * Represents a file in the code coverage information tree.
13
 *
14
 * @since Class available since Release 1.1.0
15
 */
16
class PHP_CodeCoverage_Report_Node_File extends PHP_CodeCoverage_Report_Node
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
17
{
18
    /**
19
     * @var array
20
     */
21
    protected $coverageData;
22
23
    /**
24
     * @var array
25
     */
26
    protected $testData;
27
28
    /**
29
     * @var int
30
     */
31
    protected $numExecutableLines = 0;
32
33
    /**
34
     * @var int
35
     */
36
    protected $numExecutedLines = 0;
37
38
    /**
39
     * @var array
40
     */
41
    protected $classes = array();
42
43
    /**
44
     * @var array
45
     */
46
    protected $traits = array();
47
48
    /**
49
     * @var array
50
     */
51
    protected $functions = array();
52
53
    /**
54
     * @var array
55
     */
56
    protected $linesOfCode = array();
57
58
    /**
59
     * @var int
60
     */
61
    protected $numTestedTraits = 0;
62
63
    /**
64
     * @var int
65
     */
66
    protected $numTestedClasses = 0;
67
68
    /**
69
     * @var int
70
     */
71
    protected $numMethods = null;
72
73
    /**
74
     * @var int
75
     */
76
    protected $numTestedMethods = null;
77
78
    /**
79
     * @var int
80
     */
81
    protected $numTestedFunctions = null;
82
83
    /**
84
     * @var array
85
     */
86
    protected $startLines = array();
87
88
    /**
89
     * @var array
90
     */
91
    protected $endLines = array();
92
93
    /**
94
     * @var bool
95
     */
96
    protected $cacheTokens;
97
98
    /**
99
     * Constructor.
100
     *
101
     * @param  string                       $name
102
     * @param  PHP_CodeCoverage_Report_Node $parent
103
     * @param  array                        $coverageData
104
     * @param  array                        $testData
105
     * @param  bool                         $cacheTokens
106
     * @throws PHP_CodeCoverage_Exception
107
     */
108
    public function __construct($name, PHP_CodeCoverage_Report_Node $parent, array $coverageData, array $testData, $cacheTokens)
109
    {
110
        if (!is_bool($cacheTokens)) {
111
            throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
112
                1,
113
                'boolean'
114
            );
115
        }
116
117
        parent::__construct($name, $parent);
118
119
        $this->coverageData = $coverageData;
120
        $this->testData     = $testData;
121
        $this->cacheTokens  = $cacheTokens;
122
123
        $this->calculateStatistics();
124
    }
125
126
    /**
127
     * Returns the number of files in/under this node.
128
     *
129
     * @return int
130
     */
131
    public function count()
132
    {
133
        return 1;
134
    }
135
136
    /**
137
     * Returns the code coverage data of this node.
138
     *
139
     * @return array
140
     */
141
    public function getCoverageData()
142
    {
143
        return $this->coverageData;
144
    }
145
146
    /**
147
     * Returns the test data of this node.
148
     *
149
     * @return array
150
     */
151
    public function getTestData()
152
    {
153
        return $this->testData;
154
    }
155
156
    /**
157
     * Returns the classes of this node.
158
     *
159
     * @return array
160
     */
161
    public function getClasses()
162
    {
163
        return $this->classes;
164
    }
165
166
    /**
167
     * Returns the traits of this node.
168
     *
169
     * @return array
170
     */
171
    public function getTraits()
172
    {
173
        return $this->traits;
174
    }
175
176
    /**
177
     * Returns the functions of this node.
178
     *
179
     * @return array
180
     */
181
    public function getFunctions()
182
    {
183
        return $this->functions;
184
    }
185
186
    /**
187
     * Returns the LOC/CLOC/NCLOC of this node.
188
     *
189
     * @return array
190
     */
191
    public function getLinesOfCode()
192
    {
193
        return $this->linesOfCode;
194
    }
195
196
    /**
197
     * Returns the number of executable lines.
198
     *
199
     * @return int
200
     */
201
    public function getNumExecutableLines()
202
    {
203
        return $this->numExecutableLines;
204
    }
205
206
    /**
207
     * Returns the number of executed lines.
208
     *
209
     * @return int
210
     */
211
    public function getNumExecutedLines()
212
    {
213
        return $this->numExecutedLines;
214
    }
215
216
    /**
217
     * Returns the number of classes.
218
     *
219
     * @return int
220
     */
221
    public function getNumClasses()
222
    {
223
        return count($this->classes);
224
    }
225
226
    /**
227
     * Returns the number of tested classes.
228
     *
229
     * @return int
230
     */
231
    public function getNumTestedClasses()
232
    {
233
        return $this->numTestedClasses;
234
    }
235
236
    /**
237
     * Returns the number of traits.
238
     *
239
     * @return int
240
     */
241
    public function getNumTraits()
242
    {
243
        return count($this->traits);
244
    }
245
246
    /**
247
     * Returns the number of tested traits.
248
     *
249
     * @return int
250
     */
251
    public function getNumTestedTraits()
252
    {
253
        return $this->numTestedTraits;
254
    }
255
256
    /**
257
     * Returns the number of methods.
258
     *
259
     * @return int
260
     */
261
    public function getNumMethods()
262
    {
263
        if ($this->numMethods === null) {
264
            $this->numMethods = 0;
265
266
            foreach ($this->classes as $class) {
267
                foreach ($class['methods'] as $method) {
268
                    if ($method['executableLines'] > 0) {
269
                        $this->numMethods++;
270
                    }
271
                }
272
            }
273
274
            foreach ($this->traits as $trait) {
275
                foreach ($trait['methods'] as $method) {
276
                    if ($method['executableLines'] > 0) {
277
                        $this->numMethods++;
278
                    }
279
                }
280
            }
281
        }
282
283
        return $this->numMethods;
284
    }
285
286
    /**
287
     * Returns the number of tested methods.
288
     *
289
     * @return int
290
     */
291
    public function getNumTestedMethods()
292
    {
293
        if ($this->numTestedMethods === null) {
294
            $this->numTestedMethods = 0;
295
296 View Code Duplication
            foreach ($this->classes as $class) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
297
                foreach ($class['methods'] as $method) {
298
                    if ($method['executableLines'] > 0 &&
299
                        $method['coverage'] == 100) {
300
                        $this->numTestedMethods++;
301
                    }
302
                }
303
            }
304
305 View Code Duplication
            foreach ($this->traits as $trait) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
306
                foreach ($trait['methods'] as $method) {
307
                    if ($method['executableLines'] > 0 &&
308
                        $method['coverage'] == 100) {
309
                        $this->numTestedMethods++;
310
                    }
311
                }
312
            }
313
        }
314
315
        return $this->numTestedMethods;
316
    }
317
318
    /**
319
     * Returns the number of functions.
320
     *
321
     * @return int
322
     */
323
    public function getNumFunctions()
324
    {
325
        return count($this->functions);
326
    }
327
328
    /**
329
     * Returns the number of tested functions.
330
     *
331
     * @return int
332
     */
333
    public function getNumTestedFunctions()
334
    {
335
        if ($this->numTestedFunctions === null) {
336
            $this->numTestedFunctions = 0;
337
338
            foreach ($this->functions as $function) {
339
                if ($function['executableLines'] > 0 &&
340
                    $function['coverage'] == 100) {
341
                    $this->numTestedFunctions++;
342
                }
343
            }
344
        }
345
346
        return $this->numTestedFunctions;
347
    }
348
349
    /**
350
     * Calculates coverage statistics for the file.
351
     */
352
    protected function calculateStatistics()
353
    {
354
        $classStack = $functionStack = array();
355
356
        if ($this->cacheTokens) {
357
            $tokens = PHP_Token_Stream_CachingFactory::get($this->getPath());
358
        } else {
359
            $tokens = new PHP_Token_Stream($this->getPath());
360
        }
361
362
        $this->processClasses($tokens);
363
        $this->processTraits($tokens);
364
        $this->processFunctions($tokens);
365
        $this->linesOfCode = $tokens->getLinesOfCode();
366
        unset($tokens);
367
368
        for ($lineNumber = 1; $lineNumber <= $this->linesOfCode['loc']; $lineNumber++) {
369
            if (isset($this->startLines[$lineNumber])) {
370
                // Start line of a class.
371
                if (isset($this->startLines[$lineNumber]['className'])) {
372
                    if (isset($currentClass)) {
373
                        $classStack[] = &$currentClass;
374
                    }
375
376
                    $currentClass = &$this->startLines[$lineNumber];
377
                } // Start line of a trait.
378
                elseif (isset($this->startLines[$lineNumber]['traitName'])) {
379
                    $currentTrait = &$this->startLines[$lineNumber];
380
                } // Start line of a method.
381
                elseif (isset($this->startLines[$lineNumber]['methodName'])) {
382
                    $currentMethod = &$this->startLines[$lineNumber];
383
                } // Start line of a function.
384
                elseif (isset($this->startLines[$lineNumber]['functionName'])) {
385
                    if (isset($currentFunction)) {
386
                        $functionStack[] = &$currentFunction;
387
                    }
388
389
                    $currentFunction = &$this->startLines[$lineNumber];
390
                }
391
            }
392
393
            if (isset($this->coverageData[$lineNumber])) {
394
                if (isset($currentClass)) {
395
                    $currentClass['executableLines']++;
396
                }
397
398
                if (isset($currentTrait)) {
399
                    $currentTrait['executableLines']++;
400
                }
401
402
                if (isset($currentMethod)) {
403
                    $currentMethod['executableLines']++;
404
                }
405
406
                if (isset($currentFunction)) {
407
                    $currentFunction['executableLines']++;
408
                }
409
410
                $this->numExecutableLines++;
411
412
                if (count($this->coverageData[$lineNumber]) > 0) {
413
                    if (isset($currentClass)) {
414
                        $currentClass['executedLines']++;
415
                    }
416
417
                    if (isset($currentTrait)) {
418
                        $currentTrait['executedLines']++;
419
                    }
420
421
                    if (isset($currentMethod)) {
422
                        $currentMethod['executedLines']++;
423
                    }
424
425
                    if (isset($currentFunction)) {
426
                        $currentFunction['executedLines']++;
427
                    }
428
429
                    $this->numExecutedLines++;
430
                }
431
            }
432
433
            if (isset($this->endLines[$lineNumber])) {
434
                // End line of a class.
435
                if (isset($this->endLines[$lineNumber]['className'])) {
436
                    unset($currentClass);
437
438
                    if ($classStack) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $classStack of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
439
                        end($classStack);
440
                        $key          = key($classStack);
441
                        $currentClass = &$classStack[$key];
442
                        unset($classStack[$key]);
443
                    }
444
                } // End line of a trait.
445
                elseif (isset($this->endLines[$lineNumber]['traitName'])) {
446
                    unset($currentTrait);
447
                } // End line of a method.
448
                elseif (isset($this->endLines[$lineNumber]['methodName'])) {
449
                    unset($currentMethod);
450
                } // End line of a function.
451
                elseif (isset($this->endLines[$lineNumber]['functionName'])) {
452
                    unset($currentFunction);
453
454
                    if ($functionStack) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $functionStack of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
455
                        end($functionStack);
456
                        $key             = key($functionStack);
457
                        $currentFunction = &$functionStack[$key];
458
                        unset($functionStack[$key]);
459
                    }
460
                }
461
            }
462
        }
463
464 View Code Duplication
        foreach ($this->traits as &$trait) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
465
            foreach ($trait['methods'] as &$method) {
466
                if ($method['executableLines'] > 0) {
467
                    $method['coverage'] = ($method['executedLines'] /
468
                            $method['executableLines']) * 100;
469
                } else {
470
                    $method['coverage'] = 100;
471
                }
472
473
                $method['crap'] = $this->crap(
474
                    $method['ccn'],
475
                    $method['coverage']
476
                );
477
478
                $trait['ccn'] += $method['ccn'];
479
            }
480
481
            if ($trait['executableLines'] > 0) {
482
                $trait['coverage'] = ($trait['executedLines'] /
483
                        $trait['executableLines']) * 100;
484
            } else {
485
                $trait['coverage'] = 100;
486
            }
487
488
            if ($trait['coverage'] == 100) {
489
                $this->numTestedClasses++;
490
            }
491
492
            $trait['crap'] = $this->crap(
493
                $trait['ccn'],
494
                $trait['coverage']
495
            );
496
        }
497
498 View Code Duplication
        foreach ($this->classes as &$class) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
499
            foreach ($class['methods'] as &$method) {
500
                if ($method['executableLines'] > 0) {
501
                    $method['coverage'] = ($method['executedLines'] /
502
                            $method['executableLines']) * 100;
503
                } else {
504
                    $method['coverage'] = 100;
505
                }
506
507
                $method['crap'] = $this->crap(
508
                    $method['ccn'],
509
                    $method['coverage']
510
                );
511
512
                $class['ccn'] += $method['ccn'];
513
            }
514
515
            if ($class['executableLines'] > 0) {
516
                $class['coverage'] = ($class['executedLines'] /
517
                        $class['executableLines']) * 100;
518
            } else {
519
                $class['coverage'] = 100;
520
            }
521
522
            if ($class['coverage'] == 100) {
523
                $this->numTestedClasses++;
524
            }
525
526
            $class['crap'] = $this->crap(
527
                $class['ccn'],
528
                $class['coverage']
529
            );
530
        }
531
    }
532
533
    /**
534
     * @param PHP_Token_Stream $tokens
535
     */
536 View Code Duplication
    protected function processClasses(PHP_Token_Stream $tokens)
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...
537
    {
538
        $classes = $tokens->getClasses();
539
        unset($tokens);
540
541
        $link = $this->getId() . '.html#';
542
543
        foreach ($classes as $className => $class) {
544
            $this->classes[$className] = array(
545
                'className'       => $className,
546
                'methods'         => array(),
547
                'startLine'       => $class['startLine'],
548
                'executableLines' => 0,
549
                'executedLines'   => 0,
550
                'ccn'             => 0,
551
                'coverage'        => 0,
552
                'crap'            => 0,
553
                'package'         => $class['package'],
554
                'link'            => $link . $class['startLine']
555
            );
556
557
            $this->startLines[$class['startLine']] = &$this->classes[$className];
558
            $this->endLines[$class['endLine']]     = &$this->classes[$className];
559
560
            foreach ($class['methods'] as $methodName => $method) {
561
                $this->classes[$className]['methods'][$methodName] = array(
562
                    'methodName'      => $methodName,
563
                    'signature'       => $method['signature'],
564
                    'startLine'       => $method['startLine'],
565
                    'endLine'         => $method['endLine'],
566
                    'executableLines' => 0,
567
                    'executedLines'   => 0,
568
                    'ccn'             => $method['ccn'],
569
                    'coverage'        => 0,
570
                    'crap'            => 0,
571
                    'link'            => $link . $method['startLine']
572
                );
573
574
                $this->startLines[$method['startLine']] = &$this->classes[$className]['methods'][$methodName];
575
                $this->endLines[$method['endLine']]     = &$this->classes[$className]['methods'][$methodName];
576
            }
577
        }
578
    }
579
580
    /**
581
     * @param PHP_Token_Stream $tokens
582
     */
583 View Code Duplication
    protected function processTraits(PHP_Token_Stream $tokens)
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...
584
    {
585
        $traits = $tokens->getTraits();
586
        unset($tokens);
587
588
        $link = $this->getId() . '.html#';
589
590
        foreach ($traits as $traitName => $trait) {
591
            $this->traits[$traitName] = array(
592
                'traitName'       => $traitName,
593
                'methods'         => array(),
594
                'startLine'       => $trait['startLine'],
595
                'executableLines' => 0,
596
                'executedLines'   => 0,
597
                'ccn'             => 0,
598
                'coverage'        => 0,
599
                'crap'            => 0,
600
                'package'         => $trait['package'],
601
                'link'            => $link . $trait['startLine']
602
            );
603
604
            $this->startLines[$trait['startLine']] = &$this->traits[$traitName];
605
            $this->endLines[$trait['endLine']]     = &$this->traits[$traitName];
606
607
            foreach ($trait['methods'] as $methodName => $method) {
608
                $this->traits[$traitName]['methods'][$methodName] = array(
609
                    'methodName'      => $methodName,
610
                    'signature'       => $method['signature'],
611
                    'startLine'       => $method['startLine'],
612
                    'endLine'         => $method['endLine'],
613
                    'executableLines' => 0,
614
                    'executedLines'   => 0,
615
                    'ccn'             => $method['ccn'],
616
                    'coverage'        => 0,
617
                    'crap'            => 0,
618
                    'link'            => $link . $method['startLine']
619
                );
620
621
                $this->startLines[$method['startLine']] = &$this->traits[$traitName]['methods'][$methodName];
622
                $this->endLines[$method['endLine']]     = &$this->traits[$traitName]['methods'][$methodName];
623
            }
624
        }
625
    }
626
627
    /**
628
     * @param PHP_Token_Stream $tokens
629
     */
630
    protected function processFunctions(PHP_Token_Stream $tokens)
631
    {
632
        $functions = $tokens->getFunctions();
633
        unset($tokens);
634
635
        $link = $this->getId() . '.html#';
636
637
        foreach ($functions as $functionName => $function) {
638
            $this->functions[$functionName] = array(
639
                'functionName'    => $functionName,
640
                'signature'       => $function['signature'],
641
                'startLine'       => $function['startLine'],
642
                'executableLines' => 0,
643
                'executedLines'   => 0,
644
                'ccn'             => $function['ccn'],
645
                'coverage'        => 0,
646
                'crap'            => 0,
647
                'link'            => $link . $function['startLine']
648
            );
649
650
            $this->startLines[$function['startLine']] = &$this->functions[$functionName];
651
            $this->endLines[$function['endLine']]     = &$this->functions[$functionName];
652
        }
653
    }
654
655
    /**
656
     * Calculates the Change Risk Anti-Patterns (CRAP) index for a unit of code
657
     * based on its cyclomatic complexity and percentage of code coverage.
658
     *
659
     * @param  int    $ccn
660
     * @param  float  $coverage
661
     * @return string
662
     * @since  Method available since Release 1.2.0
663
     */
664
    protected function crap($ccn, $coverage)
665
    {
666
        if ($coverage == 0) {
667
            return (string) (pow($ccn, 2) + $ccn);
668
        }
669
670
        if ($coverage >= 95) {
671
            return (string) $ccn;
672
        }
673
674
        return sprintf(
675
            '%01.2F',
676
            pow($ccn, 2) * pow(1 - $coverage/100, 3) + $ccn
677
        );
678
    }
679
}
680