123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 |
- <?php
- /**
- * @link http://www.yiiframework.com/
- * @copyright Copyright (c) 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
- namespace yii\console\widgets;
- use Yii;
- use yii\base\Widget;
- use yii\helpers\ArrayHelper;
- use yii\helpers\Console;
- /**
- * Table class displays a table in console.
- *
- * For example,
- *
- * ```php
- * $table = new Table();
- *
- * echo $table
- * ->setHeaders(['test1', 'test2', 'test3'])
- * ->setRows([
- * ['col1', 'col2', 'col3'],
- * ['col1', 'col2', ['col3-0', 'col3-1', 'col3-2']],
- * ])
- * ->run();
- * ```
- *
- * or
- *
- * ```php
- * echo Table::widget([
- * 'headers' => ['test1', 'test2', 'test3'],
- * 'rows' => [
- * ['col1', 'col2', 'col3'],
- * ['col1', 'col2', ['col3-0', 'col3-1', 'col3-2']],
- * ],
- * ]);
- *
- * @author Daniel Gomez Pan <pana_1990@hotmail.com>
- * @since 2.0.13
- */
- class Table extends Widget
- {
- const DEFAULT_CONSOLE_SCREEN_WIDTH = 120;
- const CONSOLE_SCROLLBAR_OFFSET = 3;
- const CHAR_TOP = 'top';
- const CHAR_TOP_MID = 'top-mid';
- const CHAR_TOP_LEFT = 'top-left';
- const CHAR_TOP_RIGHT = 'top-right';
- const CHAR_BOTTOM = 'bottom';
- const CHAR_BOTTOM_MID = 'bottom-mid';
- const CHAR_BOTTOM_LEFT = 'bottom-left';
- const CHAR_BOTTOM_RIGHT = 'bottom-right';
- const CHAR_LEFT = 'left';
- const CHAR_LEFT_MID = 'left-mid';
- const CHAR_MID = 'mid';
- const CHAR_MID_MID = 'mid-mid';
- const CHAR_RIGHT = 'right';
- const CHAR_RIGHT_MID = 'right-mid';
- const CHAR_MIDDLE = 'middle';
- /**
- * @var array table headers
- * @since 2.0.19
- */
- protected $headers = [];
- /**
- * @var array table rows
- * @since 2.0.19
- */
- protected $rows = [];
- /**
- * @var array table chars
- * @since 2.0.19
- */
- protected $chars = [
- self::CHAR_TOP => '═',
- self::CHAR_TOP_MID => '╤',
- self::CHAR_TOP_LEFT => '╔',
- self::CHAR_TOP_RIGHT => '╗',
- self::CHAR_BOTTOM => '═',
- self::CHAR_BOTTOM_MID => '╧',
- self::CHAR_BOTTOM_LEFT => '╚',
- self::CHAR_BOTTOM_RIGHT => '╝',
- self::CHAR_LEFT => '║',
- self::CHAR_LEFT_MID => '╟',
- self::CHAR_MID => '─',
- self::CHAR_MID_MID => '┼',
- self::CHAR_RIGHT => '║',
- self::CHAR_RIGHT_MID => '╢',
- self::CHAR_MIDDLE => '│',
- ];
- /**
- * @var array table column widths
- * @since 2.0.19
- */
- protected $columnWidths = [];
- /**
- * @var int screen width
- * @since 2.0.19
- */
- protected $screenWidth;
- /**
- * @var string list prefix
- * @since 2.0.19
- */
- protected $listPrefix = '• ';
- /**
- * Set table headers.
- *
- * @param array $headers table headers
- * @return $this
- */
- public function setHeaders(array $headers)
- {
- $this->headers = array_values($headers);
- return $this;
- }
- /**
- * Set table rows.
- *
- * @param array $rows table rows
- * @return $this
- */
- public function setRows(array $rows)
- {
- $this->rows = array_map('array_values', $rows);
- return $this;
- }
- /**
- * Set table chars.
- *
- * @param array $chars table chars
- * @return $this
- */
- public function setChars(array $chars)
- {
- $this->chars = $chars;
- return $this;
- }
- /**
- * Set screen width.
- *
- * @param int $width screen width
- * @return $this
- */
- public function setScreenWidth($width)
- {
- $this->screenWidth = $width;
- return $this;
- }
- /**
- * Set list prefix.
- *
- * @param string $listPrefix list prefix
- * @return $this
- */
- public function setListPrefix($listPrefix)
- {
- $this->listPrefix = $listPrefix;
- return $this;
- }
- /**
- * @return string the rendered table
- */
- public function run()
- {
- $this->calculateRowsSize();
- $headerCount = count($this->headers);
- $buffer = $this->renderSeparator(
- $this->chars[self::CHAR_TOP_LEFT],
- $this->chars[self::CHAR_TOP_MID],
- $this->chars[self::CHAR_TOP],
- $this->chars[self::CHAR_TOP_RIGHT]
- );
- // Header
- if ($headerCount > 0) {
- $buffer .= $this->renderRow($this->headers,
- $this->chars[self::CHAR_LEFT],
- $this->chars[self::CHAR_MIDDLE],
- $this->chars[self::CHAR_RIGHT]
- );
- }
- // Content
- foreach ($this->rows as $i => $row) {
- if ($i > 0 || $headerCount > 0) {
- $buffer .= $this->renderSeparator(
- $this->chars[self::CHAR_LEFT_MID],
- $this->chars[self::CHAR_MID_MID],
- $this->chars[self::CHAR_MID],
- $this->chars[self::CHAR_RIGHT_MID]
- );
- }
- $buffer .= $this->renderRow($row,
- $this->chars[self::CHAR_LEFT],
- $this->chars[self::CHAR_MIDDLE],
- $this->chars[self::CHAR_RIGHT]);
- }
- $buffer .= $this->renderSeparator(
- $this->chars[self::CHAR_BOTTOM_LEFT],
- $this->chars[self::CHAR_BOTTOM_MID],
- $this->chars[self::CHAR_BOTTOM],
- $this->chars[self::CHAR_BOTTOM_RIGHT]
- );
- return $buffer;
- }
- /**
- * Renders a row of data into a string.
- *
- * @param array $row row of data
- * @param string $spanLeft character for left border
- * @param string $spanMiddle character for middle border
- * @param string $spanRight character for right border
- * @return string
- * @see \yii\console\widgets\Table::render()
- */
- protected function renderRow(array $row, $spanLeft, $spanMiddle, $spanRight)
- {
- $size = $this->columnWidths;
- $buffer = '';
- $arrayPointer = [];
- $finalChunk = [];
- for ($i = 0, ($max = $this->calculateRowHeight($row)) ?: $max = 1; $i < $max; $i++) {
- $buffer .= $spanLeft . ' ';
- foreach ($size as $index => $cellSize) {
- $cell = isset($row[$index]) ? $row[$index] : null;
- $prefix = '';
- if ($index !== 0) {
- $buffer .= $spanMiddle . ' ';
- }
- if (is_array($cell)) {
- if (empty($finalChunk[$index])) {
- $finalChunk[$index] = '';
- $start = 0;
- $prefix = $this->listPrefix;
- if (!isset($arrayPointer[$index])) {
- $arrayPointer[$index] = 0;
- }
- } else {
- $start = mb_strwidth($finalChunk[$index], Yii::$app->charset);
- }
- $chunk = mb_substr($cell[$arrayPointer[$index]], $start, $cellSize - 4, Yii::$app->charset);
- $finalChunk[$index] .= $chunk;
- if (isset($cell[$arrayPointer[$index] + 1]) && $finalChunk[$index] === $cell[$arrayPointer[$index]]) {
- $arrayPointer[$index]++;
- $finalChunk[$index] = '';
- }
- } else {
- $chunk = mb_substr($cell, ($cellSize * $i) - ($i * 2), $cellSize - 2, Yii::$app->charset);
- }
- $chunk = $prefix . $chunk;
- $repeat = $cellSize - mb_strwidth($chunk, Yii::$app->charset) - 1;
- $buffer .= $chunk;
- if ($repeat >= 0) {
- $buffer .= str_repeat(' ', $repeat);
- }
- }
- $buffer .= "$spanRight\n";
- }
- return $buffer;
- }
- /**
- * Renders separator.
- *
- * @param string $spanLeft character for left border
- * @param string $spanMid character for middle border
- * @param string $spanMidMid character for middle-middle border
- * @param string $spanRight character for right border
- * @return string the generated separator row
- * @see \yii\console\widgets\Table::render()
- */
- protected function renderSeparator($spanLeft, $spanMid, $spanMidMid, $spanRight)
- {
- $separator = $spanLeft;
- foreach ($this->columnWidths as $index => $rowSize) {
- if ($index !== 0) {
- $separator .= $spanMid;
- }
- $separator .= str_repeat($spanMidMid, $rowSize);
- }
- $separator .= $spanRight . "\n";
- return $separator;
- }
- /**
- * Calculate the size of rows to draw anchor of columns in console.
- *
- * @see \yii\console\widgets\Table::render()
- */
- protected function calculateRowsSize()
- {
- $this->columnWidths = $columns = [];
- $totalWidth = 0;
- $screenWidth = $this->getScreenWidth() - self::CONSOLE_SCROLLBAR_OFFSET;
- $headerCount = count($this->headers);
- if (empty($this->rows)) {
- $rowColCount = 0;
- } else {
- $rowColCount = max(array_map('count', $this->rows));
- }
- $count = max($headerCount, $rowColCount);
- for ($i = 0; $i < $count; $i++) {
- $columns[] = ArrayHelper::getColumn($this->rows, $i);
- if ($i < $headerCount) {
- $columns[$i][] = $this->headers[$i];
- }
- }
- foreach ($columns as $column) {
- $columnWidth = max(array_map(function ($val) {
- if (is_array($val)) {
- $encodings = array_fill(0, count($val), Yii::$app->charset);
- return max(array_map('mb_strwidth', $val, $encodings)) + mb_strwidth($this->listPrefix, Yii::$app->charset);
- }
- return mb_strwidth($val, Yii::$app->charset);
- }, $column)) + 2;
- $this->columnWidths[] = $columnWidth;
- $totalWidth += $columnWidth;
- }
- $relativeWidth = $screenWidth / $totalWidth;
- if ($totalWidth > $screenWidth) {
- foreach ($this->columnWidths as $j => $width) {
- $this->columnWidths[$j] = (int) ($width * $relativeWidth);
- if ($j === count($this->columnWidths)) {
- $this->columnWidths = $totalWidth;
- }
- $totalWidth -= $this->columnWidths[$j];
- }
- }
- }
- /**
- * Calculate the height of a row.
- *
- * @param array $row
- * @return int maximum row per cell
- * @see \yii\console\widgets\Table::render()
- */
- protected function calculateRowHeight($row)
- {
- $rowsPerCell = array_map(function ($size, $columnWidth) {
- if (is_array($columnWidth)) {
- $rows = 0;
- foreach ($columnWidth as $width) {
- $rows += ceil($width / ($size - 2));
- }
- return $rows;
- }
- return ceil($columnWidth / ($size - 2));
- }, $this->columnWidths, array_map(function ($val) {
- if (is_array($val)) {
- $encodings = array_fill(0, count($val), Yii::$app->charset);
- return array_map('mb_strwidth', $val, $encodings);
- }
- return mb_strwidth($val, Yii::$app->charset);
- }, $row)
- );
- return max($rowsPerCell);
- }
- /**
- * Getting screen width.
- * If it is not able to determine screen width, default value `123` will be set.
- *
- * @return int screen width
- */
- protected function getScreenWidth()
- {
- if (!$this->screenWidth) {
- $size = Console::getScreenSize();
- $this->screenWidth = isset($size[0])
- ? $size[0]
- : self::DEFAULT_CONSOLE_SCREEN_WIDTH + self::CONSOLE_SCROLLBAR_OFFSET;
- }
- return $this->screenWidth;
- }
- }
|