Completed
Push — master ( bdaaad...4f4372 )
by personal
06:57 queued 04:36
created

GitChanges   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 173
Duplicated Lines 7.51 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 13
loc 173
rs 10
c 0
b 0
f 0
wmc 20
lcom 1
cbo 5

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
F calculate() 13 136 18
A doesThisFileShouldBeCounted() 0 4 1

How to fix   Duplicated Code   

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:

1
<?php
2
namespace Hal\Metric\System\Changes;
3
4
use Hal\Application\Config\Config;
5
use Hal\Application\Config\ConfigException;
6
use Hal\Metric\FileMetric;
7
use Hal\Metric\Metrics;
8
use Hal\Metric\ProjectMetric;
9
10
class GitChanges
11
{
12
13
    /**
14
     * @var array
15
     */
16
    private $files = [];
17
18
    /**
19
     * @var Config
20
     */
21
    private $config;
22
23
    /**
24
     * GitChanges constructor.
25
     * @param array $files
26
     */
27
    public function __construct(Config $config, array $files)
28
    {
29
        $this->files = $files;
30
        $this->config = $config;
31
    }
32
33
    /**
34
     * @param Metrics $metrics
35
     * @throws ConfigException
36
     */
37
    public function calculate(Metrics $metrics)
38
    {
39
40
        if (!$this->config->has('git')) {
41
            return;
42
        }
43
44
        $bin = $this->config->get('git');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $bin is correct as $this->config->get('git') (which targets Hal\Application\Config\Config::get()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
45
        if (is_bool($bin)) {
46
            $bin = 'git';
47
        }
48
49
        if (sizeof($this->files) == 0) {
50
            return;
51
        }
52
53
        $r = shell_exec(sprintf('%s --version', $bin));
54
        if(!preg_match('!git version!', $r)) {
55
            throw new ConfigException(sprintf('Git binary (%s) incorrect', $bin));
56
        }
57
58
        // get all history (for only on directory for the moment
59
        // @todo: git history for multiple repositories
60
        // 500 last commits max
61
        $file = current($this->files);
62
        $command = sprintf("cd %s && %s log --format='* %%at\t%%cn' --numstat -n 500", realpath(dirname($file)), $bin);
63
        $r = shell_exec($command);
64
        $r = array_filter(explode(PHP_EOL, $r));
65
66
67
        // build a range of commits info, stepped by week number
68
        $history = [];
69
        $dateFormat = 'Y-W';
70
71
        // calculate statistics
72
        $firstCommitDate = null;
73
        $commitsByFile = [];
74
        $localFiles = [];
75
        $localFiles['additions'] = 0;
76
        $localFiles['removes'] = 0;
77
        $localFiles['nbFiles'] = 0;
78
        $authors = [];
79
80
        foreach ($r as $line) {
81
            if (preg_match('!^\* (\d+)\s+(.*)!', $line, $matches)) {
82
                // head line
83
84
                if (isset($date)) {
85
                    // new head line ($author is set). Consolidate now for last commit
86 View Code Duplication
                    if (!isset($history[$date])) {
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...
87
                        $history[$date] = ['nbFiles' => 0, 'additions' => 0, 'removes' => 0];
88
                    }
89
                    $history[$date]['nbFiles'] += $localFiles['nbFiles'];
90
                    $history[$date]['additions'] += $localFiles['additions'];
91
                    $history[$date]['removes'] += $localFiles['removes'];
92
93
                    // reset
94
                    $localFiles['additions'] = 0;
95
                    $localFiles['removes'] = 0;
96
                    $localFiles['nbFiles'] = 0;
97
                }
98
99
                // new infos
100
                list(, $timestamp, $author) = $matches;
101
                $date = (new \DateTime())->setTimestamp($timestamp)->format($dateFormat);
102
103
                if (is_null($firstCommitDate)) {
104
                    $firstCommitDate = $timestamp;
105
                }
106
107
                // author
108 View Code Duplication
                if (!isset($authors[$author])) {
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...
109
                    $authors[$author] = ['nbFiles' => 0, 'commits' => 0, 'additions' => 0, 'removes' => 0];
110
                }
111
                $authors[$author]['commits']++;
112
113
            } else {
114
                if (preg_match('!(\d+)\s+(\d+)\s+(.*)!', $line, $matches)) {
115
                    // additions and changes for each file
116
                    list(, $additions, $removes, $filename) = $matches;
117
118
                    if (!$this->doesThisFileShouldBeCounted($filename)) {
119
                        // we don't care about all files
120
                        continue;
121
                    }
122
123
                    // global history
124
                    $localFiles['additions'] += $additions;
125
                    $localFiles['removes'] += $removes;
126
                    $localFiles['nbFiles']++;
127
128
                    // commits by file
129
                    if (!isset($commitsByFile[$filename])) {
130
                        $commitsByFile[$filename] = 0;
131
                    }
132
                    $commitsByFile[$filename]++;
133
134
                    // author
135
                    if (isset($author)) {
136
                        $authors[$author]['nbFiles']++;
137
                        $authors[$author]['additions'] += $additions;
138
                        $authors[$author]['removes'] += $removes;
139
                    }
140
                }
141
            }
142
        }
143
144
        // build a range of dates since first commit
145
        // (pad weeks without any commit)
146
        $current = $firstCommitDate;
147
        $last = time();
148
        while ($current <= $last) {
149
            $key = date($dateFormat, $current);
150 View Code Duplication
            if (!isset($history[$key])) {
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...
151
                $history[$key] = [
152
                    'nbFiles' => 0,
153
                    'additions' => 0,
154
                    'removes' => 0,
155
                ];
156
            }
157
            $current = strtotime('+7 day', $current);
158
        }
159
160
161
        // store results
162
        $result = new ProjectMetric('git');
163
        $result->set('history', $history);
164
        $result->set('authors', $authors);
165
        $metrics->attach($result);
166
167
        foreach ($commitsByFile as $filename => $nbCommits) {
168
            $info = new FileMetric($filename);
169
            $info->set('gitChanges', $nbCommits);
170
            $metrics->attach($info);
171
        }
172
    }
173
174
    /**
175
     * @param $file
176
     * @return int
177
     */
178
    private function doesThisFileShouldBeCounted($file)
179
    {
180
        return preg_match('!\.(php|inc)$!i', $file);
181
    }
182
}
183