[ Index ] |
PHP Cross Reference of phpBB-3.2.11-deutsch |
[Summary view] [Print] [Text view]
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\Output\OutputInterface; 16 17 /** 18 * Provides helpers to display a table. 19 * 20 * @author Fabien Potencier <fabien@symfony.com> 21 * @author Саша Стаменковић <umpirsky@gmail.com> 22 * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> 23 * @author Max Grigorian <maxakawizard@gmail.com> 24 */ 25 class Table 26 { 27 /** 28 * Table headers. 29 */ 30 private $headers = array(); 31 32 /** 33 * Table rows. 34 */ 35 private $rows = array(); 36 37 /** 38 * Column widths cache. 39 */ 40 private $columnWidths = array(); 41 42 /** 43 * Number of columns cache. 44 * 45 * @var int 46 */ 47 private $numberOfColumns; 48 49 /** 50 * @var OutputInterface 51 */ 52 private $output; 53 54 /** 55 * @var TableStyle 56 */ 57 private $style; 58 59 /** 60 * @var array 61 */ 62 private $columnStyles = array(); 63 64 private static $styles; 65 66 public function __construct(OutputInterface $output) 67 { 68 $this->output = $output; 69 70 if (!self::$styles) { 71 self::$styles = self::initStyles(); 72 } 73 74 $this->setStyle('default'); 75 } 76 77 /** 78 * Sets a style definition. 79 * 80 * @param string $name The style name 81 * @param TableStyle $style A TableStyle instance 82 */ 83 public static function setStyleDefinition($name, TableStyle $style) 84 { 85 if (!self::$styles) { 86 self::$styles = self::initStyles(); 87 } 88 89 self::$styles[$name] = $style; 90 } 91 92 /** 93 * Gets a style definition by name. 94 * 95 * @param string $name The style name 96 * 97 * @return TableStyle 98 */ 99 public static function getStyleDefinition($name) 100 { 101 if (!self::$styles) { 102 self::$styles = self::initStyles(); 103 } 104 105 if (isset(self::$styles[$name])) { 106 return self::$styles[$name]; 107 } 108 109 throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); 110 } 111 112 /** 113 * Sets table style. 114 * 115 * @param TableStyle|string $name The style name or a TableStyle instance 116 * 117 * @return $this 118 */ 119 public function setStyle($name) 120 { 121 $this->style = $this->resolveStyle($name); 122 123 return $this; 124 } 125 126 /** 127 * Gets the current table style. 128 * 129 * @return TableStyle 130 */ 131 public function getStyle() 132 { 133 return $this->style; 134 } 135 136 /** 137 * Sets table column style. 138 * 139 * @param int $columnIndex Column index 140 * @param TableStyle|string $name The style name or a TableStyle instance 141 * 142 * @return $this 143 */ 144 public function setColumnStyle($columnIndex, $name) 145 { 146 $columnIndex = (int) $columnIndex; 147 148 $this->columnStyles[$columnIndex] = $this->resolveStyle($name); 149 150 return $this; 151 } 152 153 /** 154 * Gets the current style for a column. 155 * 156 * If style was not set, it returns the global table style. 157 * 158 * @param int $columnIndex Column index 159 * 160 * @return TableStyle 161 */ 162 public function getColumnStyle($columnIndex) 163 { 164 if (isset($this->columnStyles[$columnIndex])) { 165 return $this->columnStyles[$columnIndex]; 166 } 167 168 return $this->getStyle(); 169 } 170 171 public function setHeaders(array $headers) 172 { 173 $headers = array_values($headers); 174 if (!empty($headers) && !\is_array($headers[0])) { 175 $headers = array($headers); 176 } 177 178 $this->headers = $headers; 179 180 return $this; 181 } 182 183 public function setRows(array $rows) 184 { 185 $this->rows = array(); 186 187 return $this->addRows($rows); 188 } 189 190 public function addRows(array $rows) 191 { 192 foreach ($rows as $row) { 193 $this->addRow($row); 194 } 195 196 return $this; 197 } 198 199 public function addRow($row) 200 { 201 if ($row instanceof TableSeparator) { 202 $this->rows[] = $row; 203 204 return $this; 205 } 206 207 if (!\is_array($row)) { 208 throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.'); 209 } 210 211 $this->rows[] = array_values($row); 212 213 return $this; 214 } 215 216 public function setRow($column, array $row) 217 { 218 $this->rows[$column] = $row; 219 220 return $this; 221 } 222 223 /** 224 * Renders table to output. 225 * 226 * Example: 227 * 228 * +---------------+-----------------------+------------------+ 229 * | ISBN | Title | Author | 230 * +---------------+-----------------------+------------------+ 231 * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 232 * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 233 * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | 234 * +---------------+-----------------------+------------------+ 235 */ 236 public function render() 237 { 238 $this->calculateNumberOfColumns(); 239 $rows = $this->buildTableRows($this->rows); 240 $headers = $this->buildTableRows($this->headers); 241 242 $this->calculateColumnsWidth(array_merge($headers, $rows)); 243 244 $this->renderRowSeparator(); 245 if (!empty($headers)) { 246 foreach ($headers as $header) { 247 $this->renderRow($header, $this->style->getCellHeaderFormat()); 248 $this->renderRowSeparator(); 249 } 250 } 251 foreach ($rows as $row) { 252 if ($row instanceof TableSeparator) { 253 $this->renderRowSeparator(); 254 } else { 255 $this->renderRow($row, $this->style->getCellRowFormat()); 256 } 257 } 258 if (!empty($rows)) { 259 $this->renderRowSeparator(); 260 } 261 262 $this->cleanup(); 263 } 264 265 /** 266 * Renders horizontal header separator. 267 * 268 * Example: 269 * 270 * +-----+-----------+-------+ 271 */ 272 private function renderRowSeparator() 273 { 274 if (0 === $count = $this->numberOfColumns) { 275 return; 276 } 277 278 if (!$this->style->getHorizontalBorderChar() && !$this->style->getCrossingChar()) { 279 return; 280 } 281 282 $markup = $this->style->getCrossingChar(); 283 for ($column = 0; $column < $count; ++$column) { 284 $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->columnWidths[$column]).$this->style->getCrossingChar(); 285 } 286 287 $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup)); 288 } 289 290 /** 291 * Renders vertical column separator. 292 */ 293 private function renderColumnSeparator() 294 { 295 return sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()); 296 } 297 298 /** 299 * Renders table row. 300 * 301 * Example: 302 * 303 * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 304 * 305 * @param array $row 306 * @param string $cellFormat 307 */ 308 private function renderRow(array $row, $cellFormat) 309 { 310 if (empty($row)) { 311 return; 312 } 313 314 $rowContent = $this->renderColumnSeparator(); 315 foreach ($this->getRowColumns($row) as $column) { 316 $rowContent .= $this->renderCell($row, $column, $cellFormat); 317 $rowContent .= $this->renderColumnSeparator(); 318 } 319 $this->output->writeln($rowContent); 320 } 321 322 /** 323 * Renders table cell with padding. 324 * 325 * @param array $row 326 * @param int $column 327 * @param string $cellFormat 328 */ 329 private function renderCell(array $row, $column, $cellFormat) 330 { 331 $cell = isset($row[$column]) ? $row[$column] : ''; 332 $width = $this->columnWidths[$column]; 333 if ($cell instanceof TableCell && $cell->getColspan() > 1) { 334 // add the width of the following columns(numbers of colspan). 335 foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) { 336 $width += $this->getColumnSeparatorWidth() + $this->columnWidths[$nextColumn]; 337 } 338 } 339 340 // str_pad won't work properly with multi-byte strings, we need to fix the padding 341 if (false !== $encoding = mb_detect_encoding($cell, null, true)) { 342 $width += \strlen($cell) - mb_strwidth($cell, $encoding); 343 } 344 345 $style = $this->getColumnStyle($column); 346 347 if ($cell instanceof TableSeparator) { 348 return sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width)); 349 } 350 351 $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); 352 $content = sprintf($style->getCellRowContentFormat(), $cell); 353 354 return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType())); 355 } 356 357 /** 358 * Calculate number of columns for this table. 359 */ 360 private function calculateNumberOfColumns() 361 { 362 if (null !== $this->numberOfColumns) { 363 return; 364 } 365 366 $columns = array(0); 367 foreach (array_merge($this->headers, $this->rows) as $row) { 368 if ($row instanceof TableSeparator) { 369 continue; 370 } 371 372 $columns[] = $this->getNumberOfColumns($row); 373 } 374 375 $this->numberOfColumns = max($columns); 376 } 377 378 private function buildTableRows($rows) 379 { 380 $unmergedRows = array(); 381 for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) { 382 $rows = $this->fillNextRows($rows, $rowKey); 383 384 // Remove any new line breaks and replace it with a new line 385 foreach ($rows[$rowKey] as $column => $cell) { 386 if (!strstr($cell, "\n")) { 387 continue; 388 } 389 $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell)); 390 foreach ($lines as $lineKey => $line) { 391 if ($cell instanceof TableCell) { 392 $line = new TableCell($line, array('colspan' => $cell->getColspan())); 393 } 394 if (0 === $lineKey) { 395 $rows[$rowKey][$column] = $line; 396 } else { 397 $unmergedRows[$rowKey][$lineKey][$column] = $line; 398 } 399 } 400 } 401 } 402 403 $tableRows = array(); 404 foreach ($rows as $rowKey => $row) { 405 $tableRows[] = $this->fillCells($row); 406 if (isset($unmergedRows[$rowKey])) { 407 $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]); 408 } 409 } 410 411 return $tableRows; 412 } 413 414 /** 415 * fill rows that contains rowspan > 1. 416 * 417 * @param array $rows 418 * @param int $line 419 * 420 * @return array 421 */ 422 private function fillNextRows(array $rows, $line) 423 { 424 $unmergedRows = array(); 425 foreach ($rows[$line] as $column => $cell) { 426 if ($cell instanceof TableCell && $cell->getRowspan() > 1) { 427 $nbLines = $cell->getRowspan() - 1; 428 $lines = array($cell); 429 if (strstr($cell, "\n")) { 430 $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell)); 431 $nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; 432 433 $rows[$line][$column] = new TableCell($lines[0], array('colspan' => $cell->getColspan())); 434 unset($lines[0]); 435 } 436 437 // create a two dimensional array (rowspan x colspan) 438 $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, array()), $unmergedRows); 439 foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { 440 $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : ''; 441 $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan())); 442 if ($nbLines === $unmergedRowKey - $line) { 443 break; 444 } 445 } 446 } 447 } 448 449 foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { 450 // we need to know if $unmergedRow will be merged or inserted into $rows 451 if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { 452 foreach ($unmergedRow as $cellKey => $cell) { 453 // insert cell into row at cellKey position 454 array_splice($rows[$unmergedRowKey], $cellKey, 0, array($cell)); 455 } 456 } else { 457 $row = $this->copyRow($rows, $unmergedRowKey - 1); 458 foreach ($unmergedRow as $column => $cell) { 459 if (!empty($cell)) { 460 $row[$column] = $unmergedRow[$column]; 461 } 462 } 463 array_splice($rows, $unmergedRowKey, 0, array($row)); 464 } 465 } 466 467 return $rows; 468 } 469 470 /** 471 * fill cells for a row that contains colspan > 1. 472 * 473 * @return array 474 */ 475 private function fillCells($row) 476 { 477 $newRow = array(); 478 foreach ($row as $column => $cell) { 479 $newRow[] = $cell; 480 if ($cell instanceof TableCell && $cell->getColspan() > 1) { 481 foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) { 482 // insert empty value at column position 483 $newRow[] = ''; 484 } 485 } 486 } 487 488 return $newRow ?: $row; 489 } 490 491 /** 492 * @param array $rows 493 * @param int $line 494 * 495 * @return array 496 */ 497 private function copyRow(array $rows, $line) 498 { 499 $row = $rows[$line]; 500 foreach ($row as $cellKey => $cellValue) { 501 $row[$cellKey] = ''; 502 if ($cellValue instanceof TableCell) { 503 $row[$cellKey] = new TableCell('', array('colspan' => $cellValue->getColspan())); 504 } 505 } 506 507 return $row; 508 } 509 510 /** 511 * Gets number of columns by row. 512 * 513 * @return int 514 */ 515 private function getNumberOfColumns(array $row) 516 { 517 $columns = \count($row); 518 foreach ($row as $column) { 519 $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0; 520 } 521 522 return $columns; 523 } 524 525 /** 526 * Gets list of columns for the given row. 527 * 528 * @return array 529 */ 530 private function getRowColumns(array $row) 531 { 532 $columns = range(0, $this->numberOfColumns - 1); 533 foreach ($row as $cellKey => $cell) { 534 if ($cell instanceof TableCell && $cell->getColspan() > 1) { 535 // exclude grouped columns. 536 $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1)); 537 } 538 } 539 540 return $columns; 541 } 542 543 /** 544 * Calculates columns widths. 545 * 546 * @param array $rows 547 */ 548 private function calculateColumnsWidth($rows) 549 { 550 for ($column = 0; $column < $this->numberOfColumns; ++$column) { 551 $lengths = array(); 552 foreach ($rows as $row) { 553 if ($row instanceof TableSeparator) { 554 continue; 555 } 556 557 foreach ($row as $i => $cell) { 558 if ($cell instanceof TableCell) { 559 $textContent = Helper::removeDecoration($this->output->getFormatter(), $cell); 560 $textLength = Helper::strlen($textContent); 561 if ($textLength > 0) { 562 $contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan())); 563 foreach ($contentColumns as $position => $content) { 564 $row[$i + $position] = $content; 565 } 566 } 567 } 568 } 569 570 $lengths[] = $this->getCellWidth($row, $column); 571 } 572 573 $this->columnWidths[$column] = max($lengths) + Helper::strlen($this->style->getCellRowContentFormat()) - 2; 574 } 575 } 576 577 /** 578 * Gets column width. 579 * 580 * @return int 581 */ 582 private function getColumnSeparatorWidth() 583 { 584 return Helper::strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); 585 } 586 587 /** 588 * Gets cell width. 589 * 590 * @param array $row 591 * @param int $column 592 * 593 * @return int 594 */ 595 private function getCellWidth(array $row, $column) 596 { 597 if (isset($row[$column])) { 598 $cell = $row[$column]; 599 $cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); 600 601 return $cellWidth; 602 } 603 604 return 0; 605 } 606 607 /** 608 * Called after rendering to cleanup cache data. 609 */ 610 private function cleanup() 611 { 612 $this->columnWidths = array(); 613 $this->numberOfColumns = null; 614 } 615 616 private static function initStyles() 617 { 618 $borderless = new TableStyle(); 619 $borderless 620 ->setHorizontalBorderChar('=') 621 ->setVerticalBorderChar(' ') 622 ->setCrossingChar(' ') 623 ; 624 625 $compact = new TableStyle(); 626 $compact 627 ->setHorizontalBorderChar('') 628 ->setVerticalBorderChar(' ') 629 ->setCrossingChar('') 630 ->setCellRowContentFormat('%s') 631 ; 632 633 $styleGuide = new TableStyle(); 634 $styleGuide 635 ->setHorizontalBorderChar('-') 636 ->setVerticalBorderChar(' ') 637 ->setCrossingChar(' ') 638 ->setCellHeaderFormat('%s') 639 ; 640 641 return array( 642 'default' => new TableStyle(), 643 'borderless' => $borderless, 644 'compact' => $compact, 645 'symfony-style-guide' => $styleGuide, 646 ); 647 } 648 649 private function resolveStyle($name) 650 { 651 if ($name instanceof TableStyle) { 652 return $name; 653 } 654 655 if (isset(self::$styles[$name])) { 656 return self::$styles[$name]; 657 } 658 659 throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); 660 } 661 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |