Completed
Pull Request — master (#428)
by personal
01:41
created

Reporter   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 273
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 0
loc 273
rs 10
c 0
b 0
f 0
wmc 27
lcom 1
cbo 4

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
F generate() 0 117 11
A renderPage() 0 17 1
C getTrend() 0 69 13
A isHomePage() 0 4 1
1
<?php
2
3
namespace Hal\Report\Html;
4
5
use Hal\Application\Config\Config;
6
use Hal\Component\Output\Output;
7
use Hal\Metric\Consolidated;
8
use Hal\Metric\Group\Group;
9
use Hal\Metric\Metrics;
10
11
class Reporter
12
{
13
    /**
14
     * @var Config
15
     */
16
    private $config;
17
18
    /**
19
     * @var Output
20
     */
21
    private $output;
22
23
    /**
24
     * @var string
25
     */
26
    protected $templateDir;
27
28
    /**
29
     * @var Consolidated[]
30
     */
31
    private $consolidatedByGroups;
32
33
    /**
34
     * @var Group[]
35
     */
36
    private $groups = [];
37
38
    /**
39
     * @var string
40
     */
41
    private $currentGroup;
42
43
    /**
44
     * @var string
45
     */
46
    private $assetPath = '';
47
48
    /**
49
     * @param Config $config
50
     * @param Output $output
51
     */
52
    public function __construct(Config $config, Output $output)
53
    {
54
        $this->config = $config;
55
        $this->output = $output;
56
        $this->templateDir = __DIR__ . '/../../../../templates';
57
    }
58
59
60
    public function generate(Metrics $metrics)
61
    {
62
        $logDir = $this->config->get('report-html');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $logDir is correct as $this->config->get('report-html') (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...
63
        if (!$logDir) {
64
            return;
65
        }
66
67
        // consolidate
68
69
        /** @var Group[] $groups */
70
        $groups = $this->config->get('groups');
71
        $this->groups = $groups;
72
        $consolidatedGroups = [];
73
        foreach ($groups as $group) {
74
            $reducedMetricsByGroup = $group->reduceMetrics($metrics);
75
            $consolidatedGroups[$group->getName()] = new Consolidated($reducedMetricsByGroup);
76
        }
77
78
        $consolidated = new Consolidated($metrics);
79
80
        // history of builds
81
        $today = (object)[
82
            'avg' => $consolidated->getAvg(),
83
            'sum' => $consolidated->getSum()
84
        ];
85
        $files = glob($logDir . '/js/history-*.json');
86
        $next = count($files) + 1;
87
        $history = [];
88
        natsort($files);
89
        foreach ($files as $filename) {
90
            array_push($history, json_decode(file_get_contents($filename)));
91
        }
92
93
        // copy sources
94
        if (!file_exists($logDir . '/js')) {
95
            mkdir($logDir . '/js', 0755, true);
96
        }
97
        if (!file_exists($logDir . '/css')) {
98
            mkdir($logDir . '/css', 0755, true);
99
        }
100
        if (!file_exists($logDir . '/images')) {
101
            mkdir($logDir . '/images', 0755, true);
102
        }
103
        if (!file_exists($logDir . '/fonts')) {
104
            mkdir($logDir . '/fonts', 0755, true);
105
        }
106
        recurse_copy($this->templateDir . '/html_report/js', $logDir . '/js');
107
        recurse_copy($this->templateDir . '/html_report/css', $logDir . '/css');
108
        recurse_copy($this->templateDir . '/html_report/images', $logDir . '/images');
109
        recurse_copy($this->templateDir . '/html_report/fonts', $logDir . '/fonts');
110
111
        // render dynamic pages
112
        $this->renderPage($this->templateDir . '/html_report/index.php', $logDir . '/index.html', $consolidated, $history);
113
        $this->renderPage($this->templateDir . '/html_report/loc.php', $logDir . '/loc.html', $consolidated, $history);
114
        $this->renderPage($this->templateDir . '/html_report/relations.php', $logDir . '/relations.html', $consolidated, $history);
115
        $this->renderPage($this->templateDir . '/html_report/coupling.php', $logDir . '/coupling.html', $consolidated, $history);
116
        $this->renderPage($this->templateDir . '/html_report/all.php', $logDir . '/all.html', $consolidated, $history);
117
        $this->renderPage($this->templateDir . '/html_report/oop.php', $logDir . '/oop.html', $consolidated, $history);
118
        $this->renderPage($this->templateDir . '/html_report/complexity.php', $logDir . '/complexity.html', $consolidated, $history);
119
        $this->renderPage($this->templateDir . '/html_report/panel.php', $logDir . '/panel.html', $consolidated, $history);
120
        $this->renderPage($this->templateDir . '/html_report/violations.php', $logDir . '/violations.html', $consolidated, $history);
121
        $this->renderPage($this->templateDir . '/html_report/packages.php', $logDir . '/packages.html', $consolidated, $history);
122
        $this->renderPage($this->templateDir . '/html_report/package_relations.php', $logDir . '/package_relations.html', $consolidated, $history);
123
        $this->renderPage($this->templateDir . '/html_report/composer.php', $logDir . '/composer.html', $consolidated, $history);
124
        if ($this->config->has('git')) {
125
            $this->renderPage($this->templateDir . '/html_report/git.php', $logDir . '/git.html', $consolidated, $consolidatedGroups, $history);
0 ignored issues
show
Unused Code introduced by
The call to Reporter::renderPage() has too many arguments starting with $history.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
126
        }
127
        $this->renderPage($this->templateDir . '/html_report/junit.php', $logDir . '/junit.html', $consolidated, $consolidatedGroups, $history);
0 ignored issues
show
Unused Code introduced by
The call to Reporter::renderPage() has too many arguments starting with $history.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
128
129
        // js data
130
        file_put_contents(
131
            sprintf('%s/js/history-%d.json', $logDir, $next),
132
            json_encode($today, JSON_PRETTY_PRINT)
133
        );
134
        file_put_contents(
135
            sprintf('%s/js/latest.json', $logDir),
136
            json_encode($today, JSON_PRETTY_PRINT)
137
        );
138
139
        // json data
140
        file_put_contents(
141
            $logDir . '/classes.js',
142
            'var classes = ' . json_encode($consolidated->getClasses(), JSON_PRETTY_PRINT)
143
        );
144
145
        // consolidated by groups
146
        foreach ($consolidatedGroups as $name => $consolidated) {
147
            $outDir = $logDir . DIRECTORY_SEPARATOR . $name;
148
            $this->currentGroup = $name;
0 ignored issues
show
Documentation Bug introduced by
It seems like $name can also be of type integer. However, the property $currentGroup is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
149
            $this->assetPath = '../';
150
151
            if (!file_exists($outDir)) {
152
                mkdir($outDir, 0755, true);
153
            }
154
155
            $this->renderPage($this->templateDir . '/html_report/index.php', $outDir . '/index.html', $consolidated, $history);
156
            $this->renderPage($this->templateDir . '/html_report/loc.php', $outDir . '/loc.html', $consolidated, $history);
157
            $this->renderPage($this->templateDir . '/html_report/relations.php', $outDir . '/relations.html', $consolidated, $history);
158
            $this->renderPage($this->templateDir . '/html_report/coupling.php', $outDir . '/coupling.html', $consolidated, $history);
159
            $this->renderPage($this->templateDir . '/html_report/all.php', $outDir . '/all.html', $consolidated, $history);
160
            $this->renderPage($this->templateDir . '/html_report/oop.php', $outDir . '/oop.html', $consolidated, $history);
161
            $this->renderPage($this->templateDir . '/html_report/complexity.php', $outDir . '/complexity.html', $consolidated, $history);
162
            $this->renderPage($this->templateDir . '/html_report/panel.php', $outDir . '/panel.html', $consolidated, $history);
163
            $this->renderPage($this->templateDir . '/html_report/violations.php', $outDir . '/violations.html', $consolidated, $history);
164
            $this->renderPage($this->templateDir . '/html_report/packages.php', $outDir . '/packages.html', $consolidated, $history);
165
            $this->renderPage($this->templateDir . '/html_report/package_relations.php', $outDir . '/package_relations.html', $consolidated, $history);
166
            $this->renderPage($this->templateDir . '/html_report/composer.php', $outDir . '/composer.html', $consolidated, $history);
167
168
            // json data
169
            file_put_contents(
170
                $outDir . '/classes.js',
171
                'var classes = ' . json_encode($consolidated->getClasses(), JSON_PRETTY_PRINT)
172
            );
173
        }
174
175
        $this->output->writeln(sprintf('HTML report generated in "%s" directory', $logDir));
176
    }
177
178
    /**
179
     * @param $source
180
     * @param $destination
181
     * @return $this
182
     */
183
    public function renderPage($source, $destination, Consolidated $consolidated, $history)
184
    {
185
        $this->sum = $sum = $consolidated->getSum();
0 ignored issues
show
Bug introduced by
The property sum does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
186
        $this->avg = $avg = $consolidated->getAvg();
0 ignored issues
show
Bug introduced by
The property avg does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
187
        $this->classes = $classes = $consolidated->getClasses();
0 ignored issues
show
Bug introduced by
The property classes does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
188
        $this->files = $files = $consolidated->getFiles();
0 ignored issues
show
Bug introduced by
The property files does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
189
        $this->project = $project = $consolidated->getProject();
0 ignored issues
show
Bug introduced by
The property project does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
190
        $this->packages = $packages = $consolidated->getPackages();
0 ignored issues
show
Bug introduced by
The property packages does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
191
        $config = $this->config;
192
        $this->history = $history;
0 ignored issues
show
Bug introduced by
The property history does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
193
194
        ob_start();
195
        require $source;
196
        $content = ob_get_clean();
197
        file_put_contents($destination, $content);
198
        return $this;
199
    }
200
201
    /**
202
     * @param $type
203
     * @param $key
204
     * @return string
205
     */
206
    protected function getTrend($type, $key, $lowIsBetter = false, $highIsBetter = false)
207
    {
208
        if (!$this->isHomePage()) {
209
            return '';
210
        }
211
212
        $svg = [];
213
        $svg['gt'] = '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
214
    <path d="M16 6l2.29 2.29-4.88 4.88-4-4L2 16.59 3.41 18l6-6 4 4 6.3-6.29L22 12V6z"/>
215
    <path d="M0 0h24v24H0z" fill="none"/>
216
</svg>';
217
        $svg['eq'] = '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
218
    <path d="M22 12l-4-4v3H3v2h15v3z"/>
219
    <path d="M0 0h24v24H0z" fill="none"/>
220
</svg>';
221
        $svg['lt'] = '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
222
    <path d="M16 18l2.29-2.29-4.88-4.88-4 4L2 7.41 3.41 6l6 6 4-4 6.3 6.29L22 12v6z"/>
223
    <path d="M0 0h24v24H0z" fill="none"/>
224
</svg>';
225
226
        $last = end($this->history);
227
        if (!isset($last->$type->$key)) {
228
            return '';
229
        }
230
231
        $oldValue = $last->$type->$key;
232
        $newValue = isset($this->$type->$key) ? $this->$type->$key : 0;
233
        if ($newValue > $oldValue) {
234
            $r = 'gt';
235
        } elseif ($newValue < $oldValue) {
236
            $r = 'lt';
237
        } else {
238
            $r = 'eq';
239
        }
240
241
        $diff = $newValue - $oldValue;
242
        if ($diff > 0) {
243
            $diff = '+' . $diff;
244
        }
245
246
        $goodOrBad = 'neutral';
247
        if ($lowIsBetter) {
248
            if ($newValue > $oldValue) {
249
                $goodOrBad = 'bad';
250
            } else {
251
                if ($newValue < $oldValue) {
252
                    $goodOrBad = 'good';
253
                }
254
            }
255
        }
256
        if ($highIsBetter) {
257
            if ($newValue > $oldValue) {
258
                $goodOrBad = 'good';
259
            } else {
260
                if ($newValue < $oldValue) {
261
                    $goodOrBad = 'bad';
262
                }
263
            }
264
        }
265
266
        return sprintf(
267
            '<span title="Last value: %s" class="progress progress-%s progress-%s">%s %s</span>',
268
            $oldValue,
269
            $goodOrBad,
270
            $r,
271
            $diff,
272
            $svg[$r]
273
        );
274
    }
275
276
    /**
277
     * @return bool
278
     */
279
    private function isHomePage()
280
    {
281
        return null === $this->currentGroup;
282
    }
283
}
284