comparison vendor/symfony/console/Helper/Table.php @ 17:129ea1e6d783

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:21:36 +0000
parents 1fec387a4317
children
comparison
equal deleted inserted replaced
16:c2387f117808 17:129ea1e6d783
9 * file that was distributed with this source code. 9 * file that was distributed with this source code.
10 */ 10 */
11 11
12 namespace Symfony\Component\Console\Helper; 12 namespace Symfony\Component\Console\Helper;
13 13
14 use Symfony\Component\Console\Exception\InvalidArgumentException;
14 use Symfony\Component\Console\Output\OutputInterface; 15 use Symfony\Component\Console\Output\OutputInterface;
15 use Symfony\Component\Console\Exception\InvalidArgumentException;
16 16
17 /** 17 /**
18 * Provides helpers to display a table. 18 * Provides helpers to display a table.
19 * 19 *
20 * @author Fabien Potencier <fabien@symfony.com> 20 * @author Fabien Potencier <fabien@symfony.com>
25 class Table 25 class Table
26 { 26 {
27 /** 27 /**
28 * Table headers. 28 * Table headers.
29 */ 29 */
30 private $headers = array(); 30 private $headers = [];
31 31
32 /** 32 /**
33 * Table rows. 33 * Table rows.
34 */ 34 */
35 private $rows = array(); 35 private $rows = [];
36 36
37 /** 37 /**
38 * Column widths cache. 38 * Column widths cache.
39 */ 39 */
40 private $effectiveColumnWidths = array(); 40 private $effectiveColumnWidths = [];
41 41
42 /** 42 /**
43 * Number of columns cache. 43 * Number of columns cache.
44 * 44 *
45 * @var int 45 * @var int
57 private $style; 57 private $style;
58 58
59 /** 59 /**
60 * @var array 60 * @var array
61 */ 61 */
62 private $columnStyles = array(); 62 private $columnStyles = [];
63 63
64 /** 64 /**
65 * User set column widths. 65 * User set column widths.
66 * 66 *
67 * @var array 67 * @var array
68 */ 68 */
69 private $columnWidths = array(); 69 private $columnWidths = [];
70 70
71 private static $styles; 71 private static $styles;
72 72
73 public function __construct(OutputInterface $output) 73 public function __construct(OutputInterface $output)
74 { 74 {
197 * 197 *
198 * @return $this 198 * @return $this
199 */ 199 */
200 public function setColumnWidths(array $widths) 200 public function setColumnWidths(array $widths)
201 { 201 {
202 $this->columnWidths = array(); 202 $this->columnWidths = [];
203 foreach ($widths as $index => $width) { 203 foreach ($widths as $index => $width) {
204 $this->setColumnWidth($index, $width); 204 $this->setColumnWidth($index, $width);
205 } 205 }
206 206
207 return $this; 207 return $this;
208 } 208 }
209 209
210 public function setHeaders(array $headers) 210 public function setHeaders(array $headers)
211 { 211 {
212 $headers = array_values($headers); 212 $headers = array_values($headers);
213 if (!empty($headers) && !is_array($headers[0])) { 213 if (!empty($headers) && !\is_array($headers[0])) {
214 $headers = array($headers); 214 $headers = [$headers];
215 } 215 }
216 216
217 $this->headers = $headers; 217 $this->headers = $headers;
218 218
219 return $this; 219 return $this;
220 } 220 }
221 221
222 public function setRows(array $rows) 222 public function setRows(array $rows)
223 { 223 {
224 $this->rows = array(); 224 $this->rows = [];
225 225
226 return $this->addRows($rows); 226 return $this->addRows($rows);
227 } 227 }
228 228
229 public function addRows(array $rows) 229 public function addRows(array $rows)
241 $this->rows[] = $row; 241 $this->rows[] = $row;
242 242
243 return $this; 243 return $this;
244 } 244 }
245 245
246 if (!is_array($row)) { 246 if (!\is_array($row)) {
247 throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.'); 247 throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.');
248 } 248 }
249 249
250 $this->rows[] = array_values($row); 250 $this->rows[] = array_values($row);
251 251
261 261
262 /** 262 /**
263 * Renders table to output. 263 * Renders table to output.
264 * 264 *
265 * Example: 265 * Example:
266 * <code> 266 *
267 * +---------------+-----------------------+------------------+ 267 * +---------------+-----------------------+------------------+
268 * | ISBN | Title | Author | 268 * | ISBN | Title | Author |
269 * +---------------+-----------------------+------------------+ 269 * +---------------+-----------------------+------------------+
270 * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 270 * | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
271 * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 271 * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
272 * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | 272 * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
273 * +---------------+-----------------------+------------------+ 273 * +---------------+-----------------------+------------------+
274 * </code>
275 */ 274 */
276 public function render() 275 public function render()
277 { 276 {
278 $this->calculateNumberOfColumns(); 277 $this->calculateNumberOfColumns();
279 $rows = $this->buildTableRows($this->rows); 278 $rows = $this->buildTableRows($this->rows);
303 } 302 }
304 303
305 /** 304 /**
306 * Renders horizontal header separator. 305 * Renders horizontal header separator.
307 * 306 *
308 * Example: <code>+-----+-----------+-------+</code> 307 * Example:
308 *
309 * +-----+-----------+-------+
309 */ 310 */
310 private function renderRowSeparator() 311 private function renderRowSeparator()
311 { 312 {
312 if (0 === $count = $this->numberOfColumns) { 313 if (0 === $count = $this->numberOfColumns) {
313 return; 314 return;
334 } 335 }
335 336
336 /** 337 /**
337 * Renders table row. 338 * Renders table row.
338 * 339 *
339 * Example: <code>| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |</code> 340 * Example:
341 *
342 * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
340 * 343 *
341 * @param array $row 344 * @param array $row
342 * @param string $cellFormat 345 * @param string $cellFormat
343 */ 346 */
344 private function renderRow(array $row, $cellFormat) 347 private function renderRow(array $row, $cellFormat)
373 } 376 }
374 } 377 }
375 378
376 // str_pad won't work properly with multi-byte strings, we need to fix the padding 379 // str_pad won't work properly with multi-byte strings, we need to fix the padding
377 if (false !== $encoding = mb_detect_encoding($cell, null, true)) { 380 if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
378 $width += strlen($cell) - mb_strwidth($cell, $encoding); 381 $width += \strlen($cell) - mb_strwidth($cell, $encoding);
379 } 382 }
380 383
381 $style = $this->getColumnStyle($column); 384 $style = $this->getColumnStyle($column);
382 385
383 if ($cell instanceof TableSeparator) { 386 if ($cell instanceof TableSeparator) {
397 { 400 {
398 if (null !== $this->numberOfColumns) { 401 if (null !== $this->numberOfColumns) {
399 return; 402 return;
400 } 403 }
401 404
402 $columns = array(0); 405 $columns = [0];
403 foreach (array_merge($this->headers, $this->rows) as $row) { 406 foreach (array_merge($this->headers, $this->rows) as $row) {
404 if ($row instanceof TableSeparator) { 407 if ($row instanceof TableSeparator) {
405 continue; 408 continue;
406 } 409 }
407 410
411 $this->numberOfColumns = max($columns); 414 $this->numberOfColumns = max($columns);
412 } 415 }
413 416
414 private function buildTableRows($rows) 417 private function buildTableRows($rows)
415 { 418 {
416 $unmergedRows = array(); 419 $unmergedRows = [];
417 for ($rowKey = 0; $rowKey < count($rows); ++$rowKey) { 420 for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) {
418 $rows = $this->fillNextRows($rows, $rowKey); 421 $rows = $this->fillNextRows($rows, $rowKey);
419 422
420 // Remove any new line breaks and replace it with a new line 423 // Remove any new line breaks and replace it with a new line
421 foreach ($rows[$rowKey] as $column => $cell) { 424 foreach ($rows[$rowKey] as $column => $cell) {
422 if (!strstr($cell, "\n")) { 425 if (!strstr($cell, "\n")) {
423 continue; 426 continue;
424 } 427 }
425 $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell)); 428 $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
426 foreach ($lines as $lineKey => $line) { 429 foreach ($lines as $lineKey => $line) {
427 if ($cell instanceof TableCell) { 430 if ($cell instanceof TableCell) {
428 $line = new TableCell($line, array('colspan' => $cell->getColspan())); 431 $line = new TableCell($line, ['colspan' => $cell->getColspan()]);
429 } 432 }
430 if (0 === $lineKey) { 433 if (0 === $lineKey) {
431 $rows[$rowKey][$column] = $line; 434 $rows[$rowKey][$column] = $line;
432 } else { 435 } else {
433 $unmergedRows[$rowKey][$lineKey][$column] = $line; 436 $unmergedRows[$rowKey][$lineKey][$column] = $line;
434 } 437 }
435 } 438 }
436 } 439 }
437 } 440 }
438 441
439 $tableRows = array(); 442 $tableRows = [];
440 foreach ($rows as $rowKey => $row) { 443 foreach ($rows as $rowKey => $row) {
441 $tableRows[] = $this->fillCells($row); 444 $tableRows[] = $this->fillCells($row);
442 if (isset($unmergedRows[$rowKey])) { 445 if (isset($unmergedRows[$rowKey])) {
443 $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]); 446 $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]);
444 } 447 }
457 * 460 *
458 * @throws InvalidArgumentException 461 * @throws InvalidArgumentException
459 */ 462 */
460 private function fillNextRows(array $rows, $line) 463 private function fillNextRows(array $rows, $line)
461 { 464 {
462 $unmergedRows = array(); 465 $unmergedRows = [];
463 foreach ($rows[$line] as $column => $cell) { 466 foreach ($rows[$line] as $column => $cell) {
464 if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(is_object($cell) && method_exists($cell, '__toString'))) { 467 if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) {
465 throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing __toString, %s given.', gettype($cell))); 468 throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing __toString, %s given.', \gettype($cell)));
466 } 469 }
467 if ($cell instanceof TableCell && $cell->getRowspan() > 1) { 470 if ($cell instanceof TableCell && $cell->getRowspan() > 1) {
468 $nbLines = $cell->getRowspan() - 1; 471 $nbLines = $cell->getRowspan() - 1;
469 $lines = array($cell); 472 $lines = [$cell];
470 if (strstr($cell, "\n")) { 473 if (strstr($cell, "\n")) {
471 $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell)); 474 $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
472 $nbLines = count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; 475 $nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines;
473 476
474 $rows[$line][$column] = new TableCell($lines[0], array('colspan' => $cell->getColspan())); 477 $rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan()]);
475 unset($lines[0]); 478 unset($lines[0]);
476 } 479 }
477 480
478 // create a two dimensional array (rowspan x colspan) 481 // create a two dimensional array (rowspan x colspan)
479 $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, array()), $unmergedRows); 482 $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows);
480 foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { 483 foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
481 $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : ''; 484 $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : '';
482 $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan())); 485 $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan()]);
483 if ($nbLines === $unmergedRowKey - $line) { 486 if ($nbLines === $unmergedRowKey - $line) {
484 break; 487 break;
485 } 488 }
486 } 489 }
487 } 490 }
488 } 491 }
489 492
490 foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { 493 foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
491 // we need to know if $unmergedRow will be merged or inserted into $rows 494 // we need to know if $unmergedRow will be merged or inserted into $rows
492 if (isset($rows[$unmergedRowKey]) && is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { 495 if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) {
493 foreach ($unmergedRow as $cellKey => $cell) { 496 foreach ($unmergedRow as $cellKey => $cell) {
494 // insert cell into row at cellKey position 497 // insert cell into row at cellKey position
495 array_splice($rows[$unmergedRowKey], $cellKey, 0, array($cell)); 498 array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]);
496 } 499 }
497 } else { 500 } else {
498 $row = $this->copyRow($rows, $unmergedRowKey - 1); 501 $row = $this->copyRow($rows, $unmergedRowKey - 1);
499 foreach ($unmergedRow as $column => $cell) { 502 foreach ($unmergedRow as $column => $cell) {
500 if (!empty($cell)) { 503 if (!empty($cell)) {
501 $row[$column] = $unmergedRow[$column]; 504 $row[$column] = $unmergedRow[$column];
502 } 505 }
503 } 506 }
504 array_splice($rows, $unmergedRowKey, 0, array($row)); 507 array_splice($rows, $unmergedRowKey, 0, [$row]);
505 } 508 }
506 } 509 }
507 510
508 return $rows; 511 return $rows;
509 } 512 }
513 * 516 *
514 * @return array 517 * @return array
515 */ 518 */
516 private function fillCells($row) 519 private function fillCells($row)
517 { 520 {
518 $newRow = array(); 521 $newRow = [];
519 foreach ($row as $column => $cell) { 522 foreach ($row as $column => $cell) {
520 $newRow[] = $cell; 523 $newRow[] = $cell;
521 if ($cell instanceof TableCell && $cell->getColspan() > 1) { 524 if ($cell instanceof TableCell && $cell->getColspan() > 1) {
522 foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) { 525 foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {
523 // insert empty value at column position 526 // insert empty value at column position
539 { 542 {
540 $row = $rows[$line]; 543 $row = $rows[$line];
541 foreach ($row as $cellKey => $cellValue) { 544 foreach ($row as $cellKey => $cellValue) {
542 $row[$cellKey] = ''; 545 $row[$cellKey] = '';
543 if ($cellValue instanceof TableCell) { 546 if ($cellValue instanceof TableCell) {
544 $row[$cellKey] = new TableCell('', array('colspan' => $cellValue->getColspan())); 547 $row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]);
545 } 548 }
546 } 549 }
547 550
548 return $row; 551 return $row;
549 } 552 }
553 * 556 *
554 * @return int 557 * @return int
555 */ 558 */
556 private function getNumberOfColumns(array $row) 559 private function getNumberOfColumns(array $row)
557 { 560 {
558 $columns = count($row); 561 $columns = \count($row);
559 foreach ($row as $column) { 562 foreach ($row as $column) {
560 $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0; 563 $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0;
561 } 564 }
562 565
563 return $columns; 566 return $columns;
585 * Calculates columns widths. 588 * Calculates columns widths.
586 */ 589 */
587 private function calculateColumnsWidth(array $rows) 590 private function calculateColumnsWidth(array $rows)
588 { 591 {
589 for ($column = 0; $column < $this->numberOfColumns; ++$column) { 592 for ($column = 0; $column < $this->numberOfColumns; ++$column) {
590 $lengths = array(); 593 $lengths = [];
591 foreach ($rows as $row) { 594 foreach ($rows as $row) {
592 if ($row instanceof TableSeparator) { 595 if ($row instanceof TableSeparator) {
593 continue; 596 continue;
594 } 597 }
595 598
607 } 610 }
608 611
609 $lengths[] = $this->getCellWidth($row, $column); 612 $lengths[] = $this->getCellWidth($row, $column);
610 } 613 }
611 614
612 $this->effectiveColumnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2; 615 $this->effectiveColumnWidths[$column] = max($lengths) + Helper::strlen($this->style->getCellRowContentFormat()) - 2;
613 } 616 }
614 } 617 }
615 618
616 /** 619 /**
617 * Gets column width. 620 * Gets column width.
618 * 621 *
619 * @return int 622 * @return int
620 */ 623 */
621 private function getColumnSeparatorWidth() 624 private function getColumnSeparatorWidth()
622 { 625 {
623 return strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); 626 return Helper::strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()));
624 } 627 }
625 628
626 /** 629 /**
627 * Gets cell width. 630 * Gets cell width.
628 * 631 *
648 /** 651 /**
649 * Called after rendering to cleanup cache data. 652 * Called after rendering to cleanup cache data.
650 */ 653 */
651 private function cleanup() 654 private function cleanup()
652 { 655 {
653 $this->effectiveColumnWidths = array(); 656 $this->effectiveColumnWidths = [];
654 $this->numberOfColumns = null; 657 $this->numberOfColumns = null;
655 } 658 }
656 659
657 private static function initStyles() 660 private static function initStyles()
658 { 661 {
677 ->setVerticalBorderChar(' ') 680 ->setVerticalBorderChar(' ')
678 ->setCrossingChar(' ') 681 ->setCrossingChar(' ')
679 ->setCellHeaderFormat('%s') 682 ->setCellHeaderFormat('%s')
680 ; 683 ;
681 684
682 return array( 685 return [
683 'default' => new TableStyle(), 686 'default' => new TableStyle(),
684 'borderless' => $borderless, 687 'borderless' => $borderless,
685 'compact' => $compact, 688 'compact' => $compact,
686 'symfony-style-guide' => $styleGuide, 689 'symfony-style-guide' => $styleGuide,
687 ); 690 ];
688 } 691 }
689 692
690 private function resolveStyle($name) 693 private function resolveStyle($name)
691 { 694 {
692 if ($name instanceof TableStyle) { 695 if ($name instanceof TableStyle) {