From d3a046792bf82a843c9942e3fcc0e11d8e93f70c Mon Sep 17 00:00:00 2001 From: Lkeme <19500576+lkeme@users.noreply.github.com> Date: Sat, 4 Jun 2022 15:52:09 +0800 Subject: [PATCH] [feat] DebugCommand --- .gitignore | 2 + composer.json | 5 +- phpunit.xml | 4 +- plugin/Login/Login.php | 2 +- src/Cache/Cache.php | 1 - src/Console/Command/AppCommand.php | 10 +- src/Console/Command/DebugCommand.php | 89 +++++++ src/Console/Console.php | 2 + src/Plugin/Plugin.php | 1 + src/TimeLock/TimeLock.php | 1 - src/Util/AsciiTable/AsciiTable.php | 373 +++++++++++++++++++++++++++ tests/AsciiTableTest.php | 69 +++++ tests/bootstrap.php | 41 +++ 13 files changed, 592 insertions(+), 8 deletions(-) create mode 100644 src/Console/Command/DebugCommand.php create mode 100644 src/Util/AsciiTable/AsciiTable.php create mode 100644 tests/AsciiTableTest.php create mode 100644 tests/bootstrap.php diff --git a/.gitignore b/.gitignore index 863f002..525bbe8 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ index1.php # 保留忽略目录下的指定文件夹 /profile/* !/profile/example + +.phpunit.* diff --git a/composer.json b/composer.json index b1d4753..1d72098 100644 --- a/composer.json +++ b/composer.json @@ -61,11 +61,12 @@ "overtrue/pinyin": "dev-master", "guzzlehttp/guzzle": "^7.4", "toolkit/pflag": "^2.0", - "symfony/console": "^6.1" + "symfony/console": "^6.1", + "malios/php-to-ascii-table": "^3.0" }, "autoload": { "psr-4": { - "Bhp\\": "src" + "Bhp\\": "src/" }, "files": [ "src/Helpers.php" diff --git a/phpunit.xml b/phpunit.xml index e3d27b0..0071e47 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,8 +1,8 @@ + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"> src diff --git a/plugin/Login/Login.php b/plugin/Login/Login.php index f11ff6d..f71c54e 100644 --- a/plugin/Login/Login.php +++ b/plugin/Login/Login.php @@ -507,7 +507,7 @@ class Login extends BasePlugin } else { Log::info('公钥载入完毕'); } - // print_r($data); + // $public_key = $response['data']['key']; $hash = $response['data']['hash']; openssl_public_encrypt($hash . $plaintext, $crypt, $public_key); diff --git a/src/Cache/Cache.php b/src/Cache/Cache.php index 6c7b062..8c7c4b8 100644 --- a/src/Cache/Cache.php +++ b/src/Cache/Cache.php @@ -69,7 +69,6 @@ class Cache extends SingleTon self::getInstance()->caches[$class_name] = new Flintstone($database, $options); // ->set('bob', ['email' => 'bob@site.com', 'password' => '123456']); } - print_r(array_keys(self::getInstance()->caches)); } diff --git a/src/Console/Command/AppCommand.php b/src/Console/Command/AppCommand.php index e45265e..6ed04bf 100644 --- a/src/Console/Command/AppCommand.php +++ b/src/Console/Command/AppCommand.php @@ -36,6 +36,15 @@ class AppCommand extends Command public function __construct() { parent::__construct('mode:app', $this->desc); + // + $this + ->usage( + ' $0 profile mode:app ## 完整命令' . + ' $0 profile m:a ## 完整命令(缩写)' . + ' $0 profile ## 省略命令(保留profile命令)' . + ' $0 mode:app ## 省略命令(保留动作命令)' . + ' $0 m:d ## 省略命令(保留动作命令)(缩写)' + ); } /** @@ -54,7 +63,6 @@ class AppCommand extends Command Log::info("执行 $this->desc"); // $plugins = Plugin::getPlugins(); - print_r($plugins); foreach ($plugins as $plugin) { Task::addTask($plugin['hook'], null); } diff --git a/src/Console/Command/DebugCommand.php b/src/Console/Command/DebugCommand.php new file mode 100644 index 0000000..9d2cdc6 --- /dev/null +++ b/src/Console/Command/DebugCommand.php @@ -0,0 +1,89 @@ +desc); + // + $this + ->option('-p --plugin', '[默认会同时加载Login]测试插件') + ->option('-P --plugins', '[默认会同时加载Login]测试插件列表') + ->usage( + ' $0 mode:debug --plugin TestPlugin ## details 1' . + ' $0 m:d -p TestPlugin ## details 2' . + ' $0 mode:debug --plugins TestPlugin|Test1Plugin ## details 3' . + ' $0 m:d -P TestPlugin,Test1Plugin ## details 4' + ); + } + + /** + * @param Interactor $io + * @return void + */ + public function interact(Interactor $io): void + { + } + + /** + * @return void + */ + public function execute(): void + { + Log::info("执行 $this->desc"); + // + $p = $this->values()['plugin']; + if (is_null($p)) { + $temp = $this->values()['plugins']; + $pp = explode(',', $temp); + } else { + $pp = [$p]; + } + // + if (empty($pp)) failExit('没有插件输入'); + array_unshift($pp, 'Login'); + // + $plugins = Plugin::getPlugins(); + foreach ($plugins as $plugin) { + if(!in_array($plugin['hook'],$pp)){ + continue; + } + Task::addTask($plugin['hook'], null); + } + // + Task::execTasks(); + } + + +} \ No newline at end of file diff --git a/src/Console/Console.php b/src/Console/Console.php index 4f01c06..69f3140 100644 --- a/src/Console/Console.php +++ b/src/Console/Console.php @@ -19,6 +19,7 @@ namespace Bhp\Console; use Ahc\Cli\Application; use Bhp\Console\Command\AppCommand; +use Bhp\Console\Command\DebugCommand; use Bhp\Console\Command\RestoreCommand; use Bhp\Console\Command\ScriptCommand; use Bhp\Util\DesignPattern\SingleTon; @@ -95,6 +96,7 @@ LOGO; ->add(new AppCommand(), 'm:a', true) // 模式1 ->add(new ScriptCommand(), 'm:s') // 模式2 ->add(new RestoreCommand(), 'm:r') // 模式3 + ->add(new DebugCommand(), 'm:d') // 模式4 ->logo($this->logo) ->handle($this->argv); } diff --git a/src/Plugin/Plugin.php b/src/Plugin/Plugin.php index ea961bc..1aad81e 100644 --- a/src/Plugin/Plugin.php +++ b/src/Plugin/Plugin.php @@ -181,6 +181,7 @@ class Plugin extends SingleTon } } $this->sortPlugins(); + $this->preloadPlugins(); } /** diff --git a/src/TimeLock/TimeLock.php b/src/TimeLock/TimeLock.php index 8e3decd..3da8f8f 100644 --- a/src/TimeLock/TimeLock.php +++ b/src/TimeLock/TimeLock.php @@ -47,7 +47,6 @@ class TimeLock extends SingleTon if (!array_key_exists($class_name, self::getInstance()->locks)) { self::getInstance()->locks[$class_name] = ['times' => $times, 'pause' => $status]; } - print_r(self::getInstance()->locks); } /** diff --git a/src/Util/AsciiTable/AsciiTable.php b/src/Util/AsciiTable/AsciiTable.php new file mode 100644 index 0000000..ce51472 --- /dev/null +++ b/src/Util/AsciiTable/AsciiTable.php @@ -0,0 +1,373 @@ +setData($data) + ->setIndentation('') + ->setKeysAlignment(self::AlignCenter) + ->setValuesAlignment(self::AlignLeft) + ->setFormatter(null); + } + + /** + * @return string + */ + public function __toString() + { + return $this->getTable(); + } + + /** + * @param array|null $data + * @return string + */ + public function getTable(?array $data = null): string + { + if (!is_null($data)) + $this->setData($data); + + $data = $this->prepare(); + + $i = $this->indentation; + + $table = $i . $this->line('┌', '─', '┬', '┐') . PHP_EOL; + + if ($this->displayHeader) { + //绘制table header + $headerRows = array_combine($this->keys, $this->keys); + $table .= $i . $this->row($headerRows, $this->keysAlignment) . PHP_EOL; + $table .= $i . $this->line('├', '─', '┼', '┤') . PHP_EOL; + } + + foreach ($data as $row) { + $table .= $i . $this->row($row, $this->valuesAlignment) . PHP_EOL; + } + $table .= $i . $this->line('└', '─', '┴', '┘') . PHP_EOL; + + return $table; + } + + /** + * @param string $indentation + * @return $this + */ + public function setIndentation(string $indentation): static + { + $this->indentation = $indentation; + return $this; + } + + /** + * @param bool $displayHeader + * @return $this + */ + public function isDisplayHeader(bool $displayHeader): static + { + $this->displayHeader = $displayHeader; + return $this; + } + + /** + * @param int $keysAlignment + * @return $this + */ + public function setKeysAlignment(int $keysAlignment): static + { + $this->keysAlignment = $keysAlignment; + return $this; + } + + /** + * @param int $valuesAlignment + * @return $this + */ + public function setValuesAlignment(int $valuesAlignment): static + { + $this->valuesAlignment = $valuesAlignment; + return $this; + } + + /** + * @param Closure|null $formatter + * @return $this + */ + public function setFormatter(?Closure $formatter): static + { + $this->formatter = $formatter; + return $this; + } + + /** + * @param string|null $left + * @param string $horizontal + * @param string $link + * @param string|null $right + * @return string + */ + protected function line(?string $left, string $horizontal, string $link, ?string $right): string + { + $line = $left; + foreach ($this->keys as $key) { + $line .= str_repeat($horizontal, $this->widths[$key] + 2) . $link; + } + + if (mb_strlen($line) > mb_strlen($left)) { + $line = mb_substr($line, 0, -mb_strlen($horizontal)); + } + return $line . $right; + } + + /** + * @param array $row + * @param int $alignment + * @return string + */ + protected function row(array $row, int $alignment): string + { + $line = '│'; + foreach ($this->keys as $key) { + $value = (string)($row[$key] ?? ''); + $line .= ' ' . static::mbStrPad($value, $this->widths[$key], ' ', $alignment) . ' ' . '│'; + } + if (empty($row)) { + $line .= '│'; + } + return $line; + } + + /** + * @return array + */ + protected function prepare(): array + { + $this->keys = []; + $this->widths = []; + $data = $this->data; + + //合并全部数组的key + foreach ($data as $row) { + $this->keys = array_merge($this->keys, array_keys($row)); + } + $this->keys = array_unique($this->keys); + + //补充缺陷数组 + foreach ($data as $index => $row) { + foreach ($this->keys as $key) { + if (!array_key_exists($key, $row)) { + $data[$index][$key] = null; + } + } + } + + //执行formatter + if ($this->formatter instanceof Closure) { + foreach ($data as &$row) { + array_walk($row, $this->formatter); + } + unset($row); + } + + foreach ($this->keys as $key) { + $this->setWidth($key, $key); + } + foreach ($data as $row) { + foreach ($row as $columnKey => $columnValue) { + $this->setWidth($columnKey, $columnValue); + } + } + return $data; + } + + /** + * @param mixed $key + * @param mixed $value + * @return void + */ + protected function setWidth(mixed $key, mixed $value): void + { + if (!isset($this->widths[$key])) { + $this->widths[$key] = 0; + } + // Deprecated: strlen(): Passing null to parameter #1 ($string) of type string is deprecated + // Deprecated: mb_strlen(): Passing null to parameter #1 ($string) of type string is deprecated + $value = (string)($value ?: ''); + $width = (strlen($value) + mb_strlen($value, 'UTF8')) / 2; + if ($width > $this->widths[$key]) { + $this->widths[$key] = $width; + } + } + + /** + * @param string $data + * @return bool|int|null + */ + protected static function countCJK(string $data): bool|int|null + { + return preg_match_all('/[\p{Han}\p{Katakana}\p{Hiragana}\p{Hangul}]/u', $data); + } + + /** + * @param string $input + * @param int $pad_length + * @param string $pad_string + * @param int $pad_type + * @param string|null $encoding + * @return string + */ + protected function mbStrPad(string $input, int $pad_length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string + { + // $encoding = $encoding === null ? mb_internal_encoding() : $encoding; + // $diff = strlen($input) - (strlen($input) + mb_strlen($input, $encoding)) / 2; + // return str_pad($input, $pad_length + $diff, $pad_string, $pad_type); + + // https://github.com/viossat/arraytotexttable/blob/6b1af924478cb9c3a903269e304fff006fe0dbf4/src/ArrayToTextTable.php#L255 + $encoding = $encoding === null ? mb_internal_encoding() : $encoding; + $pad_before = $pad_type === STR_PAD_BOTH || $pad_type === STR_PAD_LEFT; + $pad_after = $pad_type === STR_PAD_BOTH || $pad_type === STR_PAD_RIGHT; + $pad_length -= mb_strlen($input, $encoding) + static::countCJK($input); + $target_length = $pad_before && $pad_after ? $pad_length / 2 : $pad_length; + + $repeat_times = ceil($target_length / mb_strlen($pad_string, $encoding)); + $repeated_string = str_repeat($pad_string, max(0, (int)$repeat_times)); + $before = $pad_before ? mb_substr($repeated_string, 0, (int)floor($target_length), $encoding) : ''; + $after = $pad_after ? mb_substr($repeated_string, 0, (int)ceil($target_length), $encoding) : ''; + + return $before . $input . $after; + } + + /** + * @param array $data + * @return $this + */ + protected function setData(array $data = []): static + { + if (!is_array($data)) { + $data = []; + } + $arrayData = []; + foreach ($data as $row) { + if (is_array($row)) { + $arrayData[] = $row; + } else if (is_object($row)) { + $arrayData[] = get_object_vars($row); + } + } + $this->data = $arrayData; + return $this; + } + + /** + * @param string $string + * @param string $color + * @return string + */ + protected function asciiColorWrap(string $string, string $color): string + { +// 30: 黑 +// 31: 红 +// 32: 绿 +// 33: 黄 +// 34: 蓝 +// 35: 紫 +// 36: 深绿 +// 37: 白色 + return "\033[1;" . $color . 'm' . $string . "\033[0m"; + } + + + /** + * @use 数组转表格(外置) + * @param array $data + * @param string|null $title + * @return array + */ + public static function array2table(array $data, ?string $title = null): array + { + $th_list = []; + // + $builder = new Builder(); + $builder->addRows($data); + // + if (!is_null($title)) { + $builder->setTitle($title); + } + // + foreach (explode("\r\n", $builder->renderTable()) as $value) { + if ($value) { + echo $value.PHP_EOL; + $th_list[] = $value; + } + } + return $th_list; + } +} \ No newline at end of file diff --git a/tests/AsciiTableTest.php b/tests/AsciiTableTest.php new file mode 100644 index 0000000..d6ab240 --- /dev/null +++ b/tests/AsciiTableTest.php @@ -0,0 +1,69 @@ + 'Mo 啊大苏打allie', + 'surname' => 'Alv萨达速度asarez', + 'email' => 'molliealvarez@example.com', + ], + [ + 'firstname' => 'Dianna', + 'surname' => 'Mcbride', + 'age' => 1111, + 'email' => 'diannamcbride@example.com', + ], + [ + 'firstname' => '撒旦撒旦asra', + 'surname' => 'Muel大大是打算的ler', + 'age' => 50, + 'email' => 'elviramueller@example.com', + ], + [ + 'firstname' => 'Corine', + 'surname' => 'Morton', + 'age' => 0, + ], + [ + 'firstname' => 'James', + 'surname' => 'Allison', + ], + [ + 'firstname' => 'Bowen这是哥', + 'surname' => 'Kelley', + 'age' => 50, + 'email' => 'bowenkelley@example.com', + ], + ]; + $renderer = new AsciiTable($data); + echo $renderer->getTable(); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..c6d6206 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,41 @@ + $libDir . '/tests/', + "Bhp\\" => $libDir . '/src/', +]; + +spl_autoload_register(static function ($class) use ($npMap) { + foreach ($npMap as $np => $dir) { + $file = $dir . str_replace('\\', '/', substr($class, strlen($np))) . '.php'; + + if (file_exists($file)) { + include $file; + } + } +}); + +if (is_file(dirname(__DIR__, 3) . '/autoload.php')) { + require dirname(__DIR__, 3) . '/autoload.php'; +} elseif (is_file(dirname(__DIR__) . '/vendor/autoload.php')) { + require dirname(__DIR__) . '/vendor/autoload.php'; +} \ No newline at end of file