Completed
Push — nln-php7 ( 6680df...1a6b54 )
by Nicolas
02:06
created

CliTable::render()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 29
ccs 16
cts 16
cp 1
rs 9.456
c 0
b 0
f 0
cc 3
nc 4
nop 0
crap 3
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Karma\Display;
6
7
class CliTable
8
{
9
    private array
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_ARRAY, expecting T_FUNCTION or T_CONST
Loading history...
10
        $headers,
11
        $rows,
12
        $columnsSize;
13
    private int
14
        $nbColumns;
15
    private ?\Closure
16
        $valueRenderFunction;
17
    private bool
18
        $enableFormattingTags,
19
        $displayKeys;
20
21 12
    public function __construct(array $values)
22
    {
23 12
        $this->rows = $values;
24
25 12
        $this->headers = [];
26 12
        $this->nbColumns = 0;
27
28 12
        $this->valueRenderFunction = null;
29 12
        $this->enableFormattingTags = false;
30 12
        $this->displayKeys = false;
31 12
    }
32
33 2
    public function setValueRenderingFunction(\Closure $function): self
34
    {
35 2
        $this->valueRenderFunction = $function;
36
37 2
        return $this;
38
    }
39
40 3
    public function enableFormattingTags(bool $value = true): self
41
    {
42 3
        $this->enableFormattingTags = (bool) $value;
43
44 3
        return $this;
45
    }
46
47 8
    public function setHeaders(array $headers): self
48
    {
49 8
        $this->headers = $headers;
50
51 8
        return $this;
52
    }
53
54 3
    public function displayKeys(bool $value = true): self
55
    {
56 3
        $this->displayKeys = (bool) $value;
57
58 3
        return $this;
59
    }
60
61 12
    public function render(): string
62
    {
63 12
        $this->manageKeys();
64 12
        $this->computeColumnsSize();
65
66 8
        $nbSeparators = $this->nbColumns + 1;
67 8
        $paddingSize = 2 * $this->nbColumns;
68 8
        $totalSize = array_sum($this->columnsSize) + $nbSeparators + $paddingSize;
69
70 8
        $separatorRow = str_pad('|', $totalSize - 1, '-') . '|';
71
72 8
        $lines = array();
73 8
        $lines[] = $separatorRow;
74
75 8
        if(! empty($this->headers))
76
        {
77 8
            $lines[] = $this->renderLine($this->headers);
78 8
            $lines[] = $separatorRow;
79
        }
80
81 8
        foreach($this->rows as $row)
82
        {
83 8
            $lines[] = $this->renderLine($row);
84
        }
85
86 8
        $lines[] = $separatorRow;
87
88 8
        return implode(PHP_EOL, $lines);
89
    }
90
91 12
    private function manageKeys(): void
92
    {
93 12
        if($this->displayKeys !== true)
94
        {
95 10
            return;
96
        }
97
98 2
        if(! empty($this->headers))
99
        {
100 2
            array_unshift($this->headers, '');
101
        }
102
103 2
        foreach($this->rows as $key => $row)
104
        {
105 2
            array_unshift($row, $key);
106 2
            $this->rows[$key] = $row;
107
        }
108 2
    }
109
110 12
    private function computeColumnsSize(): void
111
    {
112 12
        $this->computeNbColumns();
113
114 12
        $this->columnsSize = array_pad(array(), $this->nbColumns, -1);
115
116 12
        if(! empty($this->headers))
117
        {
118 8
            $this->updateColumnsSize(array($this->headers));
119
        }
120
121 12
        $this->updateColumnsSize($this->rows);
122 8
    }
123
124 12
    private function computeNbColumns(): void
125
    {
126 12
        $this->nbColumns = 0;
127
128 12
        foreach($this->rows as $row)
129
        {
130 12
            if(is_array($row) ||$row instanceof \Countable)
131
            {
132 10
                $this->nbColumns = max($this->nbColumns, count($row));
133
            }
134
        }
135
136 12
        $this->nbColumns = max($this->nbColumns, count($this->headers));
137 12
    }
138
139 12
    private function updateColumnsSize(array $newValues): void
140
    {
141 12
        foreach($newValues as $row)
142
        {
143 12
            if(! is_array($row))
144
            {
145 2
                throw new \InvalidArgumentException('Rows must be arrays');
146
            }
147
148 10
            if(count($row) !== $this->nbColumns)
149
            {
150 2
                throw new \InvalidArgumentException('Rows must all have the same number of columns');
151
            }
152
153 9
            for($i = 0; $i < $this->nbColumns; $i++)
154
            {
155 9
                $value = $this->renderValueAsString($row[$i]);
156
157 9
                if($this->enableFormattingTags === true)
158
                {
159 3
                    $value = $this->stripTags($value);
160
                }
161
162 9
                $this->columnsSize[$i] = max(strlen($value), $this->columnsSize[$i]);
163
            }
164
        }
165 8
    }
166
167 9
    private function renderValueAsString($value): string
168
    {
169 9
        if($value === false)
170
        {
171 1
            $value = 'false';
172
        }
173 9
        elseif($value === true)
174
        {
175 3
            $value = 'true';
176
        }
177 9
        elseif($value === null)
178
        {
179 4
            $value = 'NULL';
180
        }
181
182 9
        if($this->valueRenderFunction instanceof \Closure)
183
        {
184 2
            $f = $this->valueRenderFunction;
185 2
            $value = $f($value);
186
        }
187
188 9
        return (string) $value;
189
    }
190
191 3
    private function stripTags(string $value): string
192
    {
193 3
        return preg_replace ('/<[^>]*>/', '', $value);
194
    }
195
196 8
    private function renderLine(array $row): string
197
    {
198 8
        $columns = array();
199
200 8
        for($i = 0; $i < $this->nbColumns; $i++)
201
        {
202 8
            $value = $this->renderValueAsString($row[$i]);
203
204 8
            $displayedString = str_pad(
205 8
                $value,
206 8
                $this->computeRealColumnSizeFor($value, $this->columnsSize[$i]),
207 8
                ' '
208
            );
209
210 8
            $columns[] = sprintf('| %s ', $displayedString);
211
        }
212
213 8
        return implode('', $columns) . '|';
214
    }
215
216 8
    private function computeRealColumnSizeFor(string $value, int $columnSize): int
217
    {
218 8
        $displayedValue = $value;
219
220 8
        if($this->enableFormattingTags === true)
221
        {
222 3
            $displayedValue = $this->stripTags($displayedValue);
223
        }
224
225 8
        $paddingLength = $columnSize - strlen($displayedValue);
226
227 8
        return strlen($value) + $paddingLength;
228
    }
229
}
230