From 35dec947df03df7c385b96c69b2ac108c52c4bae Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Thu, 3 Nov 2022 10:18:17 +0800 Subject: [PATCH] add proxy server command and update lowest version of PHP >= 8.0 --- composer.json | 4 +- config/logging.php | 4 +- instant-plugin-demo.php | 2 +- src/Globals/global_class_alias.php | 1 + src/Globals/global_defines_app.php | 4 +- src/ZM/Annotation/AnnotationBase.php | 17 +- src/ZM/Annotation/AnnotationHandler.php | 11 +- src/ZM/Annotation/AnnotationParser.php | 8 +- src/ZM/Annotation/Closed.php | 2 +- src/ZM/Annotation/Framework/BindEvent.php | 2 +- src/ZM/Annotation/Framework/Init.php | 2 +- src/ZM/Annotation/Framework/Setup.php | 2 +- src/ZM/Annotation/Http/Controller.php | 2 +- src/ZM/Annotation/Http/Route.php | 2 +- src/ZM/Annotation/Middleware/Middleware.php | 2 +- src/ZM/Annotation/OneBot/BotCommand.php | 2 +- src/ZM/Annotation/OneBot/BotEvent.php | 2 +- src/ZM/Annotation/OneBot/CommandArgument.php | 3 +- src/ZM/Command/InitCommand.php | 3 +- src/ZM/Command/ProxyServerCommand.php | 561 ++++++++++++++++++ src/ZM/Command/Server/ServerStartCommand.php | 3 +- src/ZM/ConsoleApplication.php | 7 +- src/ZM/Container/BoundMethod.php | 19 +- src/ZM/Container/ContainerInterface.php | 29 +- src/ZM/Container/ContainerTrait.php | 83 ++- src/ZM/Container/EntryNotFoundException.php | 3 +- src/ZM/Container/EntryResolutionException.php | 3 +- src/ZM/Event/EventProvider.php | 3 +- src/ZM/Event/Listener/HttpEventListener.php | 6 +- src/ZM/Event/Listener/WSEventListener.php | 33 +- src/ZM/Event/Listener/WorkerEventListener.php | 7 +- src/ZM/Exception/ConfigException.php | 4 +- src/ZM/Exception/InterruptException.php | 4 +- src/ZM/Exception/InvalidArgumentException.php | 4 +- src/ZM/Exception/ZMException.php | 4 +- src/ZM/Exception/ZMKnownException.php | 4 +- src/ZM/Framework.php | 33 +- src/ZM/InstantApplication.php | 3 +- src/ZM/Middleware/MiddlewareHandler.php | 8 +- src/ZM/Store/Database/DBConnection.php | 12 +- src/ZM/Store/Database/DBPool.php | 8 +- src/ZM/Store/Database/DBStatement.php | 14 +- src/ZM/Store/Database/DBStatementWrapper.php | 40 +- src/ZM/Store/Database/DBWrapper.php | 83 ++- src/ZM/Store/FileSystem.php | 3 +- src/ZM/Utils/HttpUtil.php | 5 +- src/ZM/Utils/ReflectionUtil.php | 39 +- tests/ZM/Utils/ReflectionUtilTest.php | 24 +- tests/ZMResultPrinter.php | 3 +- 49 files changed, 826 insertions(+), 301 deletions(-) create mode 100644 src/ZM/Command/ProxyServerCommand.php diff --git a/composer.json b/composer.json index c6294e0e..dae5f56d 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ } ], "require": { - "php": "^7.4 || ^8.0 || ^8.1", + "php": "^8.0 || ^8.1", "ext-json": "*", "doctrine/dbal": "^2.13.1", "dragonmantank/cron-expression": "^3.3", @@ -47,7 +47,7 @@ "ext-redis": "If you use Redis in framework, you will need this extension", "league/climate": "Display columns and status in terminal" }, - "minimum-stability": "stable", + "minimum-stability": "dev", "prefer-stable": true, "autoload": { "psr-4": { diff --git a/config/logging.php b/config/logging.php index 3f8b2a98..de0602d3 100644 --- a/config/logging.php +++ b/config/logging.php @@ -7,7 +7,9 @@ use Psr\Log\LogLevel; use ZM\Logger\ConsoleLogger; return [ + // 设置默认的log等级 'level' => LogLevel::INFO, + // logger自定义回调 'logger' => static function (string $prefix = null): LoggerInterface { if ($prefix) { $prefix = strtoupper($prefix); @@ -16,7 +18,7 @@ return [ $prefix = app()->has('worker_id') ? '#' . app('worker_id') : 'MST'; } - $logger = new ConsoleLogger(zm_config('logging.level')); + $logger = new ConsoleLogger(config('logging.level')); $logger::$format = "[%date%] [%level%] [{$prefix}] %body%"; $logger::$date_format = 'Y-m-d H:i:s'; // 如果你喜欢旧版的日志格式,请取消下行注释 diff --git a/instant-plugin-demo.php b/instant-plugin-demo.php index f741912c..a0a0810d 100644 --- a/instant-plugin-demo.php +++ b/instant-plugin-demo.php @@ -2,7 +2,7 @@ declare(strict_types=1); -$plugin = new \ZM\Plugin\InstantPlugin(__DIR__); +$plugin = new InstantPlugin(__DIR__); /* * 发送 "测试 123",回复 "你好,123" diff --git a/src/Globals/global_class_alias.php b/src/Globals/global_class_alias.php index a0bff9e4..ad21884a 100644 --- a/src/Globals/global_class_alias.php +++ b/src/Globals/global_class_alias.php @@ -12,3 +12,4 @@ class_alias(\ZM\Annotation\OneBot\BotCommand::class, 'BotCommand'); class_alias(\ZM\Annotation\OneBot\BotEvent::class, 'BotEvent'); class_alias(\ZM\Annotation\OneBot\CommandArgument::class, 'CommandArgument'); class_alias(\ZM\Annotation\Closed::class, 'Closed'); +class_alias(\ZM\Plugin\InstantPlugin::class, 'InstantPlugin'); diff --git a/src/Globals/global_defines_app.php b/src/Globals/global_defines_app.php index 12f1cc62..e2af9fa4 100644 --- a/src/Globals/global_defines_app.php +++ b/src/Globals/global_defines_app.php @@ -57,8 +57,10 @@ if (Phar::running() !== '') { define('FRAMEWORK_ROOT_DIR', realpath(zm_dir(__DIR__ . '/../../'))); } +define('ZM_INIT_TIME', microtime(true)); + /* 定义用于存放框架运行状态的目录(Windows 可用) */ -define('ZM_STATE_DIR', TMP_DIR . '/.zm_' . sha1(FRAMEWORK_ROOT_DIR)); +define('ZM_STATE_DIR', TMP_DIR . '/.zm_' . sha1(ZM_INIT_TIME . FRAMEWORK_ROOT_DIR)); /* 对 global.php 在 Windows 下的兼容性考虑,因为 Windows 或者无 Swoole 环境时候无法运行 */ !defined('SWOOLE_BASE') && define('SWOOLE_BASE', 1) && define('SWOOLE_PROCESS', 2); diff --git a/src/ZM/Annotation/AnnotationBase.php b/src/ZM/Annotation/AnnotationBase.php index 6d979f51..4a3e033c 100644 --- a/src/ZM/Annotation/AnnotationBase.php +++ b/src/ZM/Annotation/AnnotationBase.php @@ -4,17 +4,12 @@ declare(strict_types=1); namespace ZM\Annotation; -use ArrayIterator; -use Closure; -use IteratorAggregate; -use Traversable; - -abstract class AnnotationBase implements IteratorAggregate +abstract class AnnotationBase implements \IteratorAggregate { public string $method = ''; /** - * @var Closure|string + * @var \Closure|string */ public $class = ''; @@ -33,7 +28,7 @@ abstract class AnnotationBase implements IteratorAggregate $str .= ($v ? 'TRUE' : 'FALSE'); } elseif (is_array($v)) { $str .= json_encode($v, JSON_UNESCAPED_UNICODE); - } elseif ($v instanceof Closure) { + } elseif ($v instanceof \Closure) { $str .= '@AnonymousFunction'; } elseif (is_null($v)) { $str .= 'NULL'; @@ -47,7 +42,7 @@ abstract class AnnotationBase implements IteratorAggregate /** * 在 InstantPlugin 下调用,设置回调或匿名函数 * - * @param Closure|string $method + * @param \Closure|string $method */ public function on($method): AnnotationBase { @@ -55,9 +50,9 @@ abstract class AnnotationBase implements IteratorAggregate return $this; } - public function getIterator(): Traversable + public function getIterator(): \Traversable { - return new ArrayIterator($this); + return new \ArrayIterator($this); } public function isInGroup(string $name): bool diff --git a/src/ZM/Annotation/AnnotationHandler.php b/src/ZM/Annotation/AnnotationHandler.php index 8290ebfa..0fc3101e 100644 --- a/src/ZM/Annotation/AnnotationHandler.php +++ b/src/ZM/Annotation/AnnotationHandler.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace ZM\Annotation; -use Throwable; use ZM\Exception\InterruptException; /** @@ -89,8 +88,8 @@ class AnnotationHandler * 调用注册了该注解的所有函数们 * 此处会遍历所有注册了当前注解的函数,并支持中间件插入 * - * @param mixed ...$params 传入的参数们 - * @throws Throwable + * @param mixed ...$params 传入的参数们 + * @throws \Throwable */ public function handleAll(...$params) { @@ -113,7 +112,7 @@ class AnnotationHandler // InterruptException 用于中断,这里必须 catch,并标记状态 $this->return_val = $e->return_var; $this->status = self::STATUS_INTERRUPTED; - } catch (Throwable $e) { + } catch (\Throwable $e) { // 其他类型的异常就顺势再抛出到外层,此层不做处理 $this->status = self::STATUS_EXCEPTION; throw $e; @@ -124,7 +123,7 @@ class AnnotationHandler * 调用单个注解 * * @throws InterruptException - * @throws Throwable + * @throws \Throwable */ public function handle(AnnotationBase $v, ?callable $rule_callback = null, ...$args): bool { @@ -144,7 +143,7 @@ class AnnotationHandler } catch (InterruptException $e) { // 这里直接抛出这个异常的目的就是给上层handleAll()捕获 throw $e; - } catch (Throwable $e) { + } catch (\Throwable $e) { // 其余的异常就交给中间件的异常捕获器过一遍,没捕获的则继续抛出 $this->status = self::STATUS_EXCEPTION; throw $e; diff --git a/src/ZM/Annotation/AnnotationParser.php b/src/ZM/Annotation/AnnotationParser.php index c897baf5..e417b902 100644 --- a/src/ZM/Annotation/AnnotationParser.php +++ b/src/ZM/Annotation/AnnotationParser.php @@ -7,8 +7,6 @@ namespace ZM\Annotation; use Doctrine\Common\Annotations\AnnotationReader; use Koriym\Attributes\AttributeReader; use Koriym\Attributes\DualReader; -use ReflectionClass; -use ReflectionException; use ReflectionMethod; use ZM\Annotation\Http\Controller; use ZM\Annotation\Http\Route; @@ -78,7 +76,7 @@ class AnnotationParser /** * 注册各个模块类的注解和模块level的排序 * - * @throws ReflectionException + * @throws \ReflectionException * @throws ConfigException */ public function parseAll() @@ -112,8 +110,8 @@ class AnnotationParser logger()->debug('正在检索 ' . $v); // 通过反射实现注解读取 - $reflection_class = new ReflectionClass($v); - $methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC); + $reflection_class = new \ReflectionClass($v); + $methods = $reflection_class->getMethods(\ReflectionMethod::IS_PUBLIC); $class_annotations = $reader->getClassAnnotations($reflection_class); // 这段为新加的:start // 这里将每个类里面所有的类注解、方法注解通通加到一颗大树上,后期解析 diff --git a/src/ZM/Annotation/Closed.php b/src/ZM/Annotation/Closed.php index 9b828f10..cb276801 100644 --- a/src/ZM/Annotation/Closed.php +++ b/src/ZM/Annotation/Closed.php @@ -14,7 +14,7 @@ use Doctrine\Common\Annotations\Annotation\Target; * @NamedArgumentConstructor() * @Target("ALL") */ -#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_ALL)] +#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_ALL)] class Closed extends AnnotationBase { } diff --git a/src/ZM/Annotation/Framework/BindEvent.php b/src/ZM/Annotation/Framework/BindEvent.php index e9abfc19..4e836b0b 100644 --- a/src/ZM/Annotation/Framework/BindEvent.php +++ b/src/ZM/Annotation/Framework/BindEvent.php @@ -20,7 +20,7 @@ use ZM\Annotation\Interfaces\Level; * @Target("METHOD") * @since 3.0.0 */ -#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)] +#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_METHOD)] class BindEvent extends AnnotationBase implements Level { /** diff --git a/src/ZM/Annotation/Framework/Init.php b/src/ZM/Annotation/Framework/Init.php index 2ab13107..c465289b 100644 --- a/src/ZM/Annotation/Framework/Init.php +++ b/src/ZM/Annotation/Framework/Init.php @@ -16,7 +16,7 @@ use ZM\Annotation\AnnotationBase; * @Target("METHOD") * @since 3.0.0 */ -#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)] +#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_METHOD)] class Init extends AnnotationBase { /** @var int */ diff --git a/src/ZM/Annotation/Framework/Setup.php b/src/ZM/Annotation/Framework/Setup.php index d3a19d88..9cff1246 100644 --- a/src/ZM/Annotation/Framework/Setup.php +++ b/src/ZM/Annotation/Framework/Setup.php @@ -16,7 +16,7 @@ use ZM\Annotation\AnnotationBase; * @Target("METHOD") * @since 3.0.0 */ -#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)] +#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_METHOD)] class Setup extends AnnotationBase { } diff --git a/src/ZM/Annotation/Http/Controller.php b/src/ZM/Annotation/Http/Controller.php index 0b3db4b4..9ccd4f74 100644 --- a/src/ZM/Annotation/Http/Controller.php +++ b/src/ZM/Annotation/Http/Controller.php @@ -17,7 +17,7 @@ use ZM\Annotation\Interfaces\ErgodicAnnotation; * @NamedArgumentConstructor() * @Target("CLASS") */ -#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] +#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS)] class Controller extends AnnotationBase implements ErgodicAnnotation { /** diff --git a/src/ZM/Annotation/Http/Route.php b/src/ZM/Annotation/Http/Route.php index 1ab8798b..42b46896 100644 --- a/src/ZM/Annotation/Http/Route.php +++ b/src/ZM/Annotation/Http/Route.php @@ -16,7 +16,7 @@ use ZM\Annotation\AnnotationBase; * @NamedArgumentConstructor() * @Target("METHOD") */ -#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)] +#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_METHOD)] class Route extends AnnotationBase { /** diff --git a/src/ZM/Annotation/Middleware/Middleware.php b/src/ZM/Annotation/Middleware/Middleware.php index 797703f3..d228d573 100644 --- a/src/ZM/Annotation/Middleware/Middleware.php +++ b/src/ZM/Annotation/Middleware/Middleware.php @@ -17,7 +17,7 @@ use ZM\Annotation\Interfaces\ErgodicAnnotation; * @NamedArgumentConstructor() * @Target("ALL") */ -#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_ALL)] +#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_ALL)] class Middleware extends AnnotationBase implements ErgodicAnnotation { /** diff --git a/src/ZM/Annotation/OneBot/BotCommand.php b/src/ZM/Annotation/OneBot/BotCommand.php index 3e57583f..8193d06a 100644 --- a/src/ZM/Annotation/OneBot/BotCommand.php +++ b/src/ZM/Annotation/OneBot/BotCommand.php @@ -20,7 +20,7 @@ use ZM\Exception\ZMKnownException; * @NamedArgumentConstructor() * @Target("METHOD") */ -#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)] +#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_METHOD)] class BotCommand extends AnnotationBase implements Level { /** @var string */ diff --git a/src/ZM/Annotation/OneBot/BotEvent.php b/src/ZM/Annotation/OneBot/BotEvent.php index 1baffb76..275a0b2f 100644 --- a/src/ZM/Annotation/OneBot/BotEvent.php +++ b/src/ZM/Annotation/OneBot/BotEvent.php @@ -16,7 +16,7 @@ use ZM\Annotation\AnnotationBase; * @Target("METHOD") * @NamedArgumentConstructor() */ -#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class BotEvent extends AnnotationBase { public ?string $type; diff --git a/src/ZM/Annotation/OneBot/CommandArgument.php b/src/ZM/Annotation/OneBot/CommandArgument.php index 7aaabec3..f9654ab5 100644 --- a/src/ZM/Annotation/OneBot/CommandArgument.php +++ b/src/ZM/Annotation/OneBot/CommandArgument.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace ZM\Annotation\OneBot; -use Attribute; use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; use Doctrine\Common\Annotations\Annotation\Required; use ZM\Annotation\AnnotationBase; @@ -18,7 +17,7 @@ use ZM\Exception\ZMKnownException; * @NamedArgumentConstructor() * @Target("ALL") */ -#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_ALL)] +#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_ALL)] class CommandArgument extends AnnotationBase implements ErgodicAnnotation { /** diff --git a/src/ZM/Command/InitCommand.php b/src/ZM/Command/InitCommand.php index 8a7f411f..6ec3d867 100644 --- a/src/ZM/Command/InitCommand.php +++ b/src/ZM/Command/InitCommand.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace ZM\Command; -use Phar; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -92,7 +91,7 @@ class InitCommand extends Command return 0; } if (LOAD_MODE === 2) { // 从phar启动的框架包,初始化的模式 - $phar_link = new Phar(__DIR__); + $phar_link = new \Phar(__DIR__); $current_dir = pathinfo($phar_link->getPath())['dirname']; chdir($current_dir); $phar_link = 'phar://' . $phar_link->getPath(); diff --git a/src/ZM/Command/ProxyServerCommand.php b/src/ZM/Command/ProxyServerCommand.php new file mode 100644 index 00000000..c9fafcad --- /dev/null +++ b/src/ZM/Command/ProxyServerCommand.php @@ -0,0 +1,561 @@ +stage) { + // 初始化环节 + case STAGE_INIT: + $request = []; + // 当前偏移量 + $offset = 0; + + // 检测buffer长度 + if (strlen($buffer) < 2) { + logger()->error('init socks5 failed. buffer too short.'); + $connection->send("\x05\xff"); + $connection->stage = STAGE_DESTROYED; + $connection->close(); + return; + } + + // Socks5 版本 + $request['ver'] = ord($buffer[$offset]); + ++$offset; + + // 认证方法数量 + $request['method_count'] = ord($buffer[$offset]); + ++$offset; + + if (strlen($buffer) < 2 + $request['method_count']) { + logger()->warning('init authentic failed. buffer too short.'); + $connection->send("\x05\xff"); + $connection->stage = STAGE_DESTROYED; + $connection->close(); + return; + } + // 客户端支持的认证方法 + $request['methods'] = []; + for ($i = 1; $i <= $request['method_count']; ++$i) { + $request['methods'][] = ord($buffer[$offset]); + ++$offset; + } + + foreach (($this->config['auth'] ?? []) as $k => $v) { + if (in_array($k, $request['methods'])) { + logger()->info("auth client via method {$k}"); + logger()->debug('send:' . bin2hex("\x05" . chr($k))); + + $connection->send("\x05" . chr($k)); + if ($k === 0) { + $connection->stage = STAGE_ADDR; + } else { + $connection->stage = STAGE_AUTH; + } + $connection->auth_type = $k; + return; + } + } + if ($connection->stage != STAGE_AUTH) { + logger()->warning('client has no matched auth methods'); + logger()->info('send:' . bin2hex("\x05\xff")); + $connection->send("\x05\xff"); + $connection->stage = STAGE_DESTROYED; + $connection->close(); + } + return; + case STAGE_ADDR: + $request = []; + // 当前偏移量 + $offset = 0; + + if (strlen($buffer) < 4) { + logger()->error('connect init failed. buffer too short.'); + $connection->stage = STAGE_DESTROYED; + + $response = []; + $response['ver'] = 5; + $response['rep'] = ERR_GENERAL; + $response['rsv'] = 0; + $response['addr_type'] = ADDRTYPE_IPV4; + $response['bind_addr'] = '0.0.0.0'; + $response['bind_port'] = 0; + + $connection->close($this->packResponse($response)); + return; + } + + // Socks 版本 + $request['ver'] = ord($buffer[$offset]); + ++$offset; + + // 命令 + $request['command'] = ord($buffer[$offset]); + ++$offset; + + // RSV + $request['rsv'] = ord($buffer[$offset]); + ++$offset; + + // AddressType + $request['addr_type'] = ord($buffer[$offset]); + ++$offset; + + // DestAddr + switch ($request['addr_type']) { + case ADDRTYPE_IPV4: + if (strlen($buffer) < 4 + 4) { + logger()->error('connect init failed.[ADDRTYPE_IPV4] buffer too short.'); + $connection->stage = STAGE_DESTROYED; + + $response = []; + $response['ver'] = 5; + $response['rep'] = ERR_GENERAL; + $response['rsv'] = 0; + $response['addr_type'] = ADDRTYPE_IPV4; + $response['bind_addr'] = '0.0.0.0'; + $response['bind_port'] = 0; + + $connection->close($this->packResponse($response)); + return; + } + + $tmp = substr($buffer, $offset, 4); + $ip = 0; + for ($i = 0; $i < 4; ++$i) { + // var_dump(ord($tmp[$i])); + $ip += ord($tmp[$i]) * pow(256, 3 - $i); + } + $request['dest_addr'] = long2ip($ip); + $offset += 4; + break; + case ADDRTYPE_HOST: + $request['host_len'] = ord($buffer[$offset]); + ++$offset; + + if (strlen($buffer) < 4 + 1 + $request['host_len']) { + logger()->error('connect init failed.[ADDRTYPE_HOST] buffer too short.'); + $connection->stage = STAGE_DESTROYED; + + $response = []; + $response['ver'] = 5; + $response['rep'] = ERR_GENERAL; + $response['rsv'] = 0; + $response['addr_type'] = ADDRTYPE_IPV4; + $response['bind_addr'] = '0.0.0.0'; + $response['bind_port'] = 0; + + $connection->close($this->packResponse($response)); + return; + } + + $request['dest_addr'] = substr($buffer, $offset, $request['host_len']); + $offset += $request['host_len']; + break; + case ADDRTYPE_IPV6: + default: + logger()->error('unsupport ipv6. [ADDRTYPE_IPV6].'); + $connection->stage = STAGE_DESTROYED; + + $response = []; + $response['ver'] = 5; + $response['rep'] = ERR_UNKNOW_ADDR_TYPE; + $response['rsv'] = 0; + $response['addr_type'] = ADDRTYPE_IPV4; + $response['bind_addr'] = '0.0.0.0'; + $response['bind_port'] = 0; + + $connection->close($this->packResponse($response)); + return; + } + + // DestPort + + if (strlen($buffer) < $offset + 2) { + logger()->error('connect init failed.[port] buffer too short.'); + $connection->stage = STAGE_DESTROYED; + + $response = []; + $response['ver'] = 5; + $response['rep'] = ERR_GENERAL; + $response['rsv'] = 0; + $response['addr_type'] = ADDRTYPE_IPV4; + $response['bind_addr'] = '0.0.0.0'; + $response['bind_port'] = 0; + + $connection->close($this->packResponse($response)); + return; + } + $portData = unpack('n', substr($buffer, $offset, 2)); + $request['dest_port'] = $portData[1]; + $offset += 2; + + // var_dump($request); + switch ($request['command']) { + case CMD_CONNECT: + logger()->info('tcp://' . $request['dest_addr'] . ':' . $request['dest_port']); + if ($request['addr_type'] == ADDRTYPE_HOST) { + if (!filter_var($request['dest_addr'], FILTER_VALIDATE_IP)) { + logger()->debug('resolve DNS ' . $request['dest_addr']); + $connection->stage = STAGE_DNS; + $addr = dns_get_record($request['dest_addr'], DNS_A); + $addr = $addr ? array_pop($addr) : null; + logger()->debug('DNS resolved ' . $request['dest_addr'] . ' => ' . $addr['ip']); + } else { + $addr['ip'] = $request['dest_addr']; + } + } else { + $addr['ip'] = $request['dest_addr']; + } + if ($addr) { + $connection->stage = STAGE_CONNECTING; + $remote_connection = new AsyncTcpConnection('tcp://' . $addr['ip'] . ':' . $request['dest_port']); + $remote_connection->onConnect = function ($remote_connection) use ($connection, $request) { + $connection->state = STAGE_STREAM; + $response = []; + $response['ver'] = 5; + $response['rep'] = 0; + $response['rsv'] = 0; + $response['addr_type'] = $request['addr_type']; + $response['bind_addr'] = '0.0.0.0'; + $response['bind_port'] = 18512; + + $connection->send($this->packResponse($response)); + $connection->pipe($remote_connection); + $remote_connection->pipe($connection); + logger()->debug('tcp://' . $request['dest_addr'] . ':' . $request['dest_port'] . ' [OK]'); + }; + $remote_connection->connect(); + } else { + logger()->debug('DNS resolve failed.'); + $connection->stage = STAGE_DESTROYED; + + $response = []; + $response['ver'] = 5; + $response['rep'] = ERR_HOST; + $response['rsv'] = 0; + $response['addr_type'] = ADDRTYPE_IPV4; + $response['bind_addr'] = '0.0.0.0'; + $response['bind_port'] = 0; + + $connection->close($this->packResponse($response)); + } + break; + case CMD_UDP_ASSOCIATE: + $connection->stage = STAGE_UDP_ASSOC; + var_dump('CMD_UDP_ASSOCIATE ' . ($this->config['udp_port'] ?? 2222)); + if ($this->config['udp_port'] == 0) { + $connection->udpWorker = new Worker('udp://0.0.0.0:0'); + /* @phpstan-ignore-next-line */ + $connection->udpWorker->incId = 0; + $connection->udpWorker->onMessage = function ($udp_connection, $data) use ($connection) { + $this->udpWorkerOnMessage($udp_connection, $data, $connection->udpWorker); + }; + $connection->udpWorker->listen(); + $listenInfo = stream_socket_get_name($connection->udpWorker->getMainSocket(), false); + [$bind_addr, $bind_port] = explode(':', $listenInfo); + } else { + $bind_port = $this->config['udp_port'] ?? 2222; + } + $bind_addr = $this->config['wanIP'] ?? '192.168.1.1'; + + $response['ver'] = 5; + $response['rep'] = 0; + $response['rsv'] = 0; + $response['addr_type'] = ADDRTYPE_IPV4; + $response['bind_addr'] = $bind_addr; + $response['bind_port'] = $bind_port; + + logger()->debug('send:' . bin2hex($this->packResponse($response))); + $connection->send($this->packResponse($response)); + break; + default: + logger()->error('connect init failed. unknow command.'); + $connection->stage = STAGE_DESTROYED; + + $response = []; + $response['ver'] = 5; + $response['rep'] = ERR_UNKNOW_COMMAND; + $response['rsv'] = 0; + $response['addr_type'] = ADDRTYPE_IPV4; + $response['bind_addr'] = '0.0.0.0'; + $response['bind_port'] = 0; + + $connection->close($this->packResponse($response)); + return; + } + } + } + + public function udpWorkerOnMessage($udp_connection, $data, &$worker) + { + logger()->debug('send:' . bin2hex($data)); + $request = []; + $offset = 0; + + $request['rsv'] = substr($data, $offset, 2); + $offset += 2; + + $request['frag'] = ord($data[$offset]); + ++$offset; + + $request['addr_type'] = ord($data[$offset]); + ++$offset; + + switch ($request['addr_type']) { + case ADDRTYPE_IPV4: + $tmp = substr($data, $offset, 4); + $ip = 0; + for ($i = 0; $i < 4; ++$i) { + $ip += ord($tmp[$i]) * pow(256, 3 - $i); + } + $request['dest_addr'] = long2ip($ip); + $offset += 4; + break; + case ADDRTYPE_HOST: + $request['host_len'] = ord($data[$offset]); + ++$offset; + + $request['dest_addr'] = substr($data, $offset, $request['host_len']); + $offset += $request['host_len']; + break; + case ADDRTYPE_IPV6: + if (strlen($data) < 22) { + echo "buffer too short\n"; + $error = true; + break; + } + echo "todo ipv6\n"; + $error = true; + // no break + default: + echo "unsupported addrtype {$request['addr_type']}\n"; + $error = true; + } + + $portData = unpack('n', substr($data, $offset, 2)); + $request['dest_port'] = $portData[1]; + $offset += 2; + // var_dump($request['dest_addr']); + if ($request['addr_type'] == ADDRTYPE_HOST) { + logger()->debug('解析DNS'); + $addr = dns_get_record($request['dest_addr'], DNS_A); + $addr = $addr ? array_pop($addr) : null; + logger()->debug('DNS 解析完成' . $addr['ip']); + } else { + $addr['ip'] = $request['dest_addr']; + } + // var_dump($request); + + // var_dump($udp_connection); + + $remote_connection = new AsyncUdpConnection('udp://' . $addr['ip'] . ':' . $request['dest_port']); + /* @phpstan-ignore-next-line */ + $remote_connection->id = $worker->incId++; + /* @phpstan-ignore-next-line */ + $remote_connection->udp_connection = $udp_connection; + $remote_connection->onConnect = function ($remote_connection) use ($data, $offset) { + $remote_connection->send(substr($data, $offset)); + }; + $remote_connection->onMessage = function ($remote_connection, $recv) use ($data, $offset, $udp_connection, $worker) { + $udp_connection->close(substr($data, 0, $offset) . $recv); + $remote_connection->close(); + unset($worker->udpConnections[$remote_connection->id]); + }; + /* @phpstan-ignore-next-line */ + $remote_connection->deadTime = time() + 3; + $remote_connection->connect(); + /* @phpstan-ignore-next-line */ + $worker->udpConnections[$remote_connection->id] = $remote_connection; + } + + /** + * 配置 + */ + protected function configure() + { + $this->setDescription('Start HTTP proxy server | 开启一个 HTTP 代理服务器'); + $this->addOption('type', 't', InputOption::VALUE_REQUIRED, '类型,可选http、socks'); + $this->addOption('host', 'H', InputOption::VALUE_REQUIRED, '监听地址,默认0.0.0.0'); + $this->addOption('port', 'P', InputOption::VALUE_REQUIRED, '监听端口,默认8080'); + $this->addOption('udp-port', 'U', InputOption::VALUE_REQUIRED, '监听端口,默认8080'); + $this->addOption('username', 'u', InputOption::VALUE_REQUIRED, '认证用的用户名'); + $this->addOption('password', 'p', InputOption::VALUE_REQUIRED, '认证用的密码'); + // ... + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + ob_logger_registered() || ob_logger_register(new ConsoleLogger()); + ini_set('memory_limit', '512M'); + // Create a TCP worker. + $address = $input->getOption('host') ?? '0.0.0.0'; + $port = $input->getOption('port') ?? '8080'; + $this->config['auth'] = [0 => true]; + $type = $input->getOption('type') ?? 'http'; + logger()->notice('Proxy server started at ' . $type . '://' . $address . ':' . $port); + switch ($type) { + case 'http': + $this->startHttpProxy($address, $port); + break; + case 'socks': + case 'socks5': + $this->startSocksProxy($address, $port); + break; + } + return 0; + } + + private function startHttpProxy($address, $port) + { + // Create a TCP worker. + /** @noinspection PhpObjectFieldsAreOnlyWrittenInspection */ + $worker = new Worker('tcp://' . $address . ':' . $port); + // 6 processes + $worker->count = 6; + // Worker name. + $worker->name = 'zhamao-http-proxy'; + Worker::$internal_running = true; + + // Emitted when data received from client. + $worker->onMessage = function ($connection, $buffer) { + // Parse http header. + [$method, $addr, $http_version] = explode(' ', $buffer); + $url_data = parse_url($addr); + $addr = !isset($url_data['port']) ? "{$url_data['host']}:80" : "{$url_data['host']}:{$url_data['port']}"; + // Async TCP connection. + $remote_connection = new AsyncTcpConnection("tcp://{$addr}"); + // CONNECT. + if ($method !== 'CONNECT') { + $remote_connection->send($buffer); + // POST GET PUT DELETE etc. + } else { + $connection->send("HTTP/1.1 200 Connection Established\r\n\r\n"); + logger()->info('Receive connection: ' . $addr); + } + // Pipe. + $remote_connection->pipe($connection); + $connection->pipe($remote_connection); + $remote_connection->connect(); + }; + Worker::runAll(); + } + + /** + * @see https://github.com/walkor/php-socks5/blob/master/start.php + * @param string $address 地址 + * @param int|string $port 端口 + */ + private function startSocksProxy(string $address, $port) + { + define('STAGE_INIT', 0); + define('STAGE_AUTH', 1); + define('STAGE_ADDR', 2); + define('STAGE_UDP_ASSOC', 3); + define('STAGE_DNS', 4); + define('STAGE_CONNECTING', 5); + define('STAGE_STREAM', 6); + define('STAGE_DESTROYED', -1); + + define('CMD_CONNECT', 1); + define('CMD_BIND', 2); + define('CMD_UDP_ASSOCIATE', 3); + + define('ERR_GENERAL', 1); + define('ERR_NOT_ALLOW', 2); + define('ERR_NETWORK', 3); + define('ERR_HOST', 4); + define('ERR_REFUSE', 5); + define('ERR_TTL_EXPIRED', 6); + define('ERR_UNKNOW_COMMAND', 7); + define('ERR_UNKNOW_ADDR_TYPE', 8); + define('ERR_UNKNOW', 9); + + define('ADDRTYPE_IPV4', 1); + define('ADDRTYPE_IPV6', 4); + define('ADDRTYPE_HOST', 3); + + define('METHOD_NO_AUTH', 0); + define('METHOD_GSSAPI', 1); + define('METHOD_USER_PASS', 2); + + /** @noinspection PhpObjectFieldsAreOnlyWrittenInspection */ + $worker = new Worker('tcp://' . $address . ':' . $port); + $worker->onConnect = function ($connection) { + $connection->stage = STAGE_INIT; + $connection->auth_type = null; + }; + $worker->onMessage = [$this, 'onSocksMessage']; + $worker->onWorkerStart = function () use ($address, $port) { + $udpWorker = new Worker('udp://' . $address . ':' . $port); + /* @phpstan-ignore-next-line */ + $udpWorker->incId = 0; + $udpWorker->onWorkerStart = function ($worker) { + $worker->udpConnections = []; + Timer::add(1, function () use ($worker) { + /* @phpstan-ignore-next-line */ + foreach ($worker->udpConnections as $id => $remote_connection) { + if ($remote_connection->deadTime < time()) { + $remote_connection->close(); + $remote_connection->udp_connection->close(); + unset($worker->udpConnections[$id]); + } + } + }); + }; + $udpWorker->onMessage = [$this, 'udpWorkerOnMessage']; + $udpWorker->listen(); + }; + $worker->onClose = function ($connection) { + logger()->info('client closed.'); + }; + $worker::$internal_running = true; + Worker::runAll(); + } + + private function packResponse($response) + { + $data = ''; + $data .= chr($response['ver']); + $data .= chr($response['rep']); + $data .= chr($response['rsv']); + $data .= chr($response['addr_type']); + + switch ($response['addr_type']) { + case ADDRTYPE_IPV4: + $tmp = explode('.', $response['bind_addr']); + foreach ($tmp as $block) { + $data .= chr(intval($block)); + } + break; + case ADDRTYPE_HOST: + $host_len = strlen($response['bind_addr']); + $data .= chr($host_len); + $data .= $response['bind_addr']; + break; + } + + $data .= pack('n', $response['bind_port']); + return $data; + } +} diff --git a/src/ZM/Command/Server/ServerStartCommand.php b/src/ZM/Command/Server/ServerStartCommand.php index 154a1d65..4c7e3f1a 100644 --- a/src/ZM/Command/Server/ServerStartCommand.php +++ b/src/ZM/Command/Server/ServerStartCommand.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace ZM\Command\Server; -use Exception; use OneBot\Driver\Process\ProcessManager; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -48,7 +47,7 @@ class ServerStartCommand extends ServerCommand /** * @throws ZMKnownException * @throws InitException - * @throws Exception + * @throws \Exception * @noinspection PhpComposerExtensionStubsInspection */ protected function execute(InputInterface $input, OutputInterface $output): int diff --git a/src/ZM/ConsoleApplication.php b/src/ZM/ConsoleApplication.php index f13a12de..c670fee2 100644 --- a/src/ZM/ConsoleApplication.php +++ b/src/ZM/ConsoleApplication.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace ZM; -use Exception; use Phar; use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\InputInterface; @@ -14,6 +13,7 @@ use ZM\Command\BuildCommand; use ZM\Command\CheckConfigCommand; use ZM\Command\Generate\SystemdGenerateCommand; use ZM\Command\InitCommand; +use ZM\Command\ProxyServerCommand; use ZM\Command\ReplCommand; use ZM\Command\Server\ServerReloadCommand; use ZM\Command\Server\ServerStartCommand; @@ -47,10 +47,11 @@ final class ConsoleApplication extends Application $this->add(new SystemdGenerateCommand()); // 生成systemd文件 $this->add(new BotCraftCommand()); // 用于从命令行创建插件 $this->add(new ReplCommand()); // 交互式控制台 + $this->add(new ProxyServerCommand()); // HTTP 代理服务器 if (LOAD_MODE === 1) { // 如果是 Composer 模式加载的,那么可以输入 check:config 命令,检查配置文件是否需要更新 $this->add(new CheckConfigCommand()); } - if (Phar::running() === '') { // 不是 Phar 模式的话,可以执行打包解包初始化命令 + if (\Phar::running() === '') { // 不是 Phar 模式的话,可以执行打包解包初始化命令 $this->add(new BuildCommand()); // 用于将整个应用打包为一个可执行的 phar $this->add(new InitCommand()); // 用于在 Composer 模式启动下,初始化脚手架文件 // $this->add(new PluginPackCommand()); // 用于打包一个子模块为 phar 并进行分发 @@ -69,7 +70,7 @@ final class ConsoleApplication extends Application { try { return parent::run($input, $output); - } catch (Exception $e) { + } catch (\Exception $e) { echo zm_internal_errcode('E00005') . "{$e->getMessage()} at {$e->getFile()}({$e->getLine()})" . PHP_EOL; exit(1); } diff --git a/src/ZM/Container/BoundMethod.php b/src/ZM/Container/BoundMethod.php index 301aa0b5..5cf55e7d 100644 --- a/src/ZM/Container/BoundMethod.php +++ b/src/ZM/Container/BoundMethod.php @@ -4,9 +4,6 @@ declare(strict_types=1); namespace ZM\Container; -use InvalidArgumentException; -use ReflectionException; -use ReflectionParameter; use ZM\Utils\ReflectionUtil; class BoundMethod @@ -14,11 +11,11 @@ class BoundMethod /** * 调用指定闭包、类方法并注入依赖 * - * @param Container $container - * @param callable|string $callback + * @param Container $container + * @param callable|string $callback * @return mixed - * @throws EntryResolutionException|ReflectionException - * @throws InvalidArgumentException + * @throws EntryResolutionException|\ReflectionException + * @throws \InvalidArgumentException */ public static function call(ContainerInterface $container, $callback, array $parameters = [], string $default_method = null) { @@ -35,7 +32,7 @@ class BoundMethod } if (!is_callable($callback)) { - throw new InvalidArgumentException('Callback is not callable.'); + throw new \InvalidArgumentException('Callback is not callable.'); } return call_user_func_array($callback, self::getMethodDependencies($container, $callback, $parameters)); @@ -44,8 +41,8 @@ class BoundMethod /** * Get all dependencies for a given method. * - * @param callable|string $callback - * @throws ReflectionException + * @param callable|string $callback + * @throws \ReflectionException */ protected static function getMethodDependencies(ContainerInterface $container, $callback, array $parameters = []): array { @@ -71,7 +68,7 @@ class BoundMethod */ protected static function addDependencyForCallParameter( ContainerInterface $container, - ReflectionParameter $parameter, + \ReflectionParameter $parameter, array &$parameters, array &$dependencies ): void { diff --git a/src/ZM/Container/ContainerInterface.php b/src/ZM/Container/ContainerInterface.php index 26616c66..e0df8a6a 100644 --- a/src/ZM/Container/ContainerInterface.php +++ b/src/ZM/Container/ContainerInterface.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace ZM\Container; -use Closure; use Psr\Container\ContainerInterface as PsrContainerInterface; /** @@ -32,9 +31,9 @@ interface ContainerInterface extends PsrContainerInterface /** * 注册绑定 * - * @param string $abstract 类或接口名 - * @param null|Closure|string $concrete 返回类实例的闭包,或是类名 - * @param bool $shared 是否共享 + * @param string $abstract 类或接口名 + * @param null|\Closure|string $concrete 返回类实例的闭包,或是类名 + * @param bool $shared 是否共享 */ public function bind(string $abstract, $concrete = null, bool $shared = false): void; @@ -43,17 +42,17 @@ interface ContainerInterface extends PsrContainerInterface * * 在已经绑定时不会重复注册 * - * @param string $abstract 类或接口名 - * @param null|Closure|string $concrete 返回类实例的闭包,或是类名 - * @param bool $shared 是否共享 + * @param string $abstract 类或接口名 + * @param null|\Closure|string $concrete 返回类实例的闭包,或是类名 + * @param bool $shared 是否共享 */ public function bindIf(string $abstract, $concrete = null, bool $shared = false): void; /** * 注册一个单例绑定 * - * @param string $abstract 类或接口名 - * @param null|Closure|string $concrete 返回类实例的闭包,或是类名 + * @param string $abstract 类或接口名 + * @param null|\Closure|string $concrete 返回类实例的闭包,或是类名 */ public function singleton(string $abstract, $concrete = null): void; @@ -62,8 +61,8 @@ interface ContainerInterface extends PsrContainerInterface * * 在已经绑定时不会重复注册 * - * @param string $abstract 类或接口名 - * @param null|Closure|string $concrete 返回类实例的闭包,或是类名 + * @param string $abstract 类或接口名 + * @param null|\Closure|string $concrete 返回类实例的闭包,或是类名 */ public function singletonIf(string $abstract, $concrete = null): void; @@ -81,7 +80,7 @@ interface ContainerInterface extends PsrContainerInterface * * @param string $abstract 类或接口名 */ - public function factory(string $abstract): Closure; + public function factory(string $abstract): \Closure; /** * 清除所有绑定和实例 @@ -92,9 +91,9 @@ interface ContainerInterface extends PsrContainerInterface * 获取一个绑定的实例 * * @template T - * @param class-string $abstract 类或接口名 - * @param array $parameters 参数 - * @return Closure|mixed|T 实例 + * @param class-string $abstract 类或接口名 + * @param array $parameters 参数 + * @return \Closure|mixed|T 实例 */ public function make(string $abstract, array $parameters = []); diff --git a/src/ZM/Container/ContainerTrait.php b/src/ZM/Container/ContainerTrait.php index a5562ed7..df106823 100644 --- a/src/ZM/Container/ContainerTrait.php +++ b/src/ZM/Container/ContainerTrait.php @@ -4,15 +4,8 @@ declare(strict_types=1); namespace ZM\Container; -use Closure; -use Exception; -use InvalidArgumentException; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; -use ReflectionClass; -use ReflectionException; -use ReflectionNamedType; -use ReflectionParameter; use ZM\Utils\ReflectionUtil; trait ContainerTrait @@ -55,7 +48,7 @@ trait ContainerTrait private static $aliases = []; /** - * @var Closure[][] + * @var \Closure[][] */ private static $extenders = []; @@ -103,7 +96,7 @@ trait ContainerTrait public function alias(string $abstract, string $alias): void { if ($alias === $abstract) { - throw new InvalidArgumentException("[{$abstract}] is same as [{$alias}]"); + throw new \InvalidArgumentException("[{$abstract}] is same as [{$alias}]"); } self::$aliases[$alias] = $abstract; @@ -116,9 +109,9 @@ trait ContainerTrait /** * 注册绑定 * - * @param string $abstract 类或接口名 - * @param null|Closure|string $concrete 返回类实例的闭包,或是类名 - * @param bool $shared 是否共享 + * @param string $abstract 类或接口名 + * @param null|\Closure|string $concrete 返回类实例的闭包,或是类名 + * @param bool $shared 是否共享 */ public function bind(string $abstract, $concrete = null, bool $shared = false): void { @@ -135,7 +128,7 @@ trait ContainerTrait } // 如果不是闭包,则认为是类名,此时将其包装在一个闭包中,以方便后续处理 - if (!$concrete instanceof Closure) { + if (!$concrete instanceof \Closure) { $concrete = $this->getClosure($abstract, $concrete); } @@ -151,9 +144,9 @@ trait ContainerTrait * * 在已经绑定时不会重复注册 * - * @param string $abstract 类或接口名 - * @param null|Closure|string $concrete 返回类实例的闭包,或是类名 - * @param bool $shared 是否共享 + * @param string $abstract 类或接口名 + * @param null|\Closure|string $concrete 返回类实例的闭包,或是类名 + * @param bool $shared 是否共享 */ public function bindIf(string $abstract, $concrete = null, bool $shared = false): void { @@ -165,8 +158,8 @@ trait ContainerTrait /** * 注册一个单例绑定 * - * @param string $abstract 类或接口名 - * @param null|Closure|string $concrete 返回类实例的闭包,或是类名 + * @param string $abstract 类或接口名 + * @param null|\Closure|string $concrete 返回类实例的闭包,或是类名 */ public function singleton(string $abstract, $concrete = null): void { @@ -178,8 +171,8 @@ trait ContainerTrait * * 在已经绑定时不会重复注册 * - * @param string $abstract 类或接口名 - * @param null|Closure|string $concrete 返回类实例的闭包,或是类名 + * @param string $abstract 类或接口名 + * @param null|\Closure|string $concrete 返回类实例的闭包,或是类名 */ public function singletonIf(string $abstract, $concrete = null): void { @@ -216,7 +209,7 @@ trait ContainerTrait * * @param string $abstract 类或接口名 */ - public function factory(string $abstract): Closure + public function factory(string $abstract): \Closure { return function () use ($abstract) { return $this->make($abstract); @@ -247,7 +240,7 @@ trait ContainerTrait * @template T * @param class-string $abstract 类或接口名 * @param array $parameters 参数 - * @return Closure|mixed|T 实例 + * @return \Closure|mixed|T 实例 * @throws EntryResolutionException */ public function make(string $abstract, array $parameters = []) @@ -316,20 +309,20 @@ trait ContainerTrait /** * 实例化具体的类实例 * - * @param Closure|string $concrete 类名或对应的闭包 + * @param \Closure|string $concrete 类名或对应的闭包 * @return mixed * @throws EntryResolutionException */ public function build($concrete) { // 如果传入的是闭包,则直接执行并返回 - if ($concrete instanceof Closure) { + if ($concrete instanceof \Closure) { return $concrete($this, $this->getLastParameterOverride()); } try { - $reflection = new ReflectionClass($concrete); - } catch (ReflectionException $e) { + $reflection = new \ReflectionClass($concrete); + } catch (\ReflectionException $e) { throw new EntryResolutionException("指定的类 {$concrete} 不存在", 0, $e); } @@ -402,7 +395,7 @@ trait ContainerTrait { try { return $this->make($id); - } catch (Exception $e) { + } catch (\Exception $e) { if ($this->has($id)) { throw new EntryResolutionException('', 0, $e); } @@ -428,10 +421,10 @@ trait ContainerTrait /** * 扩展一个类或接口 * - * @param string $abstract 类或接口名 - * @param Closure $closure 扩展闭包 + * @param string $abstract 类或接口名 + * @param \Closure $closure 扩展闭包 */ - public function extend(string $abstract, Closure $closure): void + public function extend(string $abstract, \Closure $closure): void { $abstract = $this->getAlias($abstract); @@ -467,8 +460,8 @@ trait ContainerTrait /** * 获取对应类型的所有扩展器 * - * @param string $abstract 类或接口名 - * @return Closure[] + * @param string $abstract 类或接口名 + * @return \Closure[] */ protected function getExtenders(string $abstract): array { @@ -505,7 +498,7 @@ trait ContainerTrait * @param string $abstract 类或接口名 * @param string $concrete 实际类名 */ - protected function getClosure(string $abstract, string $concrete): Closure + protected function getClosure(string $abstract, string $concrete): \Closure { return static function ($container, $parameters = []) use ($abstract, $concrete) { $method = $abstract === $concrete ? 'build' : 'make'; @@ -542,7 +535,7 @@ trait ContainerTrait /** * 解析依赖 * - * @param ReflectionParameter[] $dependencies + * @param \ReflectionParameter[] $dependencies * @throws EntryResolutionException */ protected function resolveDependencies(array $dependencies): array @@ -587,7 +580,7 @@ trait ContainerTrait /** * 判断传入的参数是否存在覆盖参数 */ - protected function hasParameterOverride(ReflectionParameter $parameter): bool + protected function hasParameterOverride(\ReflectionParameter $parameter): bool { return array_key_exists($parameter->name, $this->getLastParameterOverride()); } @@ -597,7 +590,7 @@ trait ContainerTrait * * @return mixed */ - protected function getParameterOverride(ReflectionParameter $parameter) + protected function getParameterOverride(\ReflectionParameter $parameter) { return $this->getLastParameterOverride()[$parameter->name]; } @@ -605,7 +598,7 @@ trait ContainerTrait /** * 判断传入的参数是否存在临时注入的参数 */ - protected function hasParameterTypeOverride(ReflectionParameter $parameter): bool + protected function hasParameterTypeOverride(\ReflectionParameter $parameter): bool { if (!$parameter->hasType()) { return false; @@ -613,7 +606,7 @@ trait ContainerTrait $type = $parameter->getType(); - if (!$type instanceof ReflectionNamedType || $type->isBuiltin()) { + if (!$type instanceof \ReflectionNamedType || $type->isBuiltin()) { return false; } @@ -625,11 +618,11 @@ trait ContainerTrait * * @return mixed */ - protected function getParameterTypeOverride(ReflectionParameter $parameter) + protected function getParameterTypeOverride(\ReflectionParameter $parameter) { $type = $parameter->getType(); - if (!$type instanceof ReflectionNamedType) { + if (!$type instanceof \ReflectionNamedType) { return []; } @@ -642,7 +635,7 @@ trait ContainerTrait * @return mixed 对应类型的默认值 * @throws EntryResolutionException 如参数不存在默认值,则抛出异常 */ - protected function resolvePrimitive(ReflectionParameter $parameter) + protected function resolvePrimitive(\ReflectionParameter $parameter) { if ($parameter->isDefaultValueAvailable()) { return $parameter->getDefaultValue(); @@ -657,7 +650,7 @@ trait ContainerTrait * @return mixed * @throws EntryResolutionException 如果无法解析类,则抛出异常 */ - protected function resolveClass(ReflectionParameter $parameter) + protected function resolveClass(\ReflectionParameter $parameter) { try { // 尝试解析 @@ -681,8 +674,8 @@ trait ContainerTrait /** * 获取类名的实际类型 * - * @param string $abstract 类或接口名 - * @return Closure|string + * @param string $abstract 类或接口名 + * @return \Closure|string */ protected function getConcrete(string $abstract) { @@ -701,7 +694,7 @@ trait ContainerTrait */ protected function isBuildable($concrete, string $abstract): bool { - return $concrete === $abstract || $concrete instanceof Closure; + return $concrete === $abstract || $concrete instanceof \Closure; } /** diff --git a/src/ZM/Container/EntryNotFoundException.php b/src/ZM/Container/EntryNotFoundException.php index 03332d17..175b88e5 100644 --- a/src/ZM/Container/EntryNotFoundException.php +++ b/src/ZM/Container/EntryNotFoundException.php @@ -4,9 +4,8 @@ declare(strict_types=1); namespace ZM\Container; -use Exception; use Psr\Container\NotFoundExceptionInterface; -class EntryNotFoundException extends Exception implements NotFoundExceptionInterface +class EntryNotFoundException extends \Exception implements NotFoundExceptionInterface { } diff --git a/src/ZM/Container/EntryResolutionException.php b/src/ZM/Container/EntryResolutionException.php index 639d656c..dba5481f 100644 --- a/src/ZM/Container/EntryResolutionException.php +++ b/src/ZM/Container/EntryResolutionException.php @@ -4,9 +4,8 @@ declare(strict_types=1); namespace ZM\Container; -use Exception; use Psr\Container\ContainerExceptionInterface; -class EntryResolutionException extends Exception implements ContainerExceptionInterface +class EntryResolutionException extends \Exception implements ContainerExceptionInterface { } diff --git a/src/ZM/Event/EventProvider.php b/src/ZM/Event/EventProvider.php index ca62b256..e33ad95a 100644 --- a/src/ZM/Event/EventProvider.php +++ b/src/ZM/Event/EventProvider.php @@ -6,7 +6,6 @@ namespace ZM\Event; use OneBot\Driver\Interfaces\SortedProviderInterface; use OneBot\Util\Singleton; -use Stringable; class EventProvider implements SortedProviderInterface { @@ -73,7 +72,7 @@ class EventProvider implements SortedProviderInterface /** * 排序事件 * - * @param string|Stringable $name + * @param string|\Stringable $name */ private function sortEvents($name) { diff --git a/src/ZM/Event/Listener/HttpEventListener.php b/src/ZM/Event/Listener/HttpEventListener.php index 20a21745..21dd3cc9 100644 --- a/src/ZM/Event/Listener/HttpEventListener.php +++ b/src/ZM/Event/Listener/HttpEventListener.php @@ -8,8 +8,6 @@ use OneBot\Driver\Event\Http\HttpRequestEvent; use OneBot\Http\HttpFactory; use OneBot\Http\Stream; use OneBot\Util\Singleton; -use Stringable; -use Throwable; use ZM\Annotation\AnnotationHandler; use ZM\Annotation\Framework\BindEvent; use ZM\Annotation\Http\Route; @@ -26,7 +24,7 @@ class HttpEventListener * 这里处理框架特有的内容,比如: * 路由、断点续传、注解再分发等 * - * @throws Throwable + * @throws \Throwable */ public function onRequest999(HttpRequestEvent $event) { @@ -56,7 +54,7 @@ class HttpEventListener $div->class = $node['class']; $starttime = microtime(true); $handler->handle($div, null, $params, $event->getRequest(), $event); - if (is_string($val = $handler->getReturnVal()) || ($val instanceof Stringable)) { + if (is_string($val = $handler->getReturnVal()) || ($val instanceof \Stringable)) { $event->withResponse(HttpFactory::getInstance()->createResponse(200, null, [], Stream::create($val))); } elseif ($event->getResponse() === null) { $event->withResponse(HttpFactory::getInstance()->createResponse(500)); diff --git a/src/ZM/Event/Listener/WSEventListener.php b/src/ZM/Event/Listener/WSEventListener.php index c303a937..45d46a82 100644 --- a/src/ZM/Event/Listener/WSEventListener.php +++ b/src/ZM/Event/Listener/WSEventListener.php @@ -4,20 +4,32 @@ declare(strict_types=1); namespace ZM\Event\Listener; +use OneBot\Driver\Event\WebSocket\WebSocketCloseEvent; use OneBot\Driver\Event\WebSocket\WebSocketOpenEvent; +use OneBot\Driver\Process\ProcessManager; use OneBot\Http\HttpFactory; use OneBot\Util\Singleton; use ZM\Container\ContainerServicesProvider; +use ZM\Process\ProcessStateManager; class WSEventListener { use Singleton; + private static int $ws_counter = 0; + + private static array $conn_handle = []; + public function onWebSocketOpen(WebSocketOpenEvent $event) { + // 计数,最多只能接入 1024 个连接,为了适配多进程 + ++self::$ws_counter; + if (self::$ws_counter >= 1024) { + $event->withResponse(HttpFactory::getInstance()->createResponse(503)); + return; + } // 注册容器 resolve(ContainerServicesProvider::class)->registerServices('connection'); - // 判断是不是 OneBot 12 反向 WS 连进来的,通过 Sec-WebSocket-Protocol 头 $line = explode('.', $event->getRequest()->getHeaderLine('Sec-WebSocket-Protocol'), 2); if ($line[0] === '12') { @@ -32,6 +44,25 @@ class WSEventListener } } // 这里下面为连接准入,允许接入反向 WS,TODO + if (ProcessStateManager::$process_mode['worker'] > 1) { + // 如果开了多 Worker,则需要将连接信息写入文件,以便跨进程读取 + $info = ['impl' => $line[1] ?? 'unknown']; + self::$conn_handle[$event->getFd()] = $info; + file_put_contents(zm_dir(ZM_STATE_DIR . '/.WS' . $event->getFd() . '.' . ProcessManager::getProcessId()), json_encode($info)); + } } } + + public function onWebSocketClose(WebSocketCloseEvent $event) + { + --self::$ws_counter; + // 删除连接信息 + $fd = $event->getFd(); + $filename = zm_dir(ZM_STATE_DIR . '/.WS' . $fd . '.' . ProcessManager::getProcessId()); + if (file_exists($filename)) { + unlink($filename); + } + unset(self::$conn_handle[$fd]); + resolve(ContainerServicesProvider::class)->cleanup(); + } } diff --git a/src/ZM/Event/Listener/WorkerEventListener.php b/src/ZM/Event/Listener/WorkerEventListener.php index b832a67e..f37ff459 100644 --- a/src/ZM/Event/Listener/WorkerEventListener.php +++ b/src/ZM/Event/Listener/WorkerEventListener.php @@ -6,7 +6,6 @@ namespace ZM\Event\Listener; use OneBot\Driver\Process\ProcessManager; use OneBot\Util\Singleton; -use Throwable; use ZM\Annotation\AnnotationHandler; use ZM\Annotation\AnnotationMap; use ZM\Annotation\AnnotationParser; @@ -26,7 +25,7 @@ class WorkerEventListener /** * Driver 的 Worker 进程启动后执行的事件 * - * @throws Throwable + * @throws \Throwable */ public function onWorkerStart999() { @@ -113,7 +112,7 @@ class WorkerEventListener /** * 加载用户代码资源,包括普通插件、单文件插件、Composer 插件等 - * @throws Throwable + * @throws \Throwable */ private function initUserPlugins() { @@ -143,7 +142,7 @@ class WorkerEventListener } /** - * @throws Throwable + * @throws \Throwable */ private function dispatchInit() { diff --git a/src/ZM/Exception/ConfigException.php b/src/ZM/Exception/ConfigException.php index 369f2d93..7d415943 100644 --- a/src/ZM/Exception/ConfigException.php +++ b/src/ZM/Exception/ConfigException.php @@ -4,15 +4,13 @@ declare(strict_types=1); namespace ZM\Exception; -use Throwable; - class ConfigException extends ZMException { public const UNSUPPORTED_FILE_TYPE = 'E00079'; public const LOAD_CONFIG_FAILED = 'E00080'; - public function __construct($err_code, $message = '', $code = 0, Throwable $previous = null) + public function __construct($err_code, $message = '', $code = 0, \Throwable $previous = null) { parent::__construct(zm_internal_errcode($err_code) . $message, $code, $previous); } diff --git a/src/ZM/Exception/InterruptException.php b/src/ZM/Exception/InterruptException.php index 7d7813f7..3dcb2286 100644 --- a/src/ZM/Exception/InterruptException.php +++ b/src/ZM/Exception/InterruptException.php @@ -4,13 +4,11 @@ declare(strict_types=1); namespace ZM\Exception; -use Throwable; - class InterruptException extends ZMException { public $return_var; - public function __construct($return_var = null, $message = '', $code = 0, Throwable $previous = null) + public function __construct($return_var = null, $message = '', $code = 0, \Throwable $previous = null) { parent::__construct($message, $code, $previous); $this->return_var = $return_var; diff --git a/src/ZM/Exception/InvalidArgumentException.php b/src/ZM/Exception/InvalidArgumentException.php index 76c95a0b..460b0bf0 100644 --- a/src/ZM/Exception/InvalidArgumentException.php +++ b/src/ZM/Exception/InvalidArgumentException.php @@ -4,11 +4,9 @@ declare(strict_types=1); namespace ZM\Exception; -use Throwable; - class InvalidArgumentException extends ZMException { - public function __construct($message = '', $code = 0, Throwable $previous = null) + public function __construct($message = '', $code = 0, \Throwable $previous = null) { parent::__construct(zm_internal_errcode('E00074') . $message, $code, $previous); } diff --git a/src/ZM/Exception/ZMException.php b/src/ZM/Exception/ZMException.php index a79a797d..e5fef9a8 100644 --- a/src/ZM/Exception/ZMException.php +++ b/src/ZM/Exception/ZMException.php @@ -4,8 +4,6 @@ declare(strict_types=1); namespace ZM\Exception; -use Exception; - -abstract class ZMException extends Exception +abstract class ZMException extends \Exception { } diff --git a/src/ZM/Exception/ZMKnownException.php b/src/ZM/Exception/ZMKnownException.php index 0665b2a1..cd176782 100644 --- a/src/ZM/Exception/ZMKnownException.php +++ b/src/ZM/Exception/ZMKnownException.php @@ -4,11 +4,9 @@ declare(strict_types=1); namespace ZM\Exception; -use Throwable; - class ZMKnownException extends ZMException { - public function __construct($err_code, $message = '', $code = 0, Throwable $previous = null) + public function __construct($err_code, $message = '', $code = 0, \Throwable $previous = null) { parent::__construct(zm_internal_errcode($err_code) . $message, $code, $previous); } diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index 025034fd..e1ed2986 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace ZM; -use Exception; use OneBot\Driver\Driver; use OneBot\Driver\Event\DriverInitEvent; use OneBot\Driver\Event\Http\HttpRequestEvent; @@ -12,6 +11,7 @@ use OneBot\Driver\Event\Process\ManagerStartEvent; use OneBot\Driver\Event\Process\ManagerStopEvent; use OneBot\Driver\Event\Process\WorkerStartEvent; use OneBot\Driver\Event\Process\WorkerStopEvent; +use OneBot\Driver\Event\WebSocket\WebSocketCloseEvent; use OneBot\Driver\Event\WebSocket\WebSocketOpenEvent; use OneBot\Driver\Interfaces\DriverInitPolicy; use OneBot\Driver\Swoole\SwooleDriver; @@ -46,23 +46,23 @@ class Framework public const VERSION_ID = 627; /** @var string 版本名称 */ - public const VERSION = '3.0.0-alpha3'; + public const VERSION = '3.0.0-alpha4'; /** @var array 传入的参数 */ - protected $argv; + protected array $argv; /** @var Driver|SwooleDriver|WorkermanDriver OneBot驱动 */ - protected $driver; + protected SwooleDriver|Driver|WorkermanDriver $driver; /** @var array> 启动注解列表 */ - protected $setup_annotations = []; + protected array $setup_annotations = []; /** * 框架初始化文件 * * @param array $argv 传入的参数(见 ServerStartCommand) * @throws InitException - * @throws Exception + * @throws \Exception */ public function __construct(array $argv = []) { @@ -77,7 +77,7 @@ class Framework } /** - * @throws Exception + * @throws \Exception */ public function init(): Framework { @@ -96,7 +96,7 @@ class Framework /** * 启动框架 */ - public function start() + public function start(): void { // 对多进程有效,记录当前已经加载的所有文件,最后在 Worker 进程中比较可重载的文件,用于排错 global $zm_loaded_files; @@ -249,9 +249,9 @@ class Framework * 初始化驱动及相关事件 * 实例化 Driver 对象 * - * @throws Exception + * @throws \Exception */ - public function initDriver() + public function initDriver(): void { switch ($driver = config('global.driver')) { case 'swoole': @@ -278,10 +278,8 @@ class Framework * 初始化框架并输出一些信息 * * 绑定、注册框架本身的事件到 Driver 的 EventProvider 中 - * - * @throws ConfigException */ - public function initFramework() + public function initFramework(): void { // private-mode 模式下,不输出任何内容 if (!$this->argv['private-mode']) { @@ -303,17 +301,16 @@ class Framework ob_event_provider()->addEventListener(DriverInitEvent::getName(), [MasterEventListener::getInstance(), 'onMasterStart'], 999); // websocket 事件 ob_event_provider()->addEventListener(WebSocketOpenEvent::getName(), [WSEventListener::getInstance(), 'onWebSocketOpen'], 999); + ob_event_provider()->addEventListener(WebSocketCloseEvent::getName(), [WSEventListener::getInstance(), 'onWebSocketClose'], 999); // 框架多进程依赖 - if (defined('ZM_PID_DIR') && !is_dir(ZM_STATE_DIR)) { + if (defined('ZM_STATE_DIR') && !is_dir(ZM_STATE_DIR)) { mkdir(ZM_STATE_DIR); } } /** * 打印属性表格 - * @noinspection PhpComposerExtensionStubsInspection - * @throws ConfigException */ private function printProperties(): void { @@ -491,9 +488,9 @@ class Framework /** * 初始化 OnSetup 注解 */ - private function initSetupAnnotations() + private function initSetupAnnotations(): void { - if (Phar::running() !== '') { + if (\Phar::running() !== '') { // 在 Phar 下,不需要新启动进程了,因为 Phar 没办法重载,自然不需要考虑多进程的加载 reload 问题 require FRAMEWORK_ROOT_DIR . '/src/Globals/script_setup_loader.php'; $r = _zm_setup_loader(); diff --git a/src/ZM/InstantApplication.php b/src/ZM/InstantApplication.php index 74c4e703..95f79556 100644 --- a/src/ZM/InstantApplication.php +++ b/src/ZM/InstantApplication.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace ZM; -use Exception; use ZM\Command\Server\ServerStartCommand; use ZM\Exception\InitException; use ZM\Plugin\InstantPlugin; @@ -44,7 +43,7 @@ class InstantApplication extends InstantPlugin } /** - * @throws Exception + * @throws \Exception */ public function run() { diff --git a/src/ZM/Middleware/MiddlewareHandler.php b/src/ZM/Middleware/MiddlewareHandler.php index 4da7f490..4ef6fd4a 100644 --- a/src/ZM/Middleware/MiddlewareHandler.php +++ b/src/ZM/Middleware/MiddlewareHandler.php @@ -4,9 +4,7 @@ declare(strict_types=1); namespace ZM\Middleware; -use Closure; use OneBot\Util\Singleton; -use Throwable; use ZM\Exception\InvalidArgumentException; class MiddlewareHandler @@ -107,7 +105,7 @@ class MiddlewareHandler $after_result = container()->call($this->middlewares[$item[0]]['after'], $args); } } - } catch (Throwable $e) { + } catch (\Throwable $e) { while (isset($this->stack[$stack_id]) && ($item = array_pop($this->stack[$stack_id])) !== null) { // 如果是 pipeline 形式的中间件,则使用闭包回去 if (class_exists($item[0]) && is_a($item[0], PipelineInterface::class, true)) { @@ -134,7 +132,7 @@ class MiddlewareHandler /** * @throws InvalidArgumentException - * @throws Throwable + * @throws \Throwable */ public function process(callable $callback, ...$args) { @@ -169,7 +167,7 @@ class MiddlewareHandler */ public function getStackId(callable $callback): string { - if ($callback instanceof Closure) { + if ($callback instanceof \Closure) { // 闭包情况下,直接根据闭包的ID号来找stack return strval(spl_object_id($callback)); } diff --git a/src/ZM/Store/Database/DBConnection.php b/src/ZM/Store/Database/DBConnection.php index 827fdf7c..5c022031 100644 --- a/src/ZM/Store/Database/DBConnection.php +++ b/src/ZM/Store/Database/DBConnection.php @@ -8,12 +8,10 @@ namespace ZM\Store\Database; use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\ParameterType; -use PDO; -use PDOException; class DBConnection implements Connection { - /** @var PDO */ + /** @var \PDO */ private $conn; private $pool_name; @@ -42,7 +40,7 @@ class DBConnection implements Connection logger()->debug('Running SQL prepare: ' . $sql); $statement = $this->conn->prepare($sql, $options); assert($statement !== false); - } catch (PDOException $exception) { + } catch (\PDOException $exception) { throw new DBException($exception->getMessage(), 0, $exception); } return new DBStatement($statement); @@ -56,7 +54,7 @@ class DBConnection implements Connection try { $statement = $this->conn->query(...$args); assert($statement !== false); - } catch (PDOException $exception) { + } catch (\PDOException $exception) { throw new DBException($exception->getMessage(), 0, $exception); } return new DBStatement($statement); @@ -78,7 +76,7 @@ class DBConnection implements Connection $statement = $this->conn->exec($sql); assert($statement !== false); return $statement; - } catch (PDOException $exception) { + } catch (\PDOException $exception) { throw new DBException($exception->getMessage(), 0, $exception); } } @@ -91,7 +89,7 @@ class DBConnection implements Connection { try { return $name === null ? $this->conn->lastInsertId() : $this->conn->lastInsertId($name); - } catch (PDOException $exception) { + } catch (\PDOException $exception) { throw new DBException($exception->getMessage(), 0, $exception); } } diff --git a/src/ZM/Store/Database/DBPool.php b/src/ZM/Store/Database/DBPool.php index e5660646..c3578f4c 100644 --- a/src/ZM/Store/Database/DBPool.php +++ b/src/ZM/Store/Database/DBPool.php @@ -12,8 +12,6 @@ use OneBot\Driver\Swoole\ObjectPool as SwooleObjectPool; use OneBot\Driver\Swoole\SwooleDriver; use OneBot\Driver\Workerman\ObjectPool as WorkermanObjectPool; use OneBot\Driver\Workerman\WorkermanDriver; -use PDO; -use RuntimeException; use ZM\Store\FileSystem; class DBPool @@ -60,10 +58,10 @@ class DBPool } switch (Driver::getActiveDriverClass()) { case WorkermanDriver::class: - self::$pools[$name] = new WorkermanObjectPool($size, PDO::class, $connect_str, ...$args); + self::$pools[$name] = new WorkermanObjectPool($size, \PDO::class, $connect_str, ...$args); break; case SwooleDriver::class: - self::$pools[$name] = new SwooleObjectPool($size, PDO::class, $connect_str, ...$args); + self::$pools[$name] = new SwooleObjectPool($size, \PDO::class, $connect_str, ...$args); } } @@ -76,7 +74,7 @@ class DBPool public static function pool(string $name) { if (!isset(self::$pools[$name]) && count(self::$pools) !== 1) { - throw new RuntimeException("Pool {$name} not found"); + throw new \RuntimeException("Pool {$name} not found"); } return self::$pools[$name] ?? self::$pools[array_key_first(self::$pools)]; } diff --git a/src/ZM/Store/Database/DBStatement.php b/src/ZM/Store/Database/DBStatement.php index 092215df..5004b6cc 100644 --- a/src/ZM/Store/Database/DBStatement.php +++ b/src/ZM/Store/Database/DBStatement.php @@ -10,14 +10,10 @@ namespace ZM\Store\Database; use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\ParameterType; -use IteratorAggregate; -use PDO; -use PDOStatement; -use Traversable; -class DBStatement implements IteratorAggregate, Statement +class DBStatement implements \IteratorAggregate, Statement { - /** @var PDOStatement */ + /** @var \PDOStatement */ private $statement; public function __construct($obj) @@ -50,12 +46,12 @@ class DBStatement implements IteratorAggregate, Statement return $this->statement->setFetchMode($fetchMode); } - public function fetch($fetchMode = PDO::FETCH_ASSOC, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + public function fetch($fetchMode = \PDO::FETCH_ASSOC, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0) { return $this->statement->fetch($fetchMode, $cursorOrientation, $cursorOffset); } - public function fetchAll($fetchMode = PDO::FETCH_ASSOC, $fetchArgument = null, $ctorArgs = null) + public function fetchAll($fetchMode = \PDO::FETCH_ASSOC, $fetchArgument = null, $ctorArgs = null) { if ($fetchArgument === null && $ctorArgs === null) { return $this->statement->fetchAll($fetchMode); @@ -102,7 +98,7 @@ class DBStatement implements IteratorAggregate, Statement return $this->statement->rowCount(); } - public function getIterator(): Traversable + public function getIterator(): \Traversable { while (($result = $this->statement->fetch()) !== false) { yield $result; diff --git a/src/ZM/Store/Database/DBStatementWrapper.php b/src/ZM/Store/Database/DBStatementWrapper.php index 4aa9c421..5f8eb56e 100644 --- a/src/ZM/Store/Database/DBStatementWrapper.php +++ b/src/ZM/Store/Database/DBStatementWrapper.php @@ -11,8 +11,6 @@ namespace ZM\Store\Database; use Doctrine\DBAL\Driver\ResultStatement; use Doctrine\DBAL\ForwardCompatibility\Result; -use Throwable; -use Traversable; class DBStatementWrapper { @@ -52,7 +50,7 @@ class DBStatementWrapper { try { return $this->stmt->fetchNumeric(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -66,7 +64,7 @@ class DBStatementWrapper { try { return $this->stmt->fetchAssociative(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -80,7 +78,7 @@ class DBStatementWrapper { try { return $this->stmt->fetchOne(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -93,7 +91,7 @@ class DBStatementWrapper { try { return $this->stmt->fetchAllNumeric(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -106,7 +104,7 @@ class DBStatementWrapper { try { return $this->stmt->fetchAllAssociative(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -119,7 +117,7 @@ class DBStatementWrapper { try { return $this->stmt->fetchAllKeyValue(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -132,7 +130,7 @@ class DBStatementWrapper { try { return $this->stmt->fetchAllAssociativeIndexed(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -145,7 +143,7 @@ class DBStatementWrapper { try { return $this->stmt->fetchFirstColumn(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -154,11 +152,11 @@ class DBStatementWrapper * wrapper method * @throws DBException */ - public function iterateNumeric(): Traversable + public function iterateNumeric(): \Traversable { try { return $this->stmt->iterateNumeric(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -167,11 +165,11 @@ class DBStatementWrapper * wrapper method * @throws DBException */ - public function iterateAssociative(): Traversable + public function iterateAssociative(): \Traversable { try { return $this->stmt->iterateAssociative(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -180,11 +178,11 @@ class DBStatementWrapper * wrapper method * @throws DBException */ - public function iterateKeyValue(): Traversable + public function iterateKeyValue(): \Traversable { try { return $this->stmt->iterateKeyValue(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -193,11 +191,11 @@ class DBStatementWrapper * wrapper method * @throws DBException */ - public function iterateAssociativeIndexed(): Traversable + public function iterateAssociativeIndexed(): \Traversable { try { return $this->stmt->iterateAssociativeIndexed(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -206,11 +204,11 @@ class DBStatementWrapper * wrapper method * @throws DBException */ - public function iterateColumn(): Traversable + public function iterateColumn(): \Traversable { try { return $this->stmt->iterateColumn(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -224,7 +222,7 @@ class DBStatementWrapper { try { return $this->stmt->rowCount(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } diff --git a/src/ZM/Store/Database/DBWrapper.php b/src/ZM/Store/Database/DBWrapper.php index f134416f..73576954 100644 --- a/src/ZM/Store/Database/DBWrapper.php +++ b/src/ZM/Store/Database/DBWrapper.php @@ -4,14 +4,11 @@ declare(strict_types=1); namespace ZM\Store\Database; -use Closure; use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Types\Type; -use Throwable; -use Traversable; class DBWrapper { @@ -33,7 +30,7 @@ class DBWrapper } else { throw new DBException('Cannot find database config named "' . $name . '" !'); } - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -76,7 +73,7 @@ class DBWrapper { try { return $this->connection->fetchAssociative($query, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), intval($e->getCode()), $e); } } @@ -90,7 +87,7 @@ class DBWrapper { try { return $this->connection->fetchNumeric($query, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -103,7 +100,7 @@ class DBWrapper { try { return $this->connection->fetchOne($query, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -124,7 +121,7 @@ class DBWrapper { try { return $this->connection->delete($table, $criteria, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -155,7 +152,7 @@ class DBWrapper { try { return $this->connection->update($table, $data, $criteria, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -169,7 +166,7 @@ class DBWrapper { try { return $this->connection->insert($table, $data, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -206,7 +203,7 @@ class DBWrapper { try { return $this->connection->fetchAllNumeric($query, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -224,7 +221,7 @@ class DBWrapper { try { return $this->connection->fetchAllAssociative($query, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -241,7 +238,7 @@ class DBWrapper { try { return $this->connection->fetchAllKeyValue($query, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -259,7 +256,7 @@ class DBWrapper { try { return $this->connection->fetchAllAssociativeIndexed($query, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -277,7 +274,7 @@ class DBWrapper { try { return $this->connection->fetchFirstColumn($query, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -288,14 +285,14 @@ class DBWrapper * @param array|array $params Query parameters * @param array|array $types Parameter types * - * @return Traversable> + * @return \Traversable> * @throws DBException */ - public function iterateNumeric(string $query, array $params = [], array $types = []): Traversable + public function iterateNumeric(string $query, array $params = [], array $types = []): \Traversable { try { return $this->connection->iterateNumeric($query, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -306,14 +303,14 @@ class DBWrapper * @param array|array $params Query parameters * @param array|array $types Parameter types * - * @return Traversable> + * @return \Traversable> * @throws DBException */ - public function iterateAssociative(string $query, array $params = [], array $types = []): Traversable + public function iterateAssociative(string $query, array $params = [], array $types = []): \Traversable { try { return $this->connection->iterateAssociative($query, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -324,14 +321,14 @@ class DBWrapper * @param array|array $params Query parameters * @param array|array $types Parameter types * - * @return Traversable + * @return \Traversable * @throws DBException */ - public function iterateKeyValue(string $query, array $params = [], array $types = []): Traversable + public function iterateKeyValue(string $query, array $params = [], array $types = []): \Traversable { try { return $this->connection->iterateKeyValue($query, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -342,14 +339,14 @@ class DBWrapper * @param array|array $params Query parameters * @param array|array $types Parameter types * - * @return Traversable> + * @return \Traversable> * @throws DBException */ - public function iterateAssociativeIndexed(string $query, array $params = [], array $types = []): Traversable + public function iterateAssociativeIndexed(string $query, array $params = [], array $types = []): \Traversable { try { return $this->connection->iterateAssociativeIndexed($query, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -360,14 +357,14 @@ class DBWrapper * @param array|array $params Query parameters * @param array|array $types Parameter types * - * @return Traversable + * @return \Traversable * @throws DBException */ - public function iterateColumn(string $query, array $params = [], array $types = []): Traversable + public function iterateColumn(string $query, array $params = [], array $types = []): \Traversable { try { return $this->connection->iterateColumn($query, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -385,7 +382,7 @@ class DBWrapper try { $query = $this->connection->executeQuery($sql, $params, $types, $qcp); return new DBStatementWrapper($query); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw $e; // throw new DBException($e->getMessage(), intval($e->getCode()), $e); } @@ -403,7 +400,7 @@ class DBWrapper try { $query = $this->connection->executeCacheQuery($sql, $params, $types, $qcp); return new DBStatementWrapper($query); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -421,7 +418,7 @@ class DBWrapper { try { return $this->connection->executeStatement($sql, $params, $types); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -449,14 +446,14 @@ class DBWrapper * @return mixed * @throws DBException */ - public function transactional(Closure $func) + public function transactional(\Closure $func) { $this->beginTransaction(); try { $res = $func($this); $this->commit(); return $res; - } catch (Throwable $e) { + } catch (\Throwable $e) { $this->rollBack(); throw new DBException($e->getMessage(), $e->getCode(), $e); } @@ -470,7 +467,7 @@ class DBWrapper { try { $this->connection->setNestTransactionsWithSavepoints($nest_transactions_with_savepoints); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -499,7 +496,7 @@ class DBWrapper { try { return $this->connection->commit(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -512,7 +509,7 @@ class DBWrapper { try { return $this->connection->rollBack(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -526,7 +523,7 @@ class DBWrapper { try { $this->connection->createSavepoint($savepoint); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -540,7 +537,7 @@ class DBWrapper { try { $this->connection->releaseSavepoint($savepoint); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -554,7 +551,7 @@ class DBWrapper { try { $this->connection->rollbackSavepoint($savepoint); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -567,7 +564,7 @@ class DBWrapper { try { $this->connection->setRollbackOnly(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } @@ -580,7 +577,7 @@ class DBWrapper { try { return $this->connection->isRollbackOnly(); - } catch (Throwable $e) { + } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); } } diff --git a/src/ZM/Store/FileSystem.php b/src/ZM/Store/FileSystem.php index ec23914b..041f9faf 100644 --- a/src/ZM/Store/FileSystem.php +++ b/src/ZM/Store/FileSystem.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace ZM\Store; -use RuntimeException; use ZM\Utils\ZMUtil; class FileSystem @@ -85,7 +84,7 @@ class FileSystem public static function createDir(string $path): void { if (!is_dir($path) && !mkdir($path, 0777, true) && !is_dir($path)) { - throw new RuntimeException(sprintf('无法建立目录:%s', $path)); + throw new \RuntimeException(sprintf('无法建立目录:%s', $path)); } } diff --git a/src/ZM/Utils/HttpUtil.php b/src/ZM/Utils/HttpUtil.php index df651c88..92e3a072 100644 --- a/src/ZM/Utils/HttpUtil.php +++ b/src/ZM/Utils/HttpUtil.php @@ -31,11 +31,8 @@ class HttpUtil * 返回值为状态 * 第二个参数为路由节点 * 第三个参数为动态路由节点中匹配到的参数列表 - * - * @param mixed $node - * @param mixed $params */ - public static function parseUri(ServerRequest $request, &$node, &$params): int + public static function parseUri(ServerRequest $request, mixed &$node, mixed &$params): int { // 建立上下文,设置当前请求的方法 $context = new RequestContext(); diff --git a/src/ZM/Utils/ReflectionUtil.php b/src/ZM/Utils/ReflectionUtil.php index b3632e3f..ca11c373 100644 --- a/src/ZM/Utils/ReflectionUtil.php +++ b/src/ZM/Utils/ReflectionUtil.php @@ -4,28 +4,23 @@ declare(strict_types=1); namespace ZM\Utils; -use Closure; -use ReflectionException; use ReflectionFunction; -use ReflectionFunctionAbstract; use ReflectionMethod; -use ReflectionNamedType; -use ReflectionParameter; class ReflectionUtil { /** * 获取参数的类名(如有) * - * @param ReflectionParameter $parameter 参数 - * @return null|string 类名,如果参数不是类,返回 null + * @param \ReflectionParameter $parameter 参数 + * @return null|string 类名,如果参数不是类,返回 null */ - public static function getParameterClassName(ReflectionParameter $parameter): ?string + public static function getParameterClassName(\ReflectionParameter $parameter): ?string { // 获取参数类型 $type = $parameter->getType(); // 没有声明类型或为基本类型 - if (!$type instanceof ReflectionNamedType || $type->isBuiltin()) { + if (!$type instanceof \ReflectionNamedType || $type->isBuiltin()) { return null; } @@ -85,13 +80,13 @@ class ReflectionUtil /** * 判断传入的回调是否为任意类的非静态方法 * - * @param callable|string $callback 回调 - * @throws ReflectionException + * @param callable|string $callback 回调 + * @throws \ReflectionException */ public static function isNonStaticMethod($callback): bool { if (is_array($callback) && is_string($callback[0])) { - $reflection = new ReflectionMethod($callback[0], $callback[1]); + $reflection = new \ReflectionMethod($callback[0], $callback[1]); return !$reflection->isStatic(); } return false; @@ -105,20 +100,20 @@ class ReflectionUtil * * 可传入实现了 __invoke 的类 * - * @param callable|string $callback 回调 - * @throws ReflectionException + * @param callable|string $callback 回调 + * @throws \ReflectionException */ - public static function getCallReflector($callback): ReflectionFunctionAbstract + public static function getCallReflector($callback): \ReflectionFunctionAbstract { if (is_string($callback) && str_contains($callback, '::')) { $callback = explode('::', $callback); - } elseif (is_object($callback) && !$callback instanceof Closure) { + } elseif (is_object($callback) && !$callback instanceof \Closure) { $callback = [$callback, '__invoke']; } return is_array($callback) - ? new ReflectionMethod($callback[0], $callback[1]) - : new ReflectionFunction($callback); + ? new \ReflectionMethod($callback[0], $callback[1]) + : new \ReflectionFunction($callback); } /** @@ -126,11 +121,11 @@ class ReflectionUtil * * 请不要滥用此方法!!! * - * @param string $class 类名 - * @param string $method 方法名 - * @throws ReflectionException + * @param string $class 类名 + * @param string $method 方法名 + * @throws \ReflectionException */ - public static function getMethod(string $class, string $method): ReflectionMethod + public static function getMethod(string $class, string $method): \ReflectionMethod { $class = new \ReflectionClass($class); $method = $class->getMethod($method); diff --git a/tests/ZM/Utils/ReflectionUtilTest.php b/tests/ZM/Utils/ReflectionUtilTest.php index 9f4f567f..a686f19c 100644 --- a/tests/ZM/Utils/ReflectionUtilTest.php +++ b/tests/ZM/Utils/ReflectionUtilTest.php @@ -4,13 +4,7 @@ declare(strict_types=1); namespace Tests\ZM\Utils; -use Closure; use PHPUnit\Framework\TestCase; -use ReflectionClass; -use ReflectionFunction; -use ReflectionFunctionAbstract; -use ReflectionMethod; -use stdClass; use ZM\Utils\ReflectionUtil; /** @@ -30,7 +24,7 @@ class ReflectionUtilTest extends TestCase public function testGetParameterClassName(): void { - $class = new ReflectionClass(ReflectionUtilTestClass::class); + $class = new \ReflectionClass(ReflectionUtilTestClass::class); $method = $class->getMethod('method'); [$string_parameter, $object_parameter] = $method->getParameters(); @@ -52,10 +46,10 @@ class ReflectionUtilTest extends TestCase return [ 'callable' => [[new ReflectionUtilTestClass(), 'method'], ReflectionUtilTestClass::class . '@method'], 'static callable' => [[ReflectionUtilTestClass::class, 'staticMethod'], ReflectionUtilTestClass::class . '::staticMethod'], - 'closure' => [Closure::fromCallable([$this, 'testVariableToString']), 'closure'], + 'closure' => [\Closure::fromCallable([$this, 'testVariableToString']), 'closure'], 'string' => ['string', 'string'], 'array' => [['123', '42', 'hello', 122], 'array["123","42","hello",122]'], - 'object' => [new stdClass(), 'stdClass'], + 'object' => [new \stdClass(), 'stdClass'], 'resource' => [fopen('php://memory', 'rb'), 'resource(stream)'], 'null' => [null, 'null'], 'boolean 1' => [true, 'true'], @@ -69,7 +63,7 @@ class ReflectionUtilTest extends TestCase * @dataProvider provideTestGetCallReflector * @param mixed $callback */ - public function testGetCallReflector($callback, ReflectionFunctionAbstract $expected): void + public function testGetCallReflector($callback, \ReflectionFunctionAbstract $expected): void { $this->assertEquals($expected, ReflectionUtil::getCallReflector($callback)); } @@ -80,11 +74,11 @@ class ReflectionUtilTest extends TestCase }; return [ - 'callable' => [[new ReflectionUtilTestClass(), 'method'], new ReflectionMethod(ReflectionUtilTestClass::class, 'method')], - 'static callable' => [[ReflectionUtilTestClass::class, 'staticMethod'], new ReflectionMethod(ReflectionUtilTestClass::class, 'staticMethod')], - 'class::method' => [ReflectionUtilTestClass::class . '::staticMethod', new ReflectionMethod(ReflectionUtilTestClass::class, 'staticMethod')], - 'invokable class' => [new InvokableClass(), new ReflectionMethod(InvokableClass::class, '__invoke')], - 'closure' => [$closure, new ReflectionFunction($closure)], + 'callable' => [[new ReflectionUtilTestClass(), 'method'], new \ReflectionMethod(ReflectionUtilTestClass::class, 'method')], + 'static callable' => [[ReflectionUtilTestClass::class, 'staticMethod'], new \ReflectionMethod(ReflectionUtilTestClass::class, 'staticMethod')], + 'class::method' => [ReflectionUtilTestClass::class . '::staticMethod', new \ReflectionMethod(ReflectionUtilTestClass::class, 'staticMethod')], + 'invokable class' => [new InvokableClass(), new \ReflectionMethod(InvokableClass::class, '__invoke')], + 'closure' => [$closure, new \ReflectionFunction($closure)], ]; } } diff --git a/tests/ZMResultPrinter.php b/tests/ZMResultPrinter.php index fa1d6ce7..9b873ac5 100644 --- a/tests/ZMResultPrinter.php +++ b/tests/ZMResultPrinter.php @@ -9,7 +9,6 @@ use PHPUnit\Runner\BaseTestRunner; use PHPUnit\Runner\PhptTestCase; use PHPUnit\Util\Color; use PHPUnit\Util\TestDox\CliTestDoxPrinter; -use Throwable; class ZMResultPrinter extends CliTestDoxPrinter { @@ -164,7 +163,7 @@ class ZMResultPrinter extends CliTestDoxPrinter } } - protected function formatTestResultMessage(Throwable $t, array $result, ?string $prefix = null): string + protected function formatTestResultMessage(\Throwable $t, array $result, ?string $prefix = null): string { $message = $this->formatThrowable($t, $result['status']); $diff = '';