From 396444a08956b38e5b1064bff1d6eda50f55284f Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 17:42:07 +0800 Subject: [PATCH 01/14] add Windows and macOS installation tips --- docs/guide/installation.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/guide/installation.md b/docs/guide/installation.md index e5c804b5..bc68cb16 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -15,7 +15,7 @@ cd zhamao-app/ bash -c "$(curl -fsSL https://api.zhamao.xin/go.sh)" # 安装完成后的启动框架命令(2.5.0 版本后可省略掉 runtime/php 前缀) -vendor/bin/start server +./zhamao server # 扩展用法:使用静态 PHP 版本的 Composer update runtime/composer update @@ -80,3 +80,15 @@ $ vendor/bin/start server ## 进阶环境部署和开发 炸毛框架还支持更多种启动方式,如源码模式、守护进程模式,具体后续有关环境和部署的进阶教程,请查看 [进阶开发](/advanced/) 部分! + +## Windows 注意事项 + +由于 Swoole 扩展目前无法原生支持 Windows 环境的 PHP,所以以上方式都是默认在 Linux、macOS 系统下的命令。 + +如果需要在 Windows 上开发和运行,可以使用 WSL(1 和 2 均可)、Linux 虚拟机、Docker 或 cygwin。 + +如果使用 WSL、虚拟机或 Docker,方式可以直接参考上方相关命令。如果使用 cygwin,可先从 [Swoole 官方仓库](https://github.com/swoole/swoole-src) 下载 swoole 的 cygwin 构建版本,然后下载 Composer 后安装依赖,直接运行框架即可。 + +## macOS 注意事项 + +macOS 理论上运行环境和 Linux 无异,但 macOS 由于不能静态编译,所以不能使用静态编译的 PHP 直接运行,需自行从 `Homebrew` 下载最新版 PHP(命令 `brew install php`),然后使用命令 `pecl install swoole` 来安装 Swoole 后运行框架。 From a9340371798f4eb6287edf0d9fb681b19ba57bf8 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 17:43:15 +0800 Subject: [PATCH 02/14] prevent user from executing phar stub file directly --- phpstan.neon | 1 + src/ZM/script_phar_stub.php | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/phpstan.neon b/phpstan.neon index 25b6cecf..66ea6614 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -11,3 +11,4 @@ parameters: dynamicConstantNames: - SWOOLE_VERSION - ZM_TEST_LOG_DEBUG + - _PHAR_STUB_ID diff --git a/src/ZM/script_phar_stub.php b/src/ZM/script_phar_stub.php index d50e6a4f..3650b878 100644 --- a/src/ZM/script_phar_stub.php +++ b/src/ZM/script_phar_stub.php @@ -2,6 +2,8 @@ declare(strict_types=1); +const _PHAR_STUB_ID = '__generated_id__'; + function loader__generated_id__() { $obj = json_decode(file_get_contents(__DIR__ . '/zmplugin.json'), true); @@ -12,4 +14,8 @@ function loader__generated_id__() require_once Phar::running() . '/' . $v; } } +if ('__generate' . 'd_id__' === _PHAR_STUB_ID) { + echo 'Cannot execute this file directly!' . PHP_EOL; + exit(1); +} return json_decode(file_get_contents(__DIR__ . '/zmplugin.json'), true) ?? ['zm_module' => false]; From 6c9e26ce83cdb389c0a2dcd4ffcf215e3298657a Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 17:53:49 +0800 Subject: [PATCH 03/14] fix bug #113 --- src/ZM/API/CQ.php | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/ZM/API/CQ.php b/src/ZM/API/CQ.php index eec46a6f..2eb57e24 100644 --- a/src/ZM/API/CQ.php +++ b/src/ZM/API/CQ.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace ZM\API; +use Stringable; use ZM\Console\Console; use ZM\Entity\CQObject; @@ -169,11 +170,11 @@ class CQ /** * 发送好友或群推荐名片 - * @param string $type 名片类型 + * @param int|string $type 名片类型 * @param int|string $id 好友或群ID * @return string CQ码 */ - public static function contact(string $type, $id): string + public static function contact($type, $id): string { return "[CQ:contact,type={$type},id={$id}]"; } @@ -306,11 +307,11 @@ class CQ /** * 反转义字符串中的CQ码敏感符号 - * @param string $msg 字符串 - * @param bool $is_content 如果是解码CQ码本体内容,则为false(默认),如果是参数内的字符串,则为true - * @return string 转义后的CQ码 + * @param int|string|Stringable $msg 字符串 + * @param bool $is_content 如果是解码CQ码本体内容,则为false(默认),如果是参数内的字符串,则为true + * @return string 转义后的CQ码 */ - public static function decode(string $msg, bool $is_content = false): string + public static function decode($msg, bool $is_content = false): string { $msg = str_replace(['&', '[', ']'], ['&', '[', ']'], $msg); if ($is_content) { @@ -321,10 +322,10 @@ class CQ /** * 简单反转义替换CQ码的方括号 - * @param string $str 字符串 - * @return string 字符串 + * @param int|string|Stringable $str 字符串 + * @return string 字符串 */ - public static function replace(string $str): string + public static function replace($str): string { $str = str_replace('{{', '[', $str); return str_replace('}}', ']', $str); @@ -332,11 +333,11 @@ class CQ /** * 转义CQ码的特殊字符,同encode - * @param string $msg 字符串 - * @param bool $is_content 如果是转义CQ码本体内容,则为false(默认),如果是参数内的字符串,则为true - * @return string 转义后的CQ码 + * @param int|string|Stringable $msg 字符串 + * @param bool $is_content 如果是转义CQ码本体内容,则为false(默认),如果是参数内的字符串,则为true + * @return string 转义后的CQ码 */ - public static function escape(string $msg, bool $is_content = false): string + public static function escape($msg, bool $is_content = false): string { $msg = str_replace(['&', '[', ']'], ['&', '[', ']'], $msg); if ($is_content) { @@ -347,11 +348,11 @@ class CQ /** * 转义CQ码的特殊字符 - * @param string $msg 字符串 - * @param bool $is_content 如果是转义CQ码本体内容,则为false(默认),如果是参数内的字符串,则为true - * @return string 转义后的CQ码 + * @param int|string|Stringable $msg 字符串 + * @param bool $is_content 如果是转义CQ码本体内容,则为false(默认),如果是参数内的字符串,则为true + * @return string 转义后的CQ码 */ - public static function encode(string $msg, bool $is_content = false): string + public static function encode($msg, bool $is_content = false): string { $msg = str_replace(['&', '[', ']'], ['&', '[', ']'], $msg); if ($is_content) { From 54eabc948f385b3652d2d4d4c9828d6e2b004648 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 21:03:59 +0800 Subject: [PATCH 04/14] fix instant mode related files --- src/ZM/Module/ModuleBase.php | 13 +++++++++++-- src/ZM/ZMServer.php | 19 +++++++++++++------ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/ZM/Module/ModuleBase.php b/src/ZM/Module/ModuleBase.php index 4bad0c26..1a64e017 100644 --- a/src/ZM/Module/ModuleBase.php +++ b/src/ZM/Module/ModuleBase.php @@ -9,23 +9,32 @@ namespace ZM\Module; */ abstract class ModuleBase { + /** @var string 模块名称 */ protected $module_name; + /** @var array 事件列表 */ protected $events = []; - public function __construct($module_name) + /** + * @param string $module_name 模块名称 + */ + public function __construct(string $module_name) { $this->module_name = $module_name; } /** - * @return mixed + * 获取模块名称 + * @return string */ public function getModuleName() { return $this->module_name; } + /** + * 获取事件列表 + */ public function getEvents(): array { return $this->events; diff --git a/src/ZM/ZMServer.php b/src/ZM/ZMServer.php index 798f35f8..cbc1203f 100644 --- a/src/ZM/ZMServer.php +++ b/src/ZM/ZMServer.php @@ -15,21 +15,31 @@ use ZM\Module\ModuleBase; */ class ZMServer { + /** @var string App名称 */ protected $app_name; /** @var ModuleBase[] */ protected $modules = []; - public function __construct($app_name) + /** + * @param string $app_name App名称 + */ + public function __construct(string $app_name) { $this->app_name = $app_name; } + /** + * @param mixed $module_class + */ public function addModule($module_class) { $this->modules[] = $module_class; } + /** + * @throws InitException + */ public function run() { Console::setLevel(4); @@ -39,7 +49,7 @@ class ZMServer } } echo "Running...\n"; - if (defined('WORKDING_DIR')) { + if (defined('WORKING_DIR')) { throw new InitException(); } @@ -57,10 +67,7 @@ class ZMServer (new Framework($options, true))->start(); } - /** - * @return mixed - */ - public function getAppName() + public function getAppName(): string { return $this->app_name; } From 94d6a513585955877408c3e25079bb87544bd671 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 21:05:10 +0800 Subject: [PATCH 05/14] fix EventMapIterator bug and add tests --- src/ZM/Event/EventMapIterator.php | 30 ++++++++++++++++------ src/ZM/global_functions.php | 9 ++++--- tests/ZM/Event/EventMapIteratorTest.php | 33 +++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 12 deletions(-) create mode 100644 tests/ZM/Event/EventMapIteratorTest.php diff --git a/src/ZM/Event/EventMapIterator.php b/src/ZM/Event/EventMapIterator.php index ea52243f..cebf3de9 100644 --- a/src/ZM/Event/EventMapIterator.php +++ b/src/ZM/Event/EventMapIterator.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace ZM\Event; use Iterator; -use ReturnTypeWillChange; +use ZM\Console\Console; class EventMapIterator implements Iterator { @@ -22,40 +22,54 @@ class EventMapIterator implements Iterator $this->class = $class; $this->method = $method; $this->event_name = $event_name; - $this->nextToValid(); } public function current() { + Console::debug('从 [' . $this->offset . '] 开始获取'); return EventManager::$event_map[$this->class][$this->method][$this->offset]; } public function next(): void { - ++$this->offset; + Console::debug('下一个offset为 [' . ++$this->offset . ']'); $this->nextToValid(); } - #[ReturnTypeWillChange] public function key() { - return $this->offset; + Console::debug('返回key:' . $this->offset); + return isset(EventManager::$event_map[$this->class][$this->method][$this->offset]) ? $this->offset : null; } - public function valid(): bool + public function valid($s = false): bool { - return isset(EventManager::$event_map[$this->class][$this->method][$this->offset]); + Console::debug( + "[{$this->offset}] " . + ($s ? 'valid' : '') . '存在:' . + (!isset(EventManager::$event_map[$this->class][$this->method][$this->offset]) ? Console::setColor('false', 'red') : ('true' . + (is_a(EventManager::$event_map[$this->class][$this->method][$this->offset], $this->event_name, true) ? ',是目标对象' : ',不是目标对象'))) + ); + return + isset(EventManager::$event_map[$this->class][$this->method][$this->offset]) + && is_a(EventManager::$event_map[$this->class][$this->method][$this->offset], $this->event_name, true); } public function rewind(): void { + Console::debug('回到0'); $this->offset = 0; + $this->nextToValid(); } private function nextToValid() { - while ($this->valid() && !is_a($this->current(), $this->event_name, true)) { + while ( + isset(EventManager::$event_map[$this->class][$this->method][$this->offset]) + && !is_a(EventManager::$event_map[$this->class][$this->method][$this->offset], $this->event_name, true) + ) { ++$this->offset; } + Console::debug('内部偏移offset为 [' . $this->offset . ']'); } } diff --git a/src/ZM/global_functions.php b/src/ZM/global_functions.php index 21f1dc28..6e5aa045 100644 --- a/src/ZM/global_functions.php +++ b/src/ZM/global_functions.php @@ -15,11 +15,11 @@ use ZM\ConnectionManager\ManagerGM; use ZM\Console\Console; use ZM\Container\Container; use ZM\Container\ContainerInterface; +use ZM\Container\EntryResolutionException; use ZM\Context\Context; use ZM\Context\ContextInterface; use ZM\Event\EventManager; use ZM\Exception\RobotNotFoundException; -use ZM\Exception\ZMKnownException; use ZM\Framework; use ZM\Store\LightCacheInside; use ZM\Store\ZMAtomic; @@ -183,8 +183,7 @@ function match_args(string $pattern, string $subject) /** * 判断当前连接类型是否为传入的$type * - * @param string $type 连接类型 - * @throws ZMKnownException + * @param string $type 连接类型 */ function current_connection_is(string $type): bool { @@ -737,7 +736,8 @@ function container(): ContainerInterface * 解析类实例(使用容器) * * @template T - * @param class-string $abstract + * @param class-string $abstract + * @throws EntryResolutionException * @return Closure|mixed|T */ function resolve(string $abstract, array $parameters = []) @@ -750,6 +750,7 @@ function resolve(string $abstract, array $parameters = []) * * @template T * @param null|class-string $abstract + * @throws EntryResolutionException * @return Closure|ContainerInterface|mixed|T */ function app(string $abstract = null, array $parameters = []) diff --git a/tests/ZM/Event/EventMapIteratorTest.php b/tests/ZM/Event/EventMapIteratorTest.php new file mode 100644 index 00000000..f3039fc9 --- /dev/null +++ b/tests/ZM/Event/EventMapIteratorTest.php @@ -0,0 +1,33 @@ +assertArrayNotHasKey(0, $arr); + $this->assertArrayNotHasKey(1, $arr); + $this->assertArrayHasKey(2, $arr); + $this->assertArrayHasKey(3, $arr); + $this->assertInstanceOf(CommandArgument::class, $arr[2]); + $this->assertInstanceOf(CommandArgument::class, $arr[3]); + $iterator = new EventMapIterator(Hello::class, 'closeUnknownConn', CommandArgument::class); + $ls = iterator_to_array($iterator); + $this->assertCount(0, $ls); + } +} From d28672e2ad166d587c15d75caa1224336f674b1a Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 21:14:56 +0800 Subject: [PATCH 06/14] add `--audit-mode` option --- .gitignore | 3 +++ src/ZM/Command/RunServerCommand.php | 1 + src/ZM/Framework.php | 11 +++++++++++ 3 files changed, 15 insertions(+) diff --git a/.gitignore b/.gitignore index 140b6813..57d64bf1 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ /temp/ /site/ +# 框架审计文件 +audit.log + # 进程锁文件 .daemon_pid .zm_worker_*.pid diff --git a/src/ZM/Command/RunServerCommand.php b/src/ZM/Command/RunServerCommand.php index d9bd5cb2..6633bb6a 100644 --- a/src/ZM/Command/RunServerCommand.php +++ b/src/ZM/Command/RunServerCommand.php @@ -48,6 +48,7 @@ class RunServerCommand extends Command new InputOption('polling-watch', null, null, '强制启用轮询模式监听'), new InputOption('no-state-check', null, null, '关闭启动前框架运行状态检查'), new InputOption('private-mode', null, null, '启动时隐藏MOTD和敏感信息'), + new InputOption('audit-mode', null, null, '启动时开启审计模式,独立将所有日志输出到文件供开发人员审计'), ]); $this->setDescription('Run zhamao-framework | 启动框架'); $this->setHelp('直接运行可以启动'); diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index 68ca535e..e6ce591a 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -744,6 +744,17 @@ class Framework case 'log-debug': Console::setLevel(4); break; + case 'audit-mode': + Console::warning('审计模式已开启,请正常执行需要审计的流程,然后Ctrl+C正常结束框架'); + Console::warning('审计的日志文件将存放到:' . DataProvider::getWorkingDir() . '/audit.log'); + if (file_exists(DataProvider::getWorkingDir() . '/audit.log')) { + unlink(DataProvider::getWorkingDir() . '/audit.log'); + } + Console::info('框架将于5秒后开始启动...'); + Console::setOutputFile(DataProvider::getWorkingDir() . '/audit.log'); + Console::setLevel(4); + sleep(5); + break; case 'log-theme': Console::$theme = $y; break; From 4defd8ac1bce501cddb4ba5a95d9cdf09f7fbe08 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 21:31:10 +0800 Subject: [PATCH 07/14] fix non-input environment bug #95 --- src/ZM/Module/ModuleUnpacker.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ZM/Module/ModuleUnpacker.php b/src/ZM/Module/ModuleUnpacker.php index b935c2ed..9f9728f4 100644 --- a/src/ZM/Module/ModuleUnpacker.php +++ b/src/ZM/Module/ModuleUnpacker.php @@ -197,6 +197,10 @@ class ModuleUnpacker $prompt = !is_string($this->module['unpack']['global-config-override']) ? '请根据模块提供者提供的要求进行修改 global.php 中对应的配置项' : $this->module['unpack']['global-config-override']; Console::warning('模块作者要求用户手动修改 global.php 配置文件中的项目:'); Console::warning('*' . $prompt); + if (STDIN === false) { + Console::warning('检测到终端无法输入,请手动修改 global.php 配置文件中的项目'); + return; + } echo Console::setColor('请输入修改模式,y(使用vim修改)/e(自行使用其他编辑器修改后确认)/N(默认暂不修改):[y/e/N] ', 'gold'); $r = strtolower(trim(fgets(STDIN))); switch ($r) { From b6742526aa976ac116ff02758dd500bb8bb1fd7a Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 21:31:38 +0800 Subject: [PATCH 08/14] add Terminal related test --- src/ZM/Event/EventDispatcher.php | 5 +++-- src/ZM/Utils/Terminal.php | 5 ++--- tests/ZM/Utils/TerminalTest.php | 33 ++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 tests/ZM/Utils/TerminalTest.php diff --git a/src/ZM/Event/EventDispatcher.php b/src/ZM/Event/EventDispatcher.php index 6be94696..30392351 100644 --- a/src/ZM/Event/EventDispatcher.php +++ b/src/ZM/Event/EventDispatcher.php @@ -9,6 +9,7 @@ use Closure; use Doctrine\Common\Annotations\AnnotationException; use Error; use Exception; +use Throwable; use ZM\Config\ZMConfig; use ZM\Console\Console; use ZM\Exception\InterruptException; @@ -110,7 +111,7 @@ class EventDispatcher /** * @param mixed ...$params - * @throws Exception + * @throws Throwable */ public function dispatchEvents(...$params) { @@ -137,7 +138,7 @@ class EventDispatcher } catch (InterruptException $e) { $this->store = $e->return_var; $this->status = self::STATUS_INTERRUPTED; - } catch (Exception|Error $e) { + } catch (Throwable $e) { $this->status = self::STATUS_EXCEPTION; throw $e; } diff --git a/src/ZM/Utils/Terminal.php b/src/ZM/Utils/Terminal.php index 92fd41fc..fd000a46 100644 --- a/src/ZM/Utils/Terminal.php +++ b/src/ZM/Utils/Terminal.php @@ -7,10 +7,10 @@ declare(strict_types=1); namespace ZM\Utils; use Doctrine\Common\Annotations\AnnotationReader; -use Error; use Exception; use ReflectionClass; use Swoole\Process; +use Throwable; use ZM\Annotation\Command\TerminalCommand; use ZM\ConnectionManager\ManagerGM; use ZM\Console\Console; @@ -22,8 +22,7 @@ class Terminal public static $default_commands = false; /** - * @throws Exception - * @throws Error + * @throws Throwable * @return bool * @noinspection PhpMissingReturnTypeInspection * @noinspection PhpUnused diff --git a/tests/ZM/Utils/TerminalTest.php b/tests/ZM/Utils/TerminalTest.php new file mode 100644 index 00000000..5d847ac1 --- /dev/null +++ b/tests/ZM/Utils/TerminalTest.php @@ -0,0 +1,33 @@ +assertStringContainsString('Initializing Terminal', $this->getActualOutput()); + } + + /** + * @throws Throwable + */ + public function testExecuteCommand() + { + Console::setLevel(2); + Terminal::executeCommand('echo zhamao-framework'); + $this->assertStringContainsString('zhamao-framework', $this->getActualOutput()); + } +} From d7728ebc25dc0739e9c1a3bb44f08fc8d6e2ab64 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 22:25:42 +0800 Subject: [PATCH 09/14] change test and PHPDoc --- src/ZM/global_functions.php | 5 +---- tests/ZM/Utils/TerminalTest.php | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ZM/global_functions.php b/src/ZM/global_functions.php index 6e5aa045..ac223c53 100644 --- a/src/ZM/global_functions.php +++ b/src/ZM/global_functions.php @@ -15,7 +15,6 @@ use ZM\ConnectionManager\ManagerGM; use ZM\Console\Console; use ZM\Container\Container; use ZM\Container\ContainerInterface; -use ZM\Container\EntryResolutionException; use ZM\Context\Context; use ZM\Context\ContextInterface; use ZM\Event\EventManager; @@ -736,8 +735,7 @@ function container(): ContainerInterface * 解析类实例(使用容器) * * @template T - * @param class-string $abstract - * @throws EntryResolutionException + * @param class-string $abstract * @return Closure|mixed|T */ function resolve(string $abstract, array $parameters = []) @@ -750,7 +748,6 @@ function resolve(string $abstract, array $parameters = []) * * @template T * @param null|class-string $abstract - * @throws EntryResolutionException * @return Closure|ContainerInterface|mixed|T */ function app(string $abstract = null, array $parameters = []) diff --git a/tests/ZM/Utils/TerminalTest.php b/tests/ZM/Utils/TerminalTest.php index 5d847ac1..438ec9c1 100644 --- a/tests/ZM/Utils/TerminalTest.php +++ b/tests/ZM/Utils/TerminalTest.php @@ -18,7 +18,7 @@ class TerminalTest extends TestCase { Console::setLevel(4); Terminal::init(); - $this->assertStringContainsString('Initializing Terminal', $this->getActualOutput()); + $this->expectOutputRegex('/Initializing\ Terminal/'); } /** @@ -28,6 +28,6 @@ class TerminalTest extends TestCase { Console::setLevel(2); Terminal::executeCommand('echo zhamao-framework'); - $this->assertStringContainsString('zhamao-framework', $this->getActualOutput()); + $this->expectOutputRegex('/zhamao-framework/'); } } From 583b9d7cf795dc29835aaf80ddde278283e2cc19 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 22:30:56 +0800 Subject: [PATCH 10/14] reset Console level in test --- src/ZM/API/CQ.php | 2 +- tests/ZM/Utils/TerminalTest.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ZM/API/CQ.php b/src/ZM/API/CQ.php index 2eb57e24..fea189da 100644 --- a/src/ZM/API/CQ.php +++ b/src/ZM/API/CQ.php @@ -354,7 +354,7 @@ class CQ */ public static function encode($msg, bool $is_content = false): string { - $msg = str_replace(['&', '[', ']'], ['&', '[', ']'], $msg); + $msg = str_replace(['&', '[', ']'], ['&', '[', ']'], strval($msg)); if ($is_content) { $msg = str_replace(',', ',', $msg); } diff --git a/tests/ZM/Utils/TerminalTest.php b/tests/ZM/Utils/TerminalTest.php index 438ec9c1..cac5ff94 100644 --- a/tests/ZM/Utils/TerminalTest.php +++ b/tests/ZM/Utils/TerminalTest.php @@ -18,6 +18,7 @@ class TerminalTest extends TestCase { Console::setLevel(4); Terminal::init(); + Console::setLevel(0); $this->expectOutputRegex('/Initializing\ Terminal/'); } @@ -28,6 +29,7 @@ class TerminalTest extends TestCase { Console::setLevel(2); Terminal::executeCommand('echo zhamao-framework'); + Console::setLevel(0); $this->expectOutputRegex('/zhamao-framework/'); } } From 0cb3308dd13bfbae3caeb144109d4721b35f2beb Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 22:43:23 +0800 Subject: [PATCH 11/14] fix compatibility for PHP 8.1 --- src/ZM/Event/EventMapIterator.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ZM/Event/EventMapIterator.php b/src/ZM/Event/EventMapIterator.php index cebf3de9..d5e12de6 100644 --- a/src/ZM/Event/EventMapIterator.php +++ b/src/ZM/Event/EventMapIterator.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace ZM\Event; use Iterator; +use ReturnTypeWillChange; use ZM\Console\Console; class EventMapIterator implements Iterator @@ -24,6 +25,7 @@ class EventMapIterator implements Iterator $this->event_name = $event_name; } + #[ReturnTypeWillChange] public function current() { Console::debug('从 [' . $this->offset . '] 开始获取'); @@ -36,6 +38,7 @@ class EventMapIterator implements Iterator $this->nextToValid(); } + #[ReturnTypeWillChange] public function key() { Console::debug('返回key:' . $this->offset); From d9302a3120c969c8896c5a48b375a8a7a6dec211 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 22:44:21 +0800 Subject: [PATCH 12/14] remove redundant Console setlevel --- tests/ZM/Event/EventMapIteratorTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ZM/Event/EventMapIteratorTest.php b/tests/ZM/Event/EventMapIteratorTest.php index f3039fc9..0bb2d82d 100644 --- a/tests/ZM/Event/EventMapIteratorTest.php +++ b/tests/ZM/Event/EventMapIteratorTest.php @@ -17,7 +17,6 @@ class EventMapIteratorTest extends TestCase { public function testIterator(): void { - Console::setLevel(4); $iterator = new EventMapIterator(Hello::class, 'randNum', CommandArgument::class); $arr = iterator_to_array($iterator); $this->assertArrayNotHasKey(0, $arr); From c9c76bfd1051dafafd92bc8ad6775df3a6d9080f Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 23:24:55 +0800 Subject: [PATCH 13/14] refactor CQ --- src/ZM/API/CQ.php | 152 ++++++++++++++++++++++------------------------ 1 file changed, 72 insertions(+), 80 deletions(-) diff --git a/src/ZM/API/CQ.php b/src/ZM/API/CQ.php index fea189da..fcfefb5a 100644 --- a/src/ZM/API/CQ.php +++ b/src/ZM/API/CQ.php @@ -17,11 +17,7 @@ class CQ */ public static function at($qq): string { - if (is_numeric($qq) || $qq === 'all') { - return '[CQ:at,qq=' . $qq . ']'; - } - Console::warning(zm_internal_errcode('E00035') . "传入的QQ号码({$qq})错误!"); - return ' '; + return self::buildCQ('at', ['qq' => $qq]); } /** @@ -31,11 +27,7 @@ class CQ */ public static function face($id): string { - if (is_numeric($id)) { - return '[CQ:face,id=' . $id . ']'; - } - Console::warning(zm_internal_errcode('E00035') . "传入的face id({$id})错误!"); - return ' '; + return self::buildCQ('face', ['id' => $id]); } /** @@ -49,13 +41,13 @@ class CQ */ public static function image(string $file, bool $cache = true, bool $flash = false, bool $proxy = true, int $timeout = -1): string { - return - '[CQ:image,file=' . self::encode($file, true) . - (!$cache ? ',cache=0' : '') . - ($flash ? ',type=flash' : '') . - (!$proxy ? ',proxy=false' : '') . - ($timeout != -1 ? (',timeout=' . $timeout) : '') . - ']'; + $optional_values = [ + 'cache' => !$cache ? 'cache=0' : '', + 'flash' => $flash ? 'type=flash' : '', + 'proxy' => !$proxy ? 'proxy=false' : '', + 'timeout' => $timeout != -1 ? 'timeout=' . $timeout : '', + ]; + return self::buildCQ('image', ['file' => $file], $optional_values); } /** @@ -69,13 +61,13 @@ class CQ */ public static function record(string $file, bool $magic = false, bool $cache = true, bool $proxy = true, int $timeout = -1): string { - return - '[CQ:record,file=' . self::encode($file, true) . - ($magic ? ',magic=1' : '') . - (!$cache ? ',cache=0' : '') . - (!$proxy ? ',proxy=false' : '') . - ($timeout != -1 ? (',timeout=' . $timeout) : '') . - ']'; + $optional_values = [ + 'magic' => $magic ? 'magic=true' : '', + 'cache' => !$cache ? 'cache=0' : '', + 'proxy' => !$proxy ? 'proxy=false' : '', + 'timeout' => $timeout != -1 ? 'timeout=' . $timeout : '', + ]; + return self::buildCQ('record', ['file' => $file], $optional_values); } /** @@ -88,12 +80,12 @@ class CQ */ public static function video(string $file, bool $cache = true, bool $proxy = true, int $timeout = -1): string { - return - '[CQ:video,file=' . self::encode($file, true) . - (!$cache ? ',cache=0' : '') . - (!$proxy ? ',proxy=false' : '') . - ($timeout != -1 ? (',timeout=' . $timeout) : '') . - ']'; + $optional_values = [ + 'cache' => !$cache ? 'cache=0' : '', + 'proxy' => !$proxy ? 'proxy=false' : '', + 'timeout' => $timeout != -1 ? 'timeout=' . $timeout : '', + ]; + return self::buildCQ('video', ['file' => $file], $optional_values); } /** @@ -132,7 +124,10 @@ class CQ */ public static function poke($type, $id, string $name = ''): string { - return "[CQ:poke,type={$type},id={$id}" . ($name != '' ? (',name=' . self::encode($name, true)) : '') . ']'; + $optional_values = [ + 'name' => $name ? 'name=' . $name : '', + ]; + return self::buildCQ('poke', ['type' => $type, 'id' => $id], $optional_values); } /** @@ -142,7 +137,7 @@ class CQ */ public static function anonymous(int $ignore = 1): string { - return '[CQ:anonymous' . ($ignore != 1 ? ',ignore=0' : '') . ']'; + return self::buildCQ('anonymous', [], ['ignore' => $ignore != 1 ? 'ignore=0' : '']); } /** @@ -155,17 +150,11 @@ class CQ */ public static function share(string $url, string $title, ?string $content = null, ?string $image = null): string { - if ($content === null) { - $c = ''; - } else { - $c = ',content=' . self::encode($content, true); - } - if ($image === null) { - $i = ''; - } else { - $i = ',image=' . self::encode($image, true); - } - return '[CQ:share,url=' . self::encode($url, true) . ',title=' . self::encode($title, true) . $c . $i . ']'; + $optional_values = [ + 'content' => $content ? 'content=' . self::encode($content, true) : '', + 'image' => $image ? 'image=' . self::encode($image, true) : '', + ]; + return self::buildCQ('share', ['url' => $url, 'title' => $title], $optional_values); } /** @@ -176,7 +165,7 @@ class CQ */ public static function contact($type, $id): string { - return "[CQ:contact,type={$type},id={$id}]"; + return self::buildCQ('contact', ['type' => $type, 'id' => $id]); } /** @@ -189,12 +178,11 @@ class CQ */ public static function location($lat, $lon, string $title = '', string $content = ''): string { - return '[CQ:location' . - ',lat=' . self::encode((string) $lat, true) . - ',lon=' . self::encode((string) $lon, true) . - ($title != '' ? (',title=' . self::encode($title, true)) : '') . - ($content != '' ? (',content=' . self::encode($content, true)) : '') . - ']'; + $optional_values = [ + 'title' => $title ? 'title=' . self::encode($title, true) : '', + 'content' => $content ? 'content=' . self::encode($content, true) : '', + ]; + return self::buildCQ('location', ['lat' => $lat, 'lon' => $lon], $optional_values); } /** @@ -216,26 +204,17 @@ class CQ case 'qq': case '163': case 'xiami': - return "[CQ:music,type={$type},id={$id_or_url}]"; + return self::buildCQ('music', ['type' => $type, 'id' => $id_or_url]); case 'custom': if ($title === null || $audio === null) { Console::warning(zm_internal_errcode('E00035') . '传入CQ码实例的标题和音频链接不能为空!'); return ' '; } - if ($content === null) { - $c = ''; - } else { - $c = ',content=' . self::encode($content, true); - } - if ($image === null) { - $i = ''; - } else { - $i = ',image=' . self::encode($image, true); - } - return '[CQ:music,type=custom,url=' . - self::encode($id_or_url, true) . - ',audio=' . self::encode($audio, true) . ',title=' . self::encode($title, true) . $c . $i . - ']'; + $optional_values = [ + 'content' => $content ? 'content=' . self::encode($content, true) : '', + 'image' => $image ? 'image=' . self::encode($image, true) : '', + ]; + return self::buildCQ('music', ['type' => 'custom', 'url' => $id_or_url, 'audio' => $audio, 'title' => $title], $optional_values); default: Console::warning(zm_internal_errcode('E00035') . "传入的music type({$type})错误!"); return ' '; @@ -249,7 +228,7 @@ class CQ */ public static function forward($id): string { - return '[CQ:forward,id=' . self::encode((string) $id) . ']'; + return self::buildCQ('forward', ['id' => $id]); } /** @@ -257,15 +236,15 @@ class CQ * 特殊说明: 需要使用单独的API /send_group_forward_msg 发送, 并且由于消息段较为复杂, 仅支持Array形式入参。 * 如果引用消息和自定义消息同时出现, 实际查看顺序将取消息段顺序。 * 另外按 CQHTTP 文档说明, data 应全为字符串, 但由于需要接收message 类型的消息, 所以 仅限此Type的content字段 支持Array套娃 - * @deprecated 这个不推荐使用,因为 go-cqhttp 官方没有对其提供CQ码模式相关支持,仅支持Array模式发送 * @param int|string $user_id 转发消息id * @param string $nickname 发送者显示名字 * @param string $content 具体消息 * @return string CQ码 + * @deprecated 这个不推荐使用,因为 go-cqhttp 官方没有对其提供CQ码模式相关支持,仅支持Array模式发送 */ public static function node($user_id, string $nickname, string $content): string { - return "[CQ:node,user_id={$user_id},nickname=" . self::encode($nickname, true) . ',content=' . self::encode($content, true) . ']'; + return self::buildCQ('node', ['user_id' => $user_id, 'nickname' => $nickname, 'content' => $content]); } /** @@ -275,7 +254,7 @@ class CQ */ public static function xml(string $data): string { - return '[CQ:xml,data=' . self::encode($data, true) . ']'; + return self::buildCQ('xml', ['data' => $data]); } /** @@ -286,7 +265,7 @@ class CQ */ public static function json(string $data, int $resid = 0): string { - return '[CQ:json,data=' . self::encode($data, true) . ',resid=' . $resid . ']'; + return self::buildCQ('json', ['data' => $data, 'resid' => $resid]); } /** @@ -297,12 +276,7 @@ class CQ */ public static function _custom(string $type_name, array $params): string { - $code = '[CQ:' . $type_name; - foreach ($params as $k => $v) { - $code .= ',' . $k . '=' . self::escape($v, true); - } - $code .= ']'; - return $code; + return self::buildCQ($type_name, $params); } /** @@ -313,7 +287,7 @@ class CQ */ public static function decode($msg, bool $is_content = false): string { - $msg = str_replace(['&', '[', ']'], ['&', '[', ']'], $msg); + $msg = str_replace(['&', '[', ']'], ['&', '[', ']'], (string) $msg); if ($is_content) { $msg = str_replace(',', ',', $msg); } @@ -327,7 +301,7 @@ class CQ */ public static function replace($str): string { - $str = str_replace('{{', '[', $str); + $str = str_replace('{{', '[', (string) $str); return str_replace('}}', ']', $str); } @@ -339,7 +313,7 @@ class CQ */ public static function escape($msg, bool $is_content = false): string { - $msg = str_replace(['&', '[', ']'], ['&', '[', ']'], $msg); + $msg = str_replace(['&', '[', ']'], ['&', '[', ']'], (string) $msg); if ($is_content) { $msg = str_replace(',', ',', $msg); } @@ -354,7 +328,7 @@ class CQ */ public static function encode($msg, bool $is_content = false): string { - $msg = str_replace(['&', '[', ']'], ['&', '[', ']'], strval($msg)); + $msg = str_replace(['&', '[', ']'], ['&', '[', ']'], (string) $msg); if ($is_content) { $msg = str_replace(',', ',', $msg); } @@ -439,4 +413,22 @@ class CQ } return $cqs; } + + private static function buildCQ(string $cq, array $array, array $optional_values = []): string + { + $str = '[CQ:' . $cq; + foreach ($array as $k => $v) { + if ($v === null) { + Console::warning('param ' . $k . ' cannot be set with null, empty CQ will returned!'); + return ' '; + } + $str .= ',' . $k . '=' . self::encode($v); + } + foreach ($optional_values as $v) { + if ($v !== '') { + $str .= ',' . $v; + } + } + return $str . ']'; + } } From fcd108adbee32cd4365cd021b7ef03cb5ae2373d Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 4 May 2022 23:28:21 +0800 Subject: [PATCH 14/14] cs-fix code --- tests/ZM/Event/EventMapIteratorTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ZM/Event/EventMapIteratorTest.php b/tests/ZM/Event/EventMapIteratorTest.php index 0bb2d82d..d07bc017 100644 --- a/tests/ZM/Event/EventMapIteratorTest.php +++ b/tests/ZM/Event/EventMapIteratorTest.php @@ -7,7 +7,6 @@ namespace Tests\ZM\Event; use Module\Example\Hello; use PHPUnit\Framework\TestCase; use ZM\Annotation\CQ\CommandArgument; -use ZM\Console\Console; use ZM\Event\EventMapIterator; /**