Completed
Push — master ( bdaaad...4f4372 )
by personal
04:35 queued 45s
created

GitChanges::calculate()   F

Complexity

Conditions 18
Paths 233

Size

Total Lines 136
Code Lines 75

Duplication

Lines 13
Ratio 9.56 %

Importance

Changes 0
Metric Value
cc 18
eloc 75
nc 233
nop 1
dl 13
loc 136
rs 3.9556
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
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