Completed
Push — master ( 95acd7...e70d94 )
by Naveen
13:02 queued 11:07
created

PHP_CodeCoverage_Report_Node_File   D

Complexity

Total Complexity 87

Size/Duplication

Total Lines 664
Duplicated Lines 25.3 %

Coupling/Cohesion

Components 1
Dependencies 4
Metric Value
wmc 87
lcom 1
cbo 4
dl 168
loc 664
rs 4.6062

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like PHP_CodeCoverage_Report_Node_File often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PHP_CodeCoverage_Report_Node_File, and based on these observations, apply Extract Interface, too.

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
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
            foreach ($this->classes as $class) {
297
                foreach ($class['methods'] as $method) {
298
                    if ($method['executableLines'] > 0 &&
299
                        $method['coverage'] == 100) {
300
                        $this->numTestedMethods++;
301
                    }
302
                }
303
            }
304
305
            foreach ($this->traits as $trait) {
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) {
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) {
455
                        end($functionStack);
456
                        $key             = key($functionStack);
457
                        $currentFunction = &$functionStack[$key];
458
                        unset($functionStack[$key]);
459
                    }
460
                }
461
            }
462
        }
463
464
        foreach ($this->traits as &$trait) {
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
        foreach ($this->classes as &$class) {
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
    protected function processClasses(PHP_Token_Stream $tokens)
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
    protected function processTraits(PHP_Token_Stream $tokens)
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