GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( aa560e...acc50c )
by Brent
01:27 queued 10s
created

Visualizer::matrix()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.2728
c 0
b 0
f 0
cc 5
nc 5
nop 1
1
<?php
2
3
namespace Spatie\Period;
4
5
class Visualizer
6
{
7
    /**
8
     * Options used in configuring the visualization.
9
     *
10
     * - int width:
11
     * Determines the output size of the visualization
12
     * Note: This controls the width of the bars only.
13
     *
14
     * @var array
15
     */
16
    private $options;
17
18
    /**
19
     * Create a new visualizer.
20
     *
21
     * @param array $options
22
     */
23
    public function __construct(array $options = [])
24
    {
25
        $this->options = $options;
26
    }
27
28
    /**
29
     * Builds a string to visualize one or more
30
     * periods and/or collections in a more
31
     * human readable / parsable manner.
32
     *
33
     * Keys are used as identifiers in the output
34
     * and the periods are represented with bars.
35
     *
36
     * This visualizer is capable of generating
37
     * output like the following:
38
     *
39
     * A       [========]
40
     * B                    [==]
41
     * C                            [=====]
42
     * CURRENT        [===============]
43
     * OVERLAP        [=]   [==]    [=]
44
     *
45
     * @param array|Period[]|PeriodCollection[] $blocks
46
     * @return string
47
     */
48
    public function visualize(array $blocks): string
49
    {
50
        $matrix = $this->matrix($blocks);
51
52
        $nameLength = max(...array_map('strlen', array_keys($matrix)));
53
54
        $lines = [];
55
56
        foreach ($matrix as $name => $row) {
57
            $lines[] = vsprintf('%s    %s', [
58
                str_pad($name, $nameLength, ' '),
59
                $this->toBars($row),
60
            ]);
61
        }
62
63
        return implode("\n", $lines);
64
    }
65
66
    /**
67
     * Build a 2D table such that:
68
     * - There's one row for every block.
69
     * - There's one column for every unit of width.
70
     * - Each cell is true when a period is active for that unit.
71
     * - Each cell is false when a period is not active for that unit.
72
     *
73
     * @param array $blocks
74
     * @return array
75
     */
76
    private function matrix(array $blocks): array
77
    {
78
        $width = $this->options['width'];
79
80
        $matrix = array_fill(0, count($blocks), array_fill(0, $width, false));
81
        $matrix = array_combine(array_keys($blocks), array_values($matrix));
82
83
        $bounds = $this->bounds($blocks);
84
85
        foreach ($blocks as $name => $block) {
86
            if ($block instanceof Period) {
87
                $matrix[$name] = $this->populateRow($matrix[$name], $block, $bounds);
0 ignored issues
show
Bug introduced by
It seems like $bounds defined by $this->bounds($blocks) on line 83 can be null; however, Spatie\Period\Visualizer::populateRow() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
88
            } elseif ($block instanceof PeriodCollection) {
89
                foreach ($block as $period) {
90
                    $matrix[$name] = $this->populateRow($matrix[$name], $period, $bounds);
0 ignored issues
show
Bug introduced by
It seems like $bounds defined by $this->bounds($blocks) on line 83 can be null; however, Spatie\Period\Visualizer::populateRow() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
91
                }
92
            }
93
        }
94
95
        return $matrix;
96
    }
97
98
    /**
99
     * Get the start / end coordinates for a given period.
100
     *
101
     * @param Period $period
102
     * @param Period $bounds
103
     * @param int $width
104
     * @return array
105
     */
106
    private function coords(Period $period, Period $bounds, int $width): array
107
    {
108
        $boundsStart = $bounds->getStart()->getTimestamp();
109
        $boundsEnd = $bounds->getEnd()->getTimestamp();
110
        $boundsLength = $boundsEnd - $boundsStart;
111
112
        // Get the bounds
113
        $start = $period->getStart()->getTimestamp() - $boundsStart;
114
        $end = $period->getEnd()->getTimestamp() - $boundsStart;
115
116
        // Rescale from timestamps to width units
117
        $start *= $width / $boundsLength;
118
        $end *= $width / $boundsLength;
119
120
        // Cap at integer intervals
121
        $start = floor($start);
122
        $end = ceil($end);
123
124
        return [$start, $end];
125
    }
126
127
    /**
128
     * Populate a row with true values
129
     * where periods are active.
130
     *
131
     * @param array $row
132
     * @param Period $period
133
     * @param Period $bounds
134
     * @return array
135
     */
136
    private function populateRow(array $row, Period $period, Period $bounds): array
137
    {
138
        $width = $this->options['width'];
139
140
        [$startIndex, $endIndex] = $this->coords($period, $bounds, $width);
0 ignored issues
show
Bug introduced by
The variable $startIndex does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $endIndex does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
141
142
        for ($i = 0; $i < $width; $i++) {
143
            if ($startIndex <= $i && $i < $endIndex) {
144
                $row[$i] = true;
145
            }
146
        }
147
148
        return $row;
149
    }
150
151
    /**
152
     * Get the bounds encompassing all visualized periods.
153
     *
154
     * @param array $blocks
155
     * @return Period|null
156
     */
157
    private function bounds(array $blocks): ?Period
158
    {
159
        $periods = new PeriodCollection();
160
161
        foreach ($blocks as $block) {
162
            if ($block instanceof Period) {
163
                $periods[] = $block;
164
            } elseif ($block instanceof PeriodCollection) {
165
                foreach ($block as $period) {
166
                    $periods[] = $period;
167
                }
168
            }
169
        }
170
171
        return $periods->boundaries();
172
    }
173
174
    /**
175
     * Turn a series of true/false values into bars
176
     * representing the start/end of periods.
177
     *
178
     * @param array $row
179
     * @return string
180
     */
181
    private function toBars(array $row): string
182
    {
183
        $tmp = '';
184
185
        for ($i = 0, $l = count($row); $i < $l; $i++) {
186
            $prev = $row[$i - 1] ?? null;
187
            $curr = $row[$i];
188
            $next = $row[$i + 1] ?? null;
189
190
            // Small state machine to build the string
191
            switch (true) {
192
                // The current period is only one unit long so display a "="
193
                case $curr && $curr !== $prev && $curr !== $next:
194
                    $tmp .= '=';
195
                    break;
196
197
                // We've hit the start of a period
198
                case $curr && $curr !== $prev && $curr === $next:
199
                    $tmp .= '[';
200
                    break;
201
202
                // We've hit the end of the period
203
                case $curr && $curr !== $next:
204
                    $tmp .= ']';
205
                    break;
206
207
                // We're adding segments to the current period
208
                case $curr && $curr === $prev:
209
                    $tmp .= '=';
210
                    break;
211
212
                // Otherwise it's just empty space
213
                default:
214
                    $tmp .= ' ';
215
                    break;
216
            }
217
        }
218
219
        return $tmp;
220
    }
221
}
222