Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Table.php

Zuletzt modifiziert: 09.10.2024, 12:56 - Dateigröße: 18.14 KiB


 001  <?php
 002   
 003  /*
 004   * This file is part of the Symfony package.
 005   *
 006   * (c) Fabien Potencier <fabien@symfony.com>
 007   *
 008   * For the full copyright and license information, please view the LICENSE
 009   * file that was distributed with this source code.
 010   */
 011   
 012  namespace Symfony\Component\Console\Helper;
 013   
 014  use Symfony\Component\Console\Output\OutputInterface;
 015  use Symfony\Component\Console\Exception\InvalidArgumentException;
 016   
 017  /**
 018   * Provides helpers to display a table.
 019   *
 020   * @author Fabien Potencier <fabien@symfony.com>
 021   * @author Саша Стаменковић <umpirsky@gmail.com>
 022   * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
 023   * @author Max Grigorian <maxakawizard@gmail.com>
 024   */
 025  class Table
 026  {
 027      /**
 028       * Table headers.
 029       *
 030       * @var array
 031       */
 032      private $headers = array();
 033   
 034      /**
 035       * Table rows.
 036       *
 037       * @var array
 038       */
 039      private $rows = array();
 040   
 041      /**
 042       * Column widths cache.
 043       *
 044       * @var array
 045       */
 046      private $columnWidths = array();
 047   
 048      /**
 049       * Number of columns cache.
 050       *
 051       * @var array
 052       */
 053      private $numberOfColumns;
 054   
 055      /**
 056       * @var OutputInterface
 057       */
 058      private $output;
 059   
 060      /**
 061       * @var TableStyle
 062       */
 063      private $style;
 064   
 065      /**
 066       * @var array
 067       */
 068      private $columnStyles = array();
 069   
 070      private static $styles;
 071   
 072      public function __construct(OutputInterface $output)
 073      {
 074          $this->output = $output;
 075   
 076          if (!self::$styles) {
 077              self::$styles = self::initStyles();
 078          }
 079   
 080          $this->setStyle('default');
 081      }
 082   
 083      /**
 084       * Sets a style definition.
 085       *
 086       * @param string     $name  The style name
 087       * @param TableStyle $style A TableStyle instance
 088       */
 089      public static function setStyleDefinition($name, TableStyle $style)
 090      {
 091          if (!self::$styles) {
 092              self::$styles = self::initStyles();
 093          }
 094   
 095          self::$styles[$name] = $style;
 096      }
 097   
 098      /**
 099       * Gets a style definition by name.
 100       *
 101       * @param string $name The style name
 102       *
 103       * @return TableStyle A TableStyle instance
 104       */
 105      public static function getStyleDefinition($name)
 106      {
 107          if (!self::$styles) {
 108              self::$styles = self::initStyles();
 109          }
 110   
 111          if (isset(self::$styles[$name])) {
 112              return self::$styles[$name];
 113          }
 114   
 115          throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
 116      }
 117   
 118      /**
 119       * Sets table style.
 120       *
 121       * @param TableStyle|string $name The style name or a TableStyle instance
 122       *
 123       * @return Table
 124       */
 125      public function setStyle($name)
 126      {
 127          $this->style = $this->resolveStyle($name);
 128   
 129          return $this;
 130      }
 131   
 132      /**
 133       * Gets the current table style.
 134       *
 135       * @return TableStyle
 136       */
 137      public function getStyle()
 138      {
 139          return $this->style;
 140      }
 141   
 142      /**
 143       * Sets table column style.
 144       *
 145       * @param int               $columnIndex Column index
 146       * @param TableStyle|string $name        The style name or a TableStyle instance
 147       *
 148       * @return Table
 149       */
 150      public function setColumnStyle($columnIndex, $name)
 151      {
 152          $columnIndex = intval($columnIndex);
 153   
 154          $this->columnStyles[$columnIndex] = $this->resolveStyle($name);
 155   
 156          return $this;
 157      }
 158   
 159      /**
 160       * Gets the current style for a column.
 161       *
 162       * If style was not set, it returns the global table style.
 163       *
 164       * @param int $columnIndex Column index
 165       *
 166       * @return TableStyle
 167       */
 168      public function getColumnStyle($columnIndex)
 169      {
 170          if (isset($this->columnStyles[$columnIndex])) {
 171              return $this->columnStyles[$columnIndex];
 172          }
 173   
 174          return $this->getStyle();
 175      }
 176   
 177      public function setHeaders(array $headers)
 178      {
 179          $headers = array_values($headers);
 180          if (!empty($headers) && !is_array($headers[0])) {
 181              $headers = array($headers);
 182          }
 183   
 184          $this->headers = $headers;
 185   
 186          return $this;
 187      }
 188   
 189      public function setRows(array $rows)
 190      {
 191          $this->rows = array();
 192   
 193          return $this->addRows($rows);
 194      }
 195   
 196      public function addRows(array $rows)
 197      {
 198          foreach ($rows as $row) {
 199              $this->addRow($row);
 200          }
 201   
 202          return $this;
 203      }
 204   
 205      public function addRow($row)
 206      {
 207          if ($row instanceof TableSeparator) {
 208              $this->rows[] = $row;
 209   
 210              return $this;
 211          }
 212   
 213          if (!is_array($row)) {
 214              throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.');
 215          }
 216   
 217          $this->rows[] = array_values($row);
 218   
 219          return $this;
 220      }
 221   
 222      public function setRow($column, array $row)
 223      {
 224          $this->rows[$column] = $row;
 225   
 226          return $this;
 227      }
 228   
 229      /**
 230       * Renders table to output.
 231       *
 232       * Example:
 233       * +---------------+-----------------------+------------------+
 234       * | ISBN          | Title                 | Author           |
 235       * +---------------+-----------------------+------------------+
 236       * | 99921-58-10-7 | Divine Comedy         | Dante Alighieri  |
 237       * | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
 238       * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
 239       * +---------------+-----------------------+------------------+
 240       */
 241      public function render()
 242      {
 243          $this->calculateNumberOfColumns();
 244          $rows = $this->buildTableRows($this->rows);
 245          $headers = $this->buildTableRows($this->headers);
 246   
 247          $this->calculateColumnsWidth(array_merge($headers, $rows));
 248   
 249          $this->renderRowSeparator();
 250          if (!empty($headers)) {
 251              foreach ($headers as $header) {
 252                  $this->renderRow($header, $this->style->getCellHeaderFormat());
 253                  $this->renderRowSeparator();
 254              }
 255          }
 256          foreach ($rows as $row) {
 257              if ($row instanceof TableSeparator) {
 258                  $this->renderRowSeparator();
 259              } else {
 260                  $this->renderRow($row, $this->style->getCellRowFormat());
 261              }
 262          }
 263          if (!empty($rows)) {
 264              $this->renderRowSeparator();
 265          }
 266   
 267          $this->cleanup();
 268      }
 269   
 270      /**
 271       * Renders horizontal header separator.
 272       *
 273       * Example: +-----+-----------+-------+
 274       */
 275      private function renderRowSeparator()
 276      {
 277          if (0 === $count = $this->numberOfColumns) {
 278              return;
 279          }
 280   
 281          if (!$this->style->getHorizontalBorderChar() && !$this->style->getCrossingChar()) {
 282              return;
 283          }
 284   
 285          $markup = $this->style->getCrossingChar();
 286          for ($column = 0; $column < $count; ++$column) {
 287              $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->columnWidths[$column]).$this->style->getCrossingChar();
 288          }
 289   
 290          $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
 291      }
 292   
 293      /**
 294       * Renders vertical column separator.
 295       */
 296      private function renderColumnSeparator()
 297      {
 298          return sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar());
 299      }
 300   
 301      /**
 302       * Renders table row.
 303       *
 304       * Example: | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
 305       *
 306       * @param array  $row
 307       * @param string $cellFormat
 308       */
 309      private function renderRow(array $row, $cellFormat)
 310      {
 311          if (empty($row)) {
 312              return;
 313          }
 314   
 315          $rowContent = $this->renderColumnSeparator();
 316          foreach ($this->getRowColumns($row) as $column) {
 317              $rowContent .= $this->renderCell($row, $column, $cellFormat);
 318              $rowContent .= $this->renderColumnSeparator();
 319          }
 320          $this->output->writeln($rowContent);
 321      }
 322   
 323      /**
 324       * Renders table cell with padding.
 325       *
 326       * @param array  $row
 327       * @param int    $column
 328       * @param string $cellFormat
 329       */
 330      private function renderCell(array $row, $column, $cellFormat)
 331      {
 332          $cell = isset($row[$column]) ? $row[$column] : '';
 333          $width = $this->columnWidths[$column];
 334          if ($cell instanceof TableCell && $cell->getColspan() > 1) {
 335              // add the width of the following columns(numbers of colspan).
 336              foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) {
 337                  $width += $this->getColumnSeparatorWidth() + $this->columnWidths[$nextColumn];
 338              }
 339          }
 340   
 341          // str_pad won't work properly with multi-byte strings, we need to fix the padding
 342          if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
 343              $width += strlen($cell) - mb_strwidth($cell, $encoding);
 344          }
 345   
 346          $style = $this->getColumnStyle($column);
 347   
 348          if ($cell instanceof TableSeparator) {
 349              return sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width));
 350          }
 351   
 352          $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
 353          $content = sprintf($style->getCellRowContentFormat(), $cell);
 354   
 355          return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType()));
 356      }
 357   
 358      /**
 359       * Calculate number of columns for this table.
 360       */
 361      private function calculateNumberOfColumns()
 362      {
 363          if (null !== $this->numberOfColumns) {
 364              return;
 365          }
 366   
 367          $columns = array(0);
 368          foreach (array_merge($this->headers, $this->rows) as $row) {
 369              if ($row instanceof TableSeparator) {
 370                  continue;
 371              }
 372   
 373              $columns[] = $this->getNumberOfColumns($row);
 374          }
 375   
 376          $this->numberOfColumns = max($columns);
 377      }
 378   
 379      private function buildTableRows($rows)
 380      {
 381          $unmergedRows = array();
 382          for ($rowKey = 0; $rowKey < count($rows); ++$rowKey) {
 383              $rows = $this->fillNextRows($rows, $rowKey);
 384   
 385              // Remove any new line breaks and replace it with a new line
 386              foreach ($rows[$rowKey] as $column => $cell) {
 387                  if (!strstr($cell, "\n")) {
 388                      continue;
 389                  }
 390                  $lines = explode("\n", $cell);
 391                  foreach ($lines as $lineKey => $line) {
 392                      if ($cell instanceof TableCell) {
 393                          $line = new TableCell($line, array('colspan' => $cell->getColspan()));
 394                      }
 395                      if (0 === $lineKey) {
 396                          $rows[$rowKey][$column] = $line;
 397                      } else {
 398                          $unmergedRows[$rowKey][$lineKey][$column] = $line;
 399                      }
 400                  }
 401              }
 402          }
 403   
 404          $tableRows = array();
 405          foreach ($rows as $rowKey => $row) {
 406              $tableRows[] = $this->fillCells($row);
 407              if (isset($unmergedRows[$rowKey])) {
 408                  $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]);
 409              }
 410          }
 411   
 412          return $tableRows;
 413      }
 414   
 415      /**
 416       * fill rows that contains rowspan > 1.
 417       *
 418       * @param array $rows
 419       * @param int   $line
 420       *
 421       * @return array
 422       */
 423      private function fillNextRows($rows, $line)
 424      {
 425          $unmergedRows = array();
 426          foreach ($rows[$line] as $column => $cell) {
 427              if ($cell instanceof TableCell && $cell->getRowspan() > 1) {
 428                  $nbLines = $cell->getRowspan() - 1;
 429                  $lines = array($cell);
 430                  if (strstr($cell, "\n")) {
 431                      $lines = explode("\n", $cell);
 432                      $nbLines = count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines;
 433   
 434                      $rows[$line][$column] = new TableCell($lines[0], array('colspan' => $cell->getColspan()));
 435                      unset($lines[0]);
 436                  }
 437   
 438                  // create a two dimensional array (rowspan x colspan)
 439                  $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, array()), $unmergedRows);
 440                  foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
 441                      $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : '';
 442                      $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan()));
 443                  }
 444              }
 445          }
 446   
 447          foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
 448              // we need to know if $unmergedRow will be merged or inserted into $rows
 449              if (isset($rows[$unmergedRowKey]) && is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) {
 450                  foreach ($unmergedRow as $cellKey => $cell) {
 451                      // insert cell into row at cellKey position
 452                      array_splice($rows[$unmergedRowKey], $cellKey, 0, array($cell));
 453                  }
 454              } else {
 455                  $row = $this->copyRow($rows, $unmergedRowKey - 1);
 456                  foreach ($unmergedRow as $column => $cell) {
 457                      if (!empty($cell)) {
 458                          $row[$column] = $unmergedRow[$column];
 459                      }
 460                  }
 461                  array_splice($rows, $unmergedRowKey, 0, array($row));
 462              }
 463          }
 464   
 465          return $rows;
 466      }
 467   
 468      /**
 469       * fill cells for a row that contains colspan > 1.
 470       *
 471       * @param array $row
 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($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       * @param array $row
 514       *
 515       * @return int
 516       */
 517      private function getNumberOfColumns(array $row)
 518      {
 519          $columns = count($row);
 520          foreach ($row as $column) {
 521              $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0;
 522          }
 523   
 524          return $columns;
 525      }
 526   
 527      /**
 528       * Gets list of columns for the given row.
 529       *
 530       * @param array $row
 531       *
 532       * @return array
 533       */
 534      private function getRowColumns($row)
 535      {
 536          $columns = range(0, $this->numberOfColumns - 1);
 537          foreach ($row as $cellKey => $cell) {
 538              if ($cell instanceof TableCell && $cell->getColspan() > 1) {
 539                  // exclude grouped columns.
 540                  $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1));
 541              }
 542          }
 543   
 544          return $columns;
 545      }
 546   
 547      /**
 548       * Calculates columns widths.
 549       *
 550       * @param array $rows
 551       */
 552      private function calculateColumnsWidth($rows)
 553      {
 554          for ($column = 0; $column < $this->numberOfColumns; ++$column) {
 555              $lengths = array();
 556              foreach ($rows as $row) {
 557                  if ($row instanceof TableSeparator) {
 558                      continue;
 559                  }
 560   
 561                  foreach ($row as $i => $cell) {
 562                      if ($cell instanceof TableCell) {
 563                          $textLength = strlen($cell);
 564                          if ($textLength > 0) {
 565                              $contentColumns = str_split($cell, ceil($textLength / $cell->getColspan()));
 566                              foreach ($contentColumns as $position => $content) {
 567                                  $row[$i + $position] = $content;
 568                              }
 569                          }
 570                      }
 571                  }
 572   
 573                  $lengths[] = $this->getCellWidth($row, $column);
 574              }
 575   
 576              $this->columnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2;
 577          }
 578      }
 579   
 580      /**
 581       * Gets column width.
 582       *
 583       * @return int
 584       */
 585      private function getColumnSeparatorWidth()
 586      {
 587          return strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()));
 588      }
 589   
 590      /**
 591       * Gets cell width.
 592       *
 593       * @param array $row
 594       * @param int   $column
 595       *
 596       * @return int
 597       */
 598      private function getCellWidth(array $row, $column)
 599      {
 600          if (isset($row[$column])) {
 601              $cell = $row[$column];
 602              $cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
 603   
 604              return $cellWidth;
 605          }
 606   
 607          return 0;
 608      }
 609   
 610      /**
 611       * Called after rendering to cleanup cache data.
 612       */
 613      private function cleanup()
 614      {
 615          $this->columnWidths = array();
 616          $this->numberOfColumns = null;
 617      }
 618   
 619      private static function initStyles()
 620      {
 621          $borderless = new TableStyle();
 622          $borderless
 623              ->setHorizontalBorderChar('=')
 624              ->setVerticalBorderChar(' ')
 625              ->setCrossingChar(' ')
 626          ;
 627   
 628          $compact = new TableStyle();
 629          $compact
 630              ->setHorizontalBorderChar('')
 631              ->setVerticalBorderChar(' ')
 632              ->setCrossingChar('')
 633              ->setCellRowContentFormat('%s')
 634          ;
 635   
 636          $styleGuide = new TableStyle();
 637          $styleGuide
 638              ->setHorizontalBorderChar('-')
 639              ->setVerticalBorderChar(' ')
 640              ->setCrossingChar(' ')
 641              ->setCellHeaderFormat('%s')
 642          ;
 643   
 644          return array(
 645              'default' => new TableStyle(),
 646              'borderless' => $borderless,
 647              'compact' => $compact,
 648              'symfony-style-guide' => $styleGuide,
 649          );
 650      }
 651   
 652      private function resolveStyle($name)
 653      {
 654          if ($name instanceof TableStyle) {
 655              return $name;
 656          }
 657   
 658          if (isset(self::$styles[$name])) {
 659              return self::$styles[$name];
 660          }
 661   
 662          throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
 663      }
 664  }
 665