| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 | <?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;    }}
 |