Ubiquity 2.5.2
php rapid development framework
Loading...
Searching...
No Matches
ConsoleTable.php
Go to the documentation of this file.
1<?php
3
5
7
8 const TOP_LEFT = '╭', TOP_RIGHT = '╮';
9
10 const H_LINE = '─', V_LINE = '│';
11
12 const M_COL_TOP = '┬', M_COL_ROW = '┼', M_COL_BOTTOM = '┴';
13
14 const M_ROW_LEFT = '├', M_ROW_RIGHT = '┤';
15
16 const BOTTOM_LEFT = '╰', BOTTOM_RIGHT = '╯';
17
18 const BINARY_VALUES = [
19 '0011' => self::TOP_LEFT,
20 '1011' => self::M_COL_TOP,
21 '1001' => self::TOP_RIGHT,
22 '0111' => self::M_ROW_LEFT,
23 '1111' => self::M_COL_ROW,
24 '1101' => self::M_ROW_RIGHT,
25 '0110' => self::BOTTOM_LEFT,
26 '1110' => self::M_COL_BOTTOM,
27 '1100' => self::BOTTOM_RIGHT,
28 '1010' => self::H_LINE,
29 '0101' => self::V_LINE
30 ];
31
32 private $h_lines = [];
33
34 private $v_lines = [];
35
40 private $datas;
41
46 private $colWidths;
47
52 private $rowHeight;
53
54 private $colCount;
55
56 private $rowCount;
57
58 private $padding = 5;
59
60 private $indent = 0;
61
63
64 private $preserveSpaceBefore = false;
65
75 private function getCellOutput($index, $row = null) {
76 $cell = $row ? $row[$index] : '-';
77 $width = $this->colWidths[$index];
78 $padding = str_repeat($row ? ' ' : '-', $this->padding);
79 $output = '';
80 if ($index === 0) {
81 $output .= str_repeat(' ', $this->indent);
82 }
83 $output .= ($this->v_lines[$index] == 1) ? $this->getVLine() : ' ';
84
85 $output .= $padding; // left padding
86 if (\is_string($cell)) {
87 $cell = \rtrim(\preg_replace('/\s+/', ' ', $cell)); // remove line breaks
88 if (! $this->preserveSpaceBefore) {
89 $cell = \ltrim($cell);
90 }
91 } else {
92 $cell = '{}';
93 }
94 $content = \preg_replace('#\x1b[[][^A-Za-z]*[A-Za-z]#', '', $cell);
95
96 $delta = - \mb_strlen($cell, 'UTF-8') + \mb_strlen($content, 'UTF-8') + $this->padding;
97 $output .= $this->mb_str_pad($cell, $width - $delta, $row ? ' ' : '-'); // cell content
98
99 if ($row && $index == \count($row) - 1) {
100 $output .= ($this->v_lines[\count($row)] == 1) ? $this->getVLine() : ' ';
101 }
102 return $output;
103 }
104
105 private function getVLine() {
106 return ConsoleFormatter::colorize(self::V_LINE, $this->borderColor);
107 }
108
109 private function initializeBorders() {
110 $this->h_lines = array_fill(0, \count($this->datas) + 1, 1);
111 $this->v_lines = array_fill(0, $this->colCount + 1, 1);
112 }
113
125 private function mb_str_pad($str, $pad_len, $pad_str = ' ', $dir = STR_PAD_RIGHT, $encoding = NULL) {
126 $content = \preg_replace('#\x1b[[][^A-Za-z]*[A-Za-z]#', '', $str);
127 $str_len = \mb_strlen($content);
128 $pad_str_len = \mb_strlen($pad_str);
129 if (! $str_len && ($dir == STR_PAD_RIGHT || $dir == STR_PAD_LEFT)) {
130 $str_len = 1; // @debug
131 }
132 if (! $pad_len || ! $pad_str_len || $pad_len <= $str_len) {
133 return $str;
134 }
135
136 $result = null;
137 $repeat = \ceil($str_len - $pad_str_len + $pad_len);
138 if ($dir == STR_PAD_RIGHT) {
139 $result = $str . \str_repeat($pad_str, $repeat);
140 $result = \mb_substr($result, 0, $pad_len);
141 } else if ($dir == STR_PAD_LEFT) {
142 $result = str_repeat($pad_str, $repeat) . $str;
143 $result = \mb_substr($result, - $pad_len);
144 } else if ($dir == STR_PAD_BOTH) {
145 $length = ($pad_len - $str_len) / 2;
146 $repeat = \ceil($length / $pad_str_len);
147 $result = \mb_substr(str_repeat($pad_str, $repeat), 0, \floor($length)) . $str . \mb_substr(\str_repeat($pad_str, $repeat), 0, ceil($length));
148 }
149
150 return $result;
151 }
152
153 public function setDatas($datas) {
154 $this->datas = $datas;
155 $this->calculateColWidths();
156 $this->initializeBorders();
157 $this->factorize();
158 }
159
160 public function factorize() {
161 $flag = [];
162 foreach ($this->datas as $y => &$row) {
163 if (\is_array($row)) {
164 $index = 0;
165 foreach ($row as &$col) {
166 if ((isset($flag[$index]) && $col !== $flag[$index]) || ! isset($flag[$index])) {
167 $flag[$index] = $col;
168 } else {
169 $col = '≡';
170 $this->h_lines[$y] = 0;
171 }
172 $index ++;
173 }
174 }
175 }
176 }
177
178 private function getLineRow($row, $yl) {
179 $result = [];
180 foreach ($row as $col) {
181 if ($col instanceof \DateTime) {
182 $lines = [
183 \Ubiquity\utils\base\UDateTime::elapsed($col)
184 ];
185 } else {
186 $lines = \explode("\n", $col);
187 }
188 $result[] = (isset($lines[$yl])) ? $lines[$yl] : ' ';
189 }
190 return $result;
191 }
192
193 public function getTable() {
194 $res = '';
195 $y = 0;
196 foreach ($this->datas as $row) {
197 $res .= $this->border($y) . PHP_EOL;
198 $rowHeight = $this->rowHeight[$y];
199 for ($yl = 0; $yl < $rowHeight; $yl ++) {
200 $lineRow = $this->getLineRow($row, $yl);
201 $index = 0;
202 foreach ($lineRow as $col) {
203 $res .= $this->getCellOutput($index, $lineRow);
204 $index ++;
205 }
206 $res .= PHP_EOL;
207 }
208 $y ++;
209 }
210 $res .= $this->border(\count($this->datas)) . PHP_EOL;
211 return $res;
212 }
213
214 public function removeVGrid($start = 2) {
215 for ($i = $start; $i < $this->colCount; $i ++) {
216 $this->v_lines[$i] = 0;
217 }
218 }
219
220 public function removeHGrid($start = 2) {
221 $size = \count($this->datas);
222 for ($i = $start; $i < $size; $i ++) {
223 $this->h_lines[$i] = 0;
224 }
225 }
226
227 public function removeGrid($hStart = 2, $vStart = 2) {
228 $this->removeHGrid($hStart);
229 $this->removeVGrid($vStart);
230 }
231
232 private function border($row) {
233 $line = ($this->h_lines[$row]) ? self::H_LINE : ' ';
234 $res = \str_repeat(' ', $this->indent);
235 for ($i = 0; $i < $this->colCount; $i ++) {
236 $res .= $this->getBorderValue($row, $i);
237 $res .= \str_repeat($line, $this->colWidths[$i]);
238 }
239 $res .= $this->getBorderValue($row, $this->colCount);
240 return ConsoleFormatter::colorize($res, $this->borderColor);
241 }
242
243 private function getBorderValue($row, $col) {
244 $res = [
245 null,
246 null,
247 null,
248 null
249 ];
250 if ($row == 0) {
251 $res[1] = '0';
252 }
253 if ($col == 0) {
254 $res[0] = '0';
255 }
256 if ($row == $this->rowCount) {
257 $res[3] = '0';
258 }
259 if ($col == $this->colCount) {
260 $res[2] = '0';
261 }
262 if ($this->h_lines[$row] == 1) {
263 if (! isset($res[0])) {
264 $res[0] = '1';
265 }
266 if (! isset($res[2])) {
267 $res[2] = '1';
268 }
269 }
270 if ($this->v_lines[$col] == 1) {
271 if (! isset($res[1])) {
272 $res[1] = '1';
273 }
274 if (! isset($res[3])) {
275 $res[3] = '1';
276 }
277 }
278 foreach ($res as &$r) {
279 if ($r == null) {
280 $r = '0';
281 }
282 }
283 $res = implode('', $res);
284 return self::BINARY_VALUES[$res];
285 }
286
292 private function calculateColWidths() {
293 $colCount = 0;
294 $y = 0;
295 foreach ($this->datas as $row) {
296 if (\is_array($row)) {
297 if (\count($row) > $colCount) {
298 $colCount = \count($row);
299 }
300 $index = 0;
301 $this->rowHeight[$y] = 1;
302 foreach ($row as $col) {
303 if ($col instanceof \DateTime) {
304 $col = \Ubiquity\utils\base\UDateTime::elapsed($col);
305 }
306 if (\is_scalar($col) || (\is_object($col) && \method_exists($col, '__toString'))) {
307 $lines = \explode("\n", $col);
308 $size = \count($lines);
309 if ($size > $this->rowHeight[$y]) {
310 $this->rowHeight[$y] = $size;
311 }
312 foreach ($lines as $line) {
313 $content = \preg_replace('#\x1b[[][^A-Za-z]*[A-Za-z]#', '', $line);
314 $len = \mb_strlen($content, 'UTF-8');
315 if (! isset($this->colWidths[$index])) {
316 $this->colWidths[$index] = $len + 2 * $this->padding;
317 } else {
318 if ($len + 2 * $this->padding > $this->colWidths[$index]) {
319 $this->colWidths[$index] = $len + 2 * $this->padding;
320 }
321 }
322 }
323 }
324 $index ++;
325 }
326 }
327 $y ++;
328 }
329 $this->colCount = $colCount;
330 $this->rowCount = \count($this->datas);
331 return $this->colWidths;
332 }
333
338 public function setPadding($padding) {
339 $this->padding = $padding;
340 }
341
346 public function setIndent($indent) {
347 $this->indent = $indent;
348 }
349
354 public function setBorderColor($borderColor) {
355 $this->borderColor = $borderColor;
356 }
357
365 public static function borderColor($text, $color = ConsoleFormatter::LIGHT_GRAY) {
366 $border = new ConsoleTable();
367 $border->setIndent(5);
368 $border->setBorderColor($color);
369 $border->setDatas($text);
370 $border->setPreserveSpaceBefore(true);
371 return $border->getTable();
372 }
373
382 public static function borderType($text, $type) {
383 switch ($type) {
384 case 'error':
386 break;
387 case 'success':
389 break;
390 case 'info':
392 break;
393 case 'warning':
395 break;
396 default:
398 break;
399 }
400 return self::borderColor($text, $color);
401 }
402
408 $this->preserveSpaceBefore = $preserveSpaceBefore;
409 }
410
411 public function __toString() {
412 return $this->getTable();
413 }
414
422 public static function table(array $datas, $part = null) {
423 $tbl = new ConsoleTable();
424 $array = new ClassicArray($datas, $part);
425 $tbl->setDatas($array->parse());
426 return $tbl;
427 }
428}
429
static colorize($string, $color=null, $bgColor=null)
Returns a colored string.
mb_str_pad($str, $pad_len, $pad_str=' ', $dir=STR_PAD_RIGHT, $encoding=NULL)
mb_str_pad version for multibyte encoding.
static borderType($text, $type)
Surround the text with a border of the specified type.
setPreserveSpaceBefore($preserveSpaceBefore)
static table(array $datas, $part=null)
Return a formated table for displaying the datas.
getCellOutput($index, $row=null)
Get the printable cell content.
static borderColor($text, $color=ConsoleFormatter::LIGHT_GRAY)
Surround the text with a border of the specified color.
calculateColWidths()
Calculate the maximum width of each column.