mirror of
https://github.com/zhamao-robot/zhamao-logger.git
synced 2026-07-02 14:25:40 +08:00
rewrite TablePrinter
This commit is contained in:
@@ -26,12 +26,16 @@
|
||||
"minimum-stability": "stable",
|
||||
"require": {
|
||||
"php": "^7.2 || ^7.3 || ^7.4 || ^8.0 || ^8.1",
|
||||
"psr/log": "^1 || ^2 || ^3"
|
||||
"psr/log": "^1 || ^2 || ^3",
|
||||
"symfony/polyfill-mbstring": "^1.0"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"sort-packages": true
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "Use C/C++ extension instead of polyfill will be more efficient"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"phpstan/phpstan": "^1.1",
|
||||
|
||||
19
demo/table-printer-demo.php
Normal file
19
demo/table-printer-demo.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$data = [
|
||||
'path' => '我是一端比较牛逼的终端打印工具',
|
||||
'php_version' => PHP_VERSION,
|
||||
'php_version_id' => PHP_VERSION_ID,
|
||||
'info' => '并且支持中文!!',
|
||||
'path2' => __DIR__,
|
||||
'long' => '我可以' . str_repeat('很长', 50),
|
||||
];
|
||||
|
||||
(new \ZM\Logger\TablePrinter($data))
|
||||
->setValueColor('random')
|
||||
->setRowOverflowHide()
|
||||
->printAll();
|
||||
@@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Logger;
|
||||
|
||||
/**
|
||||
* 输出漂亮的表格参数形式,支持自适应终端大小
|
||||
*
|
||||
* Class ConsolePrettyPrinter
|
||||
*/
|
||||
class ConsolePrettyPrinter
|
||||
{
|
||||
protected $params;
|
||||
|
||||
protected $head;
|
||||
|
||||
protected $foot;
|
||||
|
||||
public function __construct(array $params, $head = '', $foot = '')
|
||||
{
|
||||
$this->params = $params;
|
||||
$this->head = $head === '' ? str_pad('', 65, '=') : $head;
|
||||
$this->foot = $foot === '' ? str_pad('', 65, '=') : $foot;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $params): ConsolePrettyPrinter
|
||||
{
|
||||
return new static($params);
|
||||
}
|
||||
|
||||
public function printAll(): void
|
||||
{
|
||||
$this->printHead();
|
||||
$this->printBody();
|
||||
$this->printFoot();
|
||||
}
|
||||
|
||||
public function printHead()
|
||||
{
|
||||
// TODO: 写头
|
||||
}
|
||||
|
||||
public function printBody()
|
||||
{
|
||||
// TODO: 写主体
|
||||
}
|
||||
|
||||
public function printFoot()
|
||||
{
|
||||
// TODO: 写尾
|
||||
}
|
||||
}
|
||||
205
src/ZM/Logger/TablePrinter.php
Normal file
205
src/ZM/Logger/TablePrinter.php
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Logger;
|
||||
|
||||
/**
|
||||
* 输出漂亮的表格参数形式,支持自适应终端大小
|
||||
*
|
||||
* Class ConsolePrettyPrinter
|
||||
*/
|
||||
class TablePrinter
|
||||
{
|
||||
/**
|
||||
* @var array 参数列表
|
||||
*/
|
||||
protected $params;
|
||||
|
||||
/**
|
||||
* @var string 顶部格式
|
||||
*/
|
||||
protected $head;
|
||||
|
||||
/**
|
||||
* @var string 底部格式
|
||||
*/
|
||||
protected $foot;
|
||||
|
||||
protected $border_width;
|
||||
|
||||
protected $value_color = 'green';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $row_overflow_hide = false;
|
||||
|
||||
public function __construct(array $params, string $head = '', string $foot = '', int $max_border_length = 79)
|
||||
{
|
||||
$this->params = $params;
|
||||
$this->head = $head;
|
||||
$this->foot = $foot;
|
||||
$this->setBorderWidth($max_border_length);
|
||||
}
|
||||
|
||||
public function setValueColor(string $color): TablePrinter
|
||||
{
|
||||
if ($color === 'random') {
|
||||
$random_list = [
|
||||
'red', 'green', 'blue', 'yellow', 'magenta', 'gray',
|
||||
'bright_red', 'bright_yellow', 'bright_green', 'bright_blue', 'bright_magenta', 'bright_cyan',
|
||||
];
|
||||
$random = mt_rand(0, count($random_list) - 1);
|
||||
$this->value_color = $random_list[$random];
|
||||
} else {
|
||||
$this->value_color = $color;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRowOverflowHide(bool $hide = true): TablePrinter
|
||||
{
|
||||
$this->row_overflow_hide = $hide;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function printAll(): void
|
||||
{
|
||||
$this->printHead();
|
||||
$this->printBody();
|
||||
$this->printFoot();
|
||||
}
|
||||
|
||||
public function printHead()
|
||||
{
|
||||
echo $this->head . PHP_EOL;
|
||||
}
|
||||
|
||||
public function printFoot()
|
||||
{
|
||||
echo $this->foot . PHP_EOL;
|
||||
}
|
||||
|
||||
public function printBody()
|
||||
{
|
||||
$line_data = [];
|
||||
$current_line = 0;
|
||||
foreach ($this->params as $k => $v) {
|
||||
$k = (string) $k;
|
||||
$v = (string) $v;
|
||||
$k_len = mb_strwidth($k); // 获取 key 的长度
|
||||
$v_len = mb_strwidth($v); // 获取 value 的长度
|
||||
$len = $k_len + 2 + $v_len; // 计算需要的长度
|
||||
$valid_width = $this->border_width - 2; // 获取可用的长度
|
||||
if ($k_len + 5 > $this->border_width - 2) { // 这个参数的 key 过长了,没有你这么用的!!
|
||||
continue;
|
||||
}
|
||||
while (true) {
|
||||
if (!isset($line_data[$current_line])) { // 如果行是空的,先尝试放入一个参数
|
||||
if ($len > $valid_width) { // 需要的长度超出一行,直接另起折行
|
||||
if ($this->row_overflow_hide) { // 如果开启了超出部分隐藏,则直接砍掉后面的东西,变成三个或四个点
|
||||
[$partial_v, $partial_v_len] = $this->getPartialValue($v, $valid_width - $k_len - 2);
|
||||
|
||||
// 写入到数据中
|
||||
$line_data[$current_line] = [
|
||||
'used' => $valid_width - $k_len - 2 - $partial_v_len,
|
||||
'can_put_second' => false,
|
||||
'lines' => $k . ': ' .
|
||||
ConsoleColor::apply([$this->value_color], $partial_v . str_pad('', $valid_width - $k_len - 2 - $partial_v_len, '.')),
|
||||
];
|
||||
++$current_line;
|
||||
// 下一个参数
|
||||
} else { // 没开隐藏就要拐弯输出
|
||||
$line_data[$current_line] = [
|
||||
'used' => $k_len + 2,
|
||||
'can_put_second' => false,
|
||||
'lines' => $k . ': ',
|
||||
];
|
||||
do {
|
||||
[$partial_v, $partial_v_len, $next_offset] = $this->getPartialValue($v, $valid_width - $line_data[$current_line]['used'], 0);
|
||||
$v = mb_substr($v, $next_offset);
|
||||
$line_data[$current_line]['lines'] .= ConsoleColor::apply([$this->value_color], $partial_v);
|
||||
$line_data[$current_line]['used'] += $partial_v_len;
|
||||
++$current_line;
|
||||
$line_data[$current_line] = [
|
||||
'used' => 0,
|
||||
'can_put_second' => false,
|
||||
'lines' => '',
|
||||
];
|
||||
} while ($v !== '');
|
||||
if ($line_data[$current_line]['used'] === 0) {
|
||||
unset($line_data[$current_line]);
|
||||
}
|
||||
// 下一个参数
|
||||
}
|
||||
break;
|
||||
}
|
||||
// 没超出一行,直接写
|
||||
$line_data[$current_line] = [
|
||||
'used' => $len,
|
||||
'can_put_second' => ($valid_width >= 57 && intval(floor($valid_width / 2)) - 2 + ($valid_width % 2) > $len),
|
||||
'lines' => $k . ': ' . ConsoleColor::apply([$this->value_color], $v),
|
||||
];
|
||||
break;
|
||||
}
|
||||
// 如果当前行不是空的,就要看看是否能放下这个参数
|
||||
if ($line_data[$current_line]['can_put_second'] && ($k_len + $v_len + 2 <= floor($valid_width / 2) - 2)) { // 如果可以放下,就放下
|
||||
// 首先把前面的参数补充到中间的分隔符
|
||||
$line_data[$current_line]['lines'] .= str_pad('', intval(floor($valid_width / 2)) - 2 + ($valid_width % 2) - $line_data[$current_line]['used']);
|
||||
// 然后输出分隔符
|
||||
$line_data[$current_line]['lines'] .= ' | ';
|
||||
// 最后输出第二列的参数
|
||||
$line_data[$current_line]['lines'] .= $k . ': ' . ConsoleColor::apply([$this->value_color], $v);
|
||||
++$current_line;
|
||||
break;
|
||||
} // 放不下,直接下一轮继续
|
||||
++$current_line;
|
||||
} // 这层是 while(true)
|
||||
}
|
||||
foreach ($line_data as $line) {
|
||||
echo ' ' . $line['lines'] . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
public function setBorderWidth(int $border_width): TablePrinter
|
||||
{
|
||||
if ($border_width <= 0) {
|
||||
$this->border_width = $this->fetchTerminalSize();
|
||||
} else {
|
||||
$terminal_size = $this->fetchTerminalSize();
|
||||
$this->border_width = $border_width < $terminal_size ? $border_width : $terminal_size;
|
||||
}
|
||||
$this->head = str_pad($this->head, $this->border_width, '=');
|
||||
$this->foot = str_pad($this->foot, $this->border_width, '=');
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getConsoleLength(string $v)
|
||||
{
|
||||
return mb_strwidth($v);
|
||||
}
|
||||
|
||||
private function fetchTerminalSize(): int
|
||||
{
|
||||
if (STDIN === false) {
|
||||
return 79;
|
||||
}
|
||||
$size = exec('stty size 2>/dev/null');
|
||||
if (empty($size)) {
|
||||
return 79;
|
||||
}
|
||||
return (int) explode(' ', trim($size))[1];
|
||||
}
|
||||
|
||||
private function getPartialValue($v, int $valid_width, int $remain = 3): array
|
||||
{
|
||||
$virtual_v_offset = 0;
|
||||
do { // 依次填入字符,直到空下了小于等于4长度的距离
|
||||
++$virtual_v_offset;
|
||||
$virtual_v = mb_substr($v, 0, $virtual_v_offset);
|
||||
$used_len = mb_strwidth($virtual_v);
|
||||
} while ($valid_width - $used_len > $remain && mb_strlen($virtual_v) < mb_strlen($v));
|
||||
return [$virtual_v, $used_len, $virtual_v_offset];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user