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