PluginProbe
Depicter — Popup & Slider Builder / 1.3.2
Depicter — Popup & Slider Builder v1.3.2
4.8.1 trunk 1.0.0 1.1.0 1.1.2 1.1.4 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.3.0 1.3.1 1.3.2 1.3.3 1.3.5 1.3.8 1.5.0 1.5.1 1.5.2 1.5.5 1.6.0 1.6.1 1.6.2 1.7.0 All 76 releases
depicter / vendor / symfony / console / Helper / Table.php

Table.php in Depicter — Popup & Slider Builder 1.3.2, at vendor/symfony/console/Helper/Table.php

841 lines 26.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Component\Console\Helper;
13
14 use Symfony\Component\Console\Exception\InvalidArgumentException;
15 use Symfony\Component\Console\Exception\RuntimeException;
16 use Symfony\Component\Console\Formatter\OutputFormatter;
17 use Symfony\Component\Console\Formatter\WrappableOutputFormatterInterface;
18 use Symfony\Component\Console\Output\ConsoleSectionOutput;
19 use Symfony\Component\Console\Output\OutputInterface;
20
21 /**
22 * Provides helpers to display a table.
23 *
24 * @author Fabien Potencier <fabien@symfony.com>
25 * @author Саша Стаменковић <umpirsky@gmail.com>
26 * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
27 * @author Max Grigorian <maxakawizard@gmail.com>
28 * @author Dany Maillard <danymaillard93b@gmail.com>
29 */
30 class Table
31 {
32 private const SEPARATOR_TOP = 0;
33 private const SEPARATOR_TOP_BOTTOM = 1;
34 private const SEPARATOR_MID = 2;
35 private const SEPARATOR_BOTTOM = 3;
36 private const BORDER_OUTSIDE = 0;
37 private const BORDER_INSIDE = 1;
38
39 private $headerTitle;
40 private $footerTitle;
41
42 /**
43 * Table headers.
44 */
45 private $headers = [];
46
47 /**
48 * Table rows.
49 */
50 private $rows = [];
51 private $horizontal = false;
52
53 /**
54 * Column widths cache.
55 */
56 private $effectiveColumnWidths = [];
57
58 /**
59 * Number of columns cache.
60 *
61 * @var int
62 */
63 private $numberOfColumns;
64
65 /**
66 * @var OutputInterface
67 */
68 private $output;
69
70 /**
71 * @var TableStyle
72 */
73 private $style;
74
75 /**
76 * @var array
77 */
78 private $columnStyles = [];
79
80 /**
81 * User set column widths.
82 *
83 * @var array
84 */
85 private $columnWidths = [];
86 private $columnMaxWidths = [];
87
88 private static $styles;
89
90 private $rendered = false;
91
92 public function __construct(OutputInterface $output)
93 {
94 $this->output = $output;
95
96 if (!self::$styles) {
97 self::$styles = self::initStyles();
98 }
99
100 $this->setStyle('default');
101 }
102
103 /**
104 * Sets a style definition.
105 */
106 public static function setStyleDefinition(string $name, TableStyle $style)
107 {
108 if (!self::$styles) {
109 self::$styles = self::initStyles();
110 }
111
112 self::$styles[$name] = $style;
113 }
114
115 /**
116 * Gets a style definition by name.
117 *
118 * @return TableStyle
119 */
120 public static function getStyleDefinition(string $name)
121 {
122 if (!self::$styles) {
123 self::$styles = self::initStyles();
124 }
125
126 if (isset(self::$styles[$name])) {
127 return self::$styles[$name];
128 }
129
130 throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
131 }
132
133 /**
134 * Sets table style.
135 *
136 * @param TableStyle|string $name The style name or a TableStyle instance
137 *
138 * @return $this
139 */
140 public function setStyle($name)
141 {
142 $this->style = $this->resolveStyle($name);
143
144 return $this;
145 }
146
147 /**
148 * Gets the current table style.
149 *
150 * @return TableStyle
151 */
152 public function getStyle()
153 {
154 return $this->style;
155 }
156
157 /**
158 * Sets table column style.
159 *
160 * @param TableStyle|string $name The style name or a TableStyle instance
161 *
162 * @return $this
163 */
164 public function setColumnStyle(int $columnIndex, $name)
165 {
166 $this->columnStyles[$columnIndex] = $this->resolveStyle($name);
167
168 return $this;
169 }
170
171 /**
172 * Gets the current style for a column.
173 *
174 * If style was not set, it returns the global table style.
175 *
176 * @return TableStyle
177 */
178 public function getColumnStyle(int $columnIndex)
179 {
180 return $this->columnStyles[$columnIndex] ?? $this->getStyle();
181 }
182
183 /**
184 * Sets the minimum width of a column.
185 *
186 * @return $this
187 */
188 public function setColumnWidth(int $columnIndex, int $width)
189 {
190 $this->columnWidths[$columnIndex] = $width;
191
192 return $this;
193 }
194
195 /**
196 * Sets the minimum width of all columns.
197 *
198 * @return $this
199 */
200 public function setColumnWidths(array $widths)
201 {
202 $this->columnWidths = [];
203 foreach ($widths as $index => $width) {
204 $this->setColumnWidth($index, $width);
205 }
206
207 return $this;
208 }
209
210 /**
211 * Sets the maximum width of a column.
212 *
213 * Any cell within this column which contents exceeds the specified width will be wrapped into multiple lines, while
214 * formatted strings are preserved.
215 *
216 * @return $this
217 */
218 public function setColumnMaxWidth(int $columnIndex, int $width): self
219 {
220 if (!$this->output->getFormatter() instanceof WrappableOutputFormatterInterface) {
221 throw new \LogicException(sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, get_debug_type($this->output->getFormatter())));
222 }
223
224 $this->columnMaxWidths[$columnIndex] = $width;
225
226 return $this;
227 }
228
229 public function setHeaders(array $headers)
230 {
231 $headers = array_values($headers);
232 if (!empty($headers) && !\is_array($headers[0])) {
233 $headers = [$headers];
234 }
235
236 $this->headers = $headers;
237
238 return $this;
239 }
240
241 public function setRows(array $rows)
242 {
243 $this->rows = [];
244
245 return $this->addRows($rows);
246 }
247
248 public function addRows(array $rows)
249 {
250 foreach ($rows as $row) {
251 $this->addRow($row);
252 }
253
254 return $this;
255 }
256
257 public function addRow($row)
258 {
259 if ($row instanceof TableSeparator) {
260 $this->rows[] = $row;
261
262 return $this;
263 }
264
265 if (!\is_array($row)) {
266 throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.');
267 }
268
269 $this->rows[] = array_values($row);
270
271 return $this;
272 }
273
274 /**
275 * Adds a row to the table, and re-renders the table.
276 */
277 public function appendRow($row): self
278 {
279 if (!$this->output instanceof ConsoleSectionOutput) {
280 throw new RuntimeException(sprintf('Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__));
281 }
282
283 if ($this->rendered) {
284 $this->output->clear($this->calculateRowCount());
285 }
286
287 $this->addRow($row);
288 $this->render();
289
290 return $this;
291 }
292
293 public function setRow($column, array $row)
294 {
295 $this->rows[$column] = $row;
296
297 return $this;
298 }
299
300 public function setHeaderTitle(?string $title): self
301 {
302 $this->headerTitle = $title;
303
304 return $this;
305 }
306
307 public function setFooterTitle(?string $title): self
308 {
309 $this->footerTitle = $title;
310
311 return $this;
312 }
313
314 public function setHorizontal(bool $horizontal = true): self
315 {
316 $this->horizontal = $horizontal;
317
318 return $this;
319 }
320
321 /**
322 * Renders table to output.
323 *
324 * Example:
325 *
326 * +---------------+-----------------------+------------------+
327 * | ISBN | Title | Author |
328 * +---------------+-----------------------+------------------+
329 * | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
330 * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
331 * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
332 * +---------------+-----------------------+------------------+
333 */
334 public function render()
335 {
336 $divider = new TableSeparator();
337 if ($this->horizontal) {
338 $rows = [];
339 foreach ($this->headers[0] ?? [] as $i => $header) {
340 $rows[$i] = [$header];
341 foreach ($this->rows as $row) {
342 if ($row instanceof TableSeparator) {
343 continue;
344 }
345 if (isset($row[$i])) {
346 $rows[$i][] = $row[$i];
347 } elseif ($rows[$i][0] instanceof TableCell && $rows[$i][0]->getColspan() >= 2) {
348 // Noop, there is a "title"
349 } else {
350 $rows[$i][] = null;
351 }
352 }
353 }
354 } else {
355 $rows = array_merge($this->headers, [$divider], $this->rows);
356 }
357
358 $this->calculateNumberOfColumns($rows);
359
360 $rows = $this->buildTableRows($rows);
361 $this->calculateColumnsWidth($rows);
362
363 $isHeader = !$this->horizontal;
364 $isFirstRow = $this->horizontal;
365 foreach ($rows as $row) {
366 if ($divider === $row) {
367 $isHeader = false;
368 $isFirstRow = true;
369
370 continue;
371 }
372 if ($row instanceof TableSeparator) {
373 $this->renderRowSeparator();
374
375 continue;
376 }
377 if (!$row) {
378 continue;
379 }
380
381 if ($isHeader || $isFirstRow) {
382 if ($isFirstRow) {
383 $this->renderRowSeparator(self::SEPARATOR_TOP_BOTTOM);
384 $isFirstRow = false;
385 } else {
386 $this->renderRowSeparator(self::SEPARATOR_TOP, $this->headerTitle, $this->style->getHeaderTitleFormat());
387 }
388 }
389 if ($this->horizontal) {
390 $this->renderRow($row, $this->style->getCellRowFormat(), $this->style->getCellHeaderFormat());
391 } else {
392 $this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat());
393 }
394 }
395 $this->renderRowSeparator(self::SEPARATOR_BOTTOM, $this->footerTitle, $this->style->getFooterTitleFormat());
396
397 $this->cleanup();
398 $this->rendered = true;
399 }
400
401 /**
402 * Renders horizontal header separator.
403 *
404 * Example:
405 *
406 * +-----+-----------+-------+
407 */
408 private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null)
409 {
410 if (0 === $count = $this->numberOfColumns) {
411 return;
412 }
413
414 $borders = $this->style->getBorderChars();
415 if (!$borders[0] && !$borders[2] && !$this->style->getCrossingChar()) {
416 return;
417 }
418
419 $crossings = $this->style->getCrossingChars();
420 if (self::SEPARATOR_MID === $type) {
421 [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[2], $crossings[8], $crossings[0], $crossings[4]];
422 } elseif (self::SEPARATOR_TOP === $type) {
423 [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[1], $crossings[2], $crossings[3]];
424 } elseif (self::SEPARATOR_TOP_BOTTOM === $type) {
425 [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[9], $crossings[10], $crossings[11]];
426 } else {
427 [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[7], $crossings[6], $crossings[5]];
428 }
429
430 $markup = $leftChar;
431 for ($column = 0; $column < $count; ++$column) {
432 $markup .= str_repeat($horizontal, $this->effectiveColumnWidths[$column]);
433 $markup .= $column === $count - 1 ? $rightChar : $midChar;
434 }
435
436 if (null !== $title) {
437 $titleLength = Helper::strlenWithoutDecoration($formatter = $this->output->getFormatter(), $formattedTitle = sprintf($titleFormat, $title));
438 $markupLength = Helper::strlen($markup);
439 if ($titleLength > $limit = $markupLength - 4) {
440 $titleLength = $limit;
441 $formatLength = Helper::strlenWithoutDecoration($formatter, sprintf($titleFormat, ''));
442 $formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...');
443 }
444
445 $titleStart = ($markupLength - $titleLength) / 2;
446 if (false === mb_detect_encoding($markup, null, true)) {
447 $markup = substr_replace($markup, $formattedTitle, $titleStart, $titleLength);
448 } else {
449 $markup = mb_substr($markup, 0, $titleStart).$formattedTitle.mb_substr($markup, $titleStart + $titleLength);
450 }
451 }
452
453 $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
454 }
455
456 /**
457 * Renders vertical column separator.
458 */
459 private function renderColumnSeparator(int $type = self::BORDER_OUTSIDE): string
460 {
461 $borders = $this->style->getBorderChars();
462
463 return sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]);
464 }
465
466 /**
467 * Renders table row.
468 *
469 * Example:
470 *
471 * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
472 */
473 private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null)
474 {
475 $rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE);
476 $columns = $this->getRowColumns($row);
477 $last = \count($columns) - 1;
478 foreach ($columns as $i => $column) {
479 if ($firstCellFormat && 0 === $i) {
480 $rowContent .= $this->renderCell($row, $column, $firstCellFormat);
481 } else {
482 $rowContent .= $this->renderCell($row, $column, $cellFormat);
483 }
484 $rowContent .= $this->renderColumnSeparator($last === $i ? self::BORDER_OUTSIDE : self::BORDER_INSIDE);
485 }
486 $this->output->writeln($rowContent);
487 }
488
489 /**
490 * Renders table cell with padding.
491 */
492 private function renderCell(array $row, int $column, string $cellFormat): string
493 {
494 $cell = $row[$column] ?? '';
495 $width = $this->effectiveColumnWidths[$column];
496 if ($cell instanceof TableCell && $cell->getColspan() > 1) {
497 // add the width of the following columns(numbers of colspan).
498 foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) {
499 $width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn];
500 }
501 }
502
503 // str_pad won't work properly with multi-byte strings, we need to fix the padding
504 if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
505 $width += \strlen($cell) - mb_strwidth($cell, $encoding);
506 }
507
508 $style = $this->getColumnStyle($column);
509
510 if ($cell instanceof TableSeparator) {
511 return sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width));
512 }
513
514 $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
515 $content = sprintf($style->getCellRowContentFormat(), $cell);
516
517 return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType()));
518 }
519
520 /**
521 * Calculate number of columns for this table.
522 */
523 private function calculateNumberOfColumns(array $rows)
524 {
525 $columns = [0];
526 foreach ($rows as $row) {
527 if ($row instanceof TableSeparator) {
528 continue;
529 }
530
531 $columns[] = $this->getNumberOfColumns($row);
532 }
533
534 $this->numberOfColumns = max($columns);
535 }
536
537 private function buildTableRows(array $rows): TableRows
538 {
539 /** @var WrappableOutputFormatterInterface $formatter */
540 $formatter = $this->output->getFormatter();
541 $unmergedRows = [];
542 for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) {
543 $rows = $this->fillNextRows($rows, $rowKey);
544
545 // Remove any new line breaks and replace it with a new line
546 foreach ($rows[$rowKey] as $column => $cell) {
547 $colspan = $cell instanceof TableCell ? $cell->getColspan() : 1;
548
549 if (isset($this->columnMaxWidths[$column]) && Helper::strlenWithoutDecoration($formatter, $cell) > $this->columnMaxWidths[$column]) {
550 $cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan);
551 }
552 if (!strstr($cell, "\n")) {
553 continue;
554 }
555 $escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell)));
556 $cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped;
557 $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
558 foreach ($lines as $lineKey => $line) {
559 if ($colspan > 1) {
560 $line = new TableCell($line, ['colspan' => $colspan]);
561 }
562 if (0 === $lineKey) {
563 $rows[$rowKey][$column] = $line;
564 } else {
565 if (!\array_key_exists($rowKey, $unmergedRows) || !\array_key_exists($lineKey, $unmergedRows[$rowKey])) {
566 $unmergedRows[$rowKey][$lineKey] = $this->copyRow($rows, $rowKey);
567 }
568 $unmergedRows[$rowKey][$lineKey][$column] = $line;
569 }
570 }
571 }
572 }
573
574 return new TableRows(function () use ($rows, $unmergedRows): \Traversable {
575 foreach ($rows as $rowKey => $row) {
576 yield $this->fillCells($row);
577
578 if (isset($unmergedRows[$rowKey])) {
579 foreach ($unmergedRows[$rowKey] as $unmergedRow) {
580 yield $this->fillCells($unmergedRow);
581 }
582 }
583 }
584 });
585 }
586
587 private function calculateRowCount(): int
588 {
589 $numberOfRows = \count(iterator_to_array($this->buildTableRows(array_merge($this->headers, [new TableSeparator()], $this->rows))));
590
591 if ($this->headers) {
592 ++$numberOfRows; // Add row for header separator
593 }
594
595 if (\count($this->rows) > 0) {
596 ++$numberOfRows; // Add row for footer separator
597 }
598
599 return $numberOfRows;
600 }
601
602 /**
603 * fill rows that contains rowspan > 1.
604 *
605 * @throws InvalidArgumentException
606 */
607 private function fillNextRows(array $rows, int $line): array
608 {
609 $unmergedRows = [];
610 foreach ($rows[$line] as $column => $cell) {
611 if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) {
612 throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type($cell)));
613 }
614 if ($cell instanceof TableCell && $cell->getRowspan() > 1) {
615 $nbLines = $cell->getRowspan() - 1;
616 $lines = [$cell];
617 if (strstr($cell, "\n")) {
618 $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
619 $nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines;
620
621 $rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan()]);
622 unset($lines[0]);
623 }
624
625 // create a two dimensional array (rowspan x colspan)
626 $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows);
627 foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
628 $value = $lines[$unmergedRowKey - $line] ?? '';
629 $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan()]);
630 if ($nbLines === $unmergedRowKey - $line) {
631 break;
632 }
633 }
634 }
635 }
636
637 foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
638 // we need to know if $unmergedRow will be merged or inserted into $rows
639 if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) {
640 foreach ($unmergedRow as $cellKey => $cell) {
641 // insert cell into row at cellKey position
642 array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]);
643 }
644 } else {
645 $row = $this->copyRow($rows, $unmergedRowKey - 1);
646 foreach ($unmergedRow as $column => $cell) {
647 if (!empty($cell)) {
648 $row[$column] = $unmergedRow[$column];
649 }
650 }
651 array_splice($rows, $unmergedRowKey, 0, [$row]);
652 }
653 }
654
655 return $rows;
656 }
657
658 /**
659 * fill cells for a row that contains colspan > 1.
660 */
661 private function fillCells($row)
662 {
663 $newRow = [];
664
665 foreach ($row as $column => $cell) {
666 $newRow[] = $cell;
667 if ($cell instanceof TableCell && $cell->getColspan() > 1) {
668 foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {
669 // insert empty value at column position
670 $newRow[] = '';
671 }
672 }
673 }
674
675 return $newRow ?: $row;
676 }
677
678 private function copyRow(array $rows, int $line): array
679 {
680 $row = $rows[$line];
681 foreach ($row as $cellKey => $cellValue) {
682 $row[$cellKey] = '';
683 if ($cellValue instanceof TableCell) {
684 $row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]);
685 }
686 }
687
688 return $row;
689 }
690
691 /**
692 * Gets number of columns by row.
693 */
694 private function getNumberOfColumns(array $row): int
695 {
696 $columns = \count($row);
697 foreach ($row as $column) {
698 $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0;
699 }
700
701 return $columns;
702 }
703
704 /**
705 * Gets list of columns for the given row.
706 */
707 private function getRowColumns(array $row): array
708 {
709 $columns = range(0, $this->numberOfColumns - 1);
710 foreach ($row as $cellKey => $cell) {
711 if ($cell instanceof TableCell && $cell->getColspan() > 1) {
712 // exclude grouped columns.
713 $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1));
714 }
715 }
716
717 return $columns;
718 }
719
720 /**
721 * Calculates columns widths.
722 */
723 private function calculateColumnsWidth(iterable $rows)
724 {
725 for ($column = 0; $column < $this->numberOfColumns; ++$column) {
726 $lengths = [];
727 foreach ($rows as $row) {
728 if ($row instanceof TableSeparator) {
729 continue;
730 }
731
732 foreach ($row as $i => $cell) {
733 if ($cell instanceof TableCell) {
734 $textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
735 $textLength = Helper::strlen($textContent);
736 if ($textLength > 0) {
737 $contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
738 foreach ($contentColumns as $position => $content) {
739 $row[$i + $position] = $content;
740 }
741 }
742 }
743 }
744
745 $lengths[] = $this->getCellWidth($row, $column);
746 }
747
748 $this->effectiveColumnWidths[$column] = max($lengths) + Helper::strlen($this->style->getCellRowContentFormat()) - 2;
749 }
750 }
751
752 private function getColumnSeparatorWidth(): int
753 {
754 return Helper::strlen(sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3]));
755 }
756
757 private function getCellWidth(array $row, int $column): int
758 {
759 $cellWidth = 0;
760
761 if (isset($row[$column])) {
762 $cell = $row[$column];
763 $cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
764 }
765
766 $columnWidth = $this->columnWidths[$column] ?? 0;
767 $cellWidth = max($cellWidth, $columnWidth);
768
769 return isset($this->columnMaxWidths[$column]) ? min($this->columnMaxWidths[$column], $cellWidth) : $cellWidth;
770 }
771
772 /**
773 * Called after rendering to cleanup cache data.
774 */
775 private function cleanup()
776 {
777 $this->effectiveColumnWidths = [];
778 $this->numberOfColumns = null;
779 }
780
781 private static function initStyles(): array
782 {
783 $borderless = new TableStyle();
784 $borderless
785 ->setHorizontalBorderChars('=')
786 ->setVerticalBorderChars(' ')
787 ->setDefaultCrossingChar(' ')
788 ;
789
790 $compact = new TableStyle();
791 $compact
792 ->setHorizontalBorderChars('')
793 ->setVerticalBorderChars(' ')
794 ->setDefaultCrossingChar('')
795 ->setCellRowContentFormat('%s')
796 ;
797
798 $styleGuide = new TableStyle();
799 $styleGuide
800 ->setHorizontalBorderChars('-')
801 ->setVerticalBorderChars(' ')
802 ->setDefaultCrossingChar(' ')
803 ->setCellHeaderFormat('%s')
804 ;
805
806 $box = (new TableStyle())
807 ->setHorizontalBorderChars('')
808 ->setVerticalBorderChars('')
809 ->setCrossingChars('', '', '', '', '', '', '', '', '')
810 ;
811
812 $boxDouble = (new TableStyle())
813 ->setHorizontalBorderChars('', '')
814 ->setVerticalBorderChars('', '')
815 ->setCrossingChars('', '', '', '', '', '', '', '', '', '', '', '')
816 ;
817
818 return [
819 'default' => new TableStyle(),
820 'borderless' => $borderless,
821 'compact' => $compact,
822 'symfony-style-guide' => $styleGuide,
823 'box' => $box,
824 'box-double' => $boxDouble,
825 ];
826 }
827
828 private function resolveStyle($name): TableStyle
829 {
830 if ($name instanceof TableStyle) {
831 return $name;
832 }
833
834 if (isset(self::$styles[$name])) {
835 return self::$styles[$name];
836 }
837
838 throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
839 }
840 }
841