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

Reporter::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
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 array
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 = $this->config->get('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
        $svg = [];
209
        $svg['gt'] = '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
210
    <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"/>
211
    <path d="M0 0h24v24H0z" fill="none"/>
212
</svg>';
213
        $svg['eq'] = '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
214
    <path d="M22 12l-4-4v3H3v2h15v3z"/>
215
    <path d="M0 0h24v24H0z" fill="none"/>
216
</svg>';
217
        $svg['lt'] = '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
218
    <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"/>
219
    <path d="M0 0h24v24H0z" fill="none"/>
220
</svg>';
221
222
        $last = end($this->history);
223
        if (!isset($last->$type->$key)) {
224
            return '';
225
        }
226
227
        $oldValue = $last->$type->$key;
228
        $newValue = isset($this->$type->$key) ? $this->$type->$key : 0;
229
        if ($newValue > $oldValue) {
230
            $r = 'gt';
231
        } elseif ($newValue < $oldValue) {
232
            $r = 'lt';
233
        } else {
234
            $r = 'eq';
235
        }
236
237
        $diff = $newValue - $oldValue;
238
        if ($diff > 0) {
239
            $diff = '+' . $diff;
240
        }
241
242
        $goodOrBad = 'neutral';
243
        if ($lowIsBetter) {
244
            if ($newValue > $oldValue) {
245
                $goodOrBad = 'bad';
246
            } else {
247
                if ($newValue < $oldValue) {
248
                    $goodOrBad = 'good';
249
                }
250
            }
251
        }
252
        if ($highIsBetter) {
253
            if ($newValue > $oldValue) {
254
                $goodOrBad = 'good';
255
            } else {
256
                if ($newValue < $oldValue) {
257
                    $goodOrBad = 'bad';
258
                }
259
            }
260
        }
261
262
        return sprintf(
263
            '<span title="Last value: %s" class="progress progress-%s progress-%s">%s %s</span>',
264
            $oldValue,
265
            $goodOrBad,
266
            $r,
267
            $diff,
268
            $svg[$r]
269
        );
270
    }
271
272
    /**
273
     * @return bool
274
     */
275
    private function isHomePage()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
276
    {
277
        return !$this->currentGroup;
278
    }
279
}
280