canyin-project/ybcy/vendor/yiisoft/yii2/console/widgets/Table.php
2024-11-01 16:07:54 +08:00

406 lines
12 KiB
PHP

<?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']],
* ],
* ]);
*
* @property string $listPrefix List prefix. This property is write-only.
* @property int $screenWidth Screen width. This property is write-only.
*
* @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(function($row) {
return array_map(function($value) {
return empty($value) && !is_numeric($value) ? ' ' : $value;
}, array_values($row));
}, $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 = [];
$alreadyPrintedCells = [];
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 = '';
$chunk = '';
if ($index !== 0) {
$buffer .= $spanMiddle . ' ';
}
if (is_array($cell)) {
if (empty($finalChunk[$index])) {
$finalChunk[$index] = '';
$prefix = $this->listPrefix;
if (!isset($arrayPointer[$index])) {
$arrayPointer[$index] = 0;
}
}
$chunk = $cell[$arrayPointer[$index]];
$finalChunk[$index] .= $chunk;
if (isset($cell[$arrayPointer[$index] + 1]) && $finalChunk[$index] === $cell[$arrayPointer[$index]]) {
$arrayPointer[$index]++;
$finalChunk[$index] = '';
}
} else {
if (!isset($alreadyPrintedCells[$index])) {
$chunk = $cell;
}
$alreadyPrintedCells[$index] = true;
}
$chunk = $prefix . $chunk;
$repeat = $cellSize - Console::ansiStrwidth($chunk) - 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)) {
return max(array_map('yii\helpers\Console::ansiStrwidth', $val)) + Console::ansiStrwidth($this->listPrefix);
}
return Console::ansiStrwidth($val);
}, $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 += $size == 2 ? 0 : ceil($width / ($size - 2));
}
return $rows;
}
return $size == 2 || $columnWidth == 0 ? 0 : ceil($columnWidth / ($size - 2));
}, $this->columnWidths, array_map(function ($val) {
if (is_array($val)) {
return array_map('yii\helpers\Console::ansiStrwidth', $val);
}
return Console::ansiStrwidth($val);
}, $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;
}
}