diff --git a/composer.json b/composer.json index f113980d..dc8330f8 100644 --- a/composer.json +++ b/composer.json @@ -21,6 +21,7 @@ "jelix/version": "^2.0", "koriym/attributes": "^1.0", "onebot/libonebot": "dev-develop", + "php-di/php-di": "^7", "psr/container": "^2.0", "psy/psysh": "^0.11.8", "symfony/console": "^6.0", diff --git a/config/container.php b/config/container.php new file mode 100644 index 00000000..6bc44c2f --- /dev/null +++ b/config/container.php @@ -0,0 +1,26 @@ + [ + 'worker_id' => fn() => ProcessManager::getProcessId(), + Driver::class => fn() => Framework::getInstance()->getDriver(), + LoggerInterface::class => fn() => logger(), + ], +]; diff --git a/src/Globals/global_functions.php b/src/Globals/global_functions.php index 214c1694..01b65dc5 100644 --- a/src/Globals/global_functions.php +++ b/src/Globals/global_functions.php @@ -9,8 +9,7 @@ use OneBot\V12\Object\MessageSegment; use OneBot\V12\Object\OneBotEvent; use Psr\Log\LoggerInterface; use ZM\Config\ZMConfig; -use ZM\Container\Container; -use ZM\Container\ContainerInterface; +use ZM\Container\ContainerHolder; use ZM\Logger\ConsoleLogger; use ZM\Middleware\MiddlewareHandler; use ZM\Store\Database\DBException; @@ -126,41 +125,26 @@ function middleware(): MiddlewareHandler // ////////////////// 容器部分 ////////////////////// /** - * 获取容器(请求级)实例 + * 获取容器实例 */ -function container(): ContainerInterface +function container(): DI\Container { - return Container::getInstance(); + return ContainerHolder::getEventContainer(); } /** * 解析类实例(使用容器) * + * 这是 {@see container()}->make($abstract, $parameters) 的别名 + * * @template T * @param class-string $abstract * @return Closure|mixed|T - * @noinspection PhpDocMissingThrowsInspection */ function resolve(string $abstract, array $parameters = []) { /* @noinspection PhpUnhandledExceptionInspection */ - return Container::getInstance()->make($abstract, $parameters); -} - -/** - * 获取容器实例 - * - * @template T - * @param null|class-string $abstract - * @return Closure|ContainerInterface|mixed|T - */ -function app(string $abstract = null, array $parameters = []) -{ - if (is_null($abstract)) { - return container(); - } - - return resolve($abstract, $parameters); + return container()->make($abstract, $parameters); } /** diff --git a/src/Module/Example/Hello123.php b/src/Module/Example/Hello123.php index 605ff397..651b0f7c 100644 --- a/src/Module/Example/Hello123.php +++ b/src/Module/Example/Hello123.php @@ -4,8 +4,11 @@ declare(strict_types=1); namespace Module\Example; +use OneBot\Driver\Event\WebSocket\WebSocketMessageEvent; +use OneBot\V12\Object\OneBotEvent; use ZM\Annotation\Http\Route; use ZM\Annotation\Middleware\Middleware; +use ZM\Annotation\OneBot\BotEvent; use ZM\Middleware\TimerMiddleware; class Hello123 @@ -16,4 +19,10 @@ class Hello123 { return 'Hello Zhamao!This is the first 3.0 page!'; } + + #[BotEvent()] + public function onOBEvent(OneBotEvent $event, WebSocketMessageEvent $messageEvent): void + { + logger()->info("收到了 {$event->getType()}.{$event->getDetailType()} 事件"); + } } diff --git a/src/ZM/Container/AliasBasedAutowiring.php b/src/ZM/Container/AliasBasedAutowiring.php new file mode 100644 index 00000000..095a459a --- /dev/null +++ b/src/ZM/Container/AliasBasedAutowiring.php @@ -0,0 +1,34 @@ +source = $source; + } + + public function getDefinition(string $name): ?Definition + { + // 如果是别名,使用类名获取定义 + if (ClassAliasHelper::isAlias($name)) { + $class = ClassAliasHelper::getAlias($name); + } else { + $class = $name; + } + return $this->source->getDefinition($class); + } + + public function getDefinitions(): array + { + return $this->source->getDefinitions(); + } +} diff --git a/src/ZM/Container/BoundMethod.php b/src/ZM/Container/BoundMethod.php deleted file mode 100644 index c2d7b48b..00000000 --- a/src/ZM/Container/BoundMethod.php +++ /dev/null @@ -1,99 +0,0 @@ -make($callback[0]); - } - - if (!is_callable($callback)) { - throw new \InvalidArgumentException('Callback is not callable.'); - } - - return call_user_func_array($callback, self::getMethodDependencies($container, $callback, $parameters)); - } - - /** - * Get all dependencies for a given method. - * - * @throws \ReflectionException - */ - protected static function getMethodDependencies(ContainerInterface $container, callable|string $callback, array $parameters = []): array - { - $dependencies = []; - - foreach (ReflectionUtil::getCallReflector($callback)->getParameters() as $i => $parameter) { - if (isset($parameters[$i]) && $parameter->hasType() && ($type = $parameter->getType())) { - if ($type instanceof \ReflectionNamedType && gettype($parameters[$i]) === $type->getName()) { - $dependencies[] = $parameters[$i]; - continue; - } - } - static::addDependencyForCallParameter($container, $parameter, $parameters, $dependencies); - } - - return array_merge($dependencies, array_values($parameters)); - } - - /** - * Get the dependency for the given call parameter. - * - * @throws EntryResolutionException - */ - protected static function addDependencyForCallParameter( - ContainerInterface $container, - \ReflectionParameter $parameter, - array &$parameters, - array &$dependencies - ): void { - if (array_key_exists($param_name = $parameter->getName(), $parameters)) { - $dependencies[] = $parameters[$param_name]; - - unset($parameters[$param_name]); - } elseif (!is_null($class_name = ReflectionUtil::getParameterClassName($parameter))) { - if (array_key_exists($class_name, $parameters)) { - $dependencies[] = $parameters[$class_name]; - - unset($parameters[$class_name]); - } elseif ($parameter->isVariadic()) { - $variadic_dependencies = $container->make($class_name); - - $dependencies = array_merge($dependencies, is_array($variadic_dependencies) - ? $variadic_dependencies - : [$variadic_dependencies]); - } else { - $dependencies[] = $container->make($class_name); - } - } elseif ($parameter->isDefaultValueAvailable()) { - $dependencies[] = $parameter->getDefaultValue(); - } elseif (!array_key_exists($param_name, $parameters) && !$parameter->isOptional()) { - $message = "无法解析类 {$parameter->getDeclaringClass()->getName()} 的依赖 {$parameter}"; - - throw new EntryResolutionException($message); - } - } -} diff --git a/src/ZM/Container/ClassAliasHelper.php b/src/ZM/Container/ClassAliasHelper.php index 38911af5..4232e023 100644 --- a/src/ZM/Container/ClassAliasHelper.php +++ b/src/ZM/Container/ClassAliasHelper.php @@ -10,7 +10,7 @@ namespace ZM\Container; class ClassAliasHelper { /** - * @var array{class:string,alias:string}[] + * @var array */ private static array $aliases = []; @@ -23,7 +23,7 @@ class ClassAliasHelper public static function addAlias(string $class, string $alias): void { class_alias($class, $alias); - self::$aliases[$alias] = ['class' => $class, 'alias' => $alias]; + self::$aliases[$alias] = $class; } /** @@ -39,10 +39,10 @@ class ClassAliasHelper /** * 获取别名定义信息 * - * @param string $alias 别名 - * @return null|array{class:string,alias:string} 如果没有定义则返回 null + * @param string $alias 别名 + * @return null|class-string 如果没有定义则返回 null */ - public static function getAlias(string $alias): ?array + public static function getAlias(string $alias): ?string { return self::$aliases[$alias] ?? null; } @@ -50,17 +50,12 @@ class ClassAliasHelper /** * 根据类名获取别名 * - * @param string $class 类名 - * @return null|array{class:string,alias:string} 如果没有定义则返回 null + * @param string $class 类名 + * @return null|class-string 如果没有定义则返回 null */ - public static function getAliasByClass(string $class): ?array + public static function getAliasByClass(string $class): ?string { - foreach (self::$aliases as $alias) { - if ($alias['class'] === $class) { - return $alias; - } - } - return null; + return array_search($class, self::$aliases, true) ?: null; } /** @@ -71,7 +66,7 @@ class ClassAliasHelper */ public static function getClass(string $alias): string { - return self::$aliases[$alias]['class'] ?? $alias; + return self::$aliases[$alias] ?? $alias; } /** diff --git a/src/ZM/Container/Container.php b/src/ZM/Container/Container.php deleted file mode 100644 index 7d22faff..00000000 --- a/src/ZM/Container/Container.php +++ /dev/null @@ -1,61 +0,0 @@ -bound($id) || $this->getParent()->has($id); - } - - /** - * 获取一个绑定的实例 - * - * @template T - * @param class-string $abstract 类或接口名 - * @param array $parameters 参数 - * @return Closure|mixed|T 实例 - * @throws EntryResolutionException - */ - public function make(string $abstract, array $parameters = []) - { - if (isset($this->shared[$abstract])) { - return $this->shared[$abstract]; - } - - // 此类没有,父类有,则从父类中获取 - if (!$this->bound($abstract) && $this->getParent()->bound($abstract)) { - $this->log("{$abstract} is not bound, but in parent container, using parent container"); - return $this->getParent()->make($abstract, $parameters); - } - - return $this->traitMake($abstract, $parameters); - } -} diff --git a/src/ZM/Container/ContainerBindingListener.php b/src/ZM/Container/ContainerBindingListener.php new file mode 100644 index 00000000..ac4728da --- /dev/null +++ b/src/ZM/Container/ContainerBindingListener.php @@ -0,0 +1,39 @@ +addEventListener($event, [self::class, 'callback'], PHP_INT_MAX); + ob_event_provider()->addEventListener($event, [self::class, 'cleanCallback'], PHP_INT_MIN); + } + } + + public static function callback(): void + { + } + + public static function cleanCallback(): void + { + ContainerHolder::clearEventContainer(); + } +} diff --git a/src/ZM/Container/ContainerHolder.php b/src/ZM/Container/ContainerHolder.php new file mode 100644 index 00000000..c60a26e7 --- /dev/null +++ b/src/ZM/Container/ContainerHolder.php @@ -0,0 +1,40 @@ +addDefinitions(new AliasBasedAutowiring($source)); + return $builder->build(); + } +} diff --git a/src/ZM/Container/ContainerInterface.php b/src/ZM/Container/ContainerInterface.php deleted file mode 100644 index e0df8a6a..00000000 --- a/src/ZM/Container/ContainerInterface.php +++ /dev/null @@ -1,109 +0,0 @@ - $abstract 类或接口名 - * @param array $parameters 参数 - * @return \Closure|mixed|T 实例 - */ - public function make(string $abstract, array $parameters = []); - - /** - * 调用对应的方法,并自动注入依赖 - * - * @param callable $callback 对应的方法 - * @param array $parameters 参数 - * @param null|string $default_method 默认方法 - * @return mixed - */ - public function call(callable $callback, array $parameters = [], string $default_method = null); -} diff --git a/src/ZM/Container/ContainerRegistrant.php b/src/ZM/Container/ContainerRegistrant.php new file mode 100644 index 00000000..d3a2cd80 --- /dev/null +++ b/src/ZM/Container/ContainerRegistrant.php @@ -0,0 +1,97 @@ + $event, + 'bot.event' => DI\get(OneBotEvent::class), + BotContext::class => fn () => bot(), + ]); + } + + /** + * 应在收到 OneBot 动作响应时调用 + */ + public static function registerOBActionResponseServices(ActionResponse $response): void + { + self::addServices([ + ActionResponse::class => $response, + 'bot.action.response' => DI\get(ActionResponse::class), + ]); + } + + /** + * 应在收到 HTTP 请求时调用 + */ + public static function registerHttpRequestServices(HttpRequestEvent $event): void + { + self::addServices([ + HttpRequestEvent::class => $event, + 'http.request.event' => DI\get(HttpRequestEvent::class), + ServerRequestInterface::class => fn () => $event->getRequest(), + 'http.request' => DI\get(ServerRequestInterface::class), + ContextInterface::class => Context::class, + ]); + } + + /** + * 应在收到 WebSocket 连接时调用 + */ + public static function registerWSOpenServices(WebSocketOpenEvent $event): void + { + self::addServices([ + WebSocketOpenEvent::class => $event, + 'ws.open.event' => DI\get(WebSocketOpenEvent::class), + ]); + } + + /** + * 应在收到 WebSocket 消息时调用 + */ + public static function registerWSMessageServices(WebSocketMessageEvent $event): void + { + self::addServices([ + WebSocketMessageEvent::class => $event, + 'ws.message.event' => DI\get(WebSocketMessageEvent::class), + ]); + } + + /** + * 应在收到 WebSocket 关闭时调用 + */ + public static function registerWSCloseServices(WebSocketCloseEvent $event): void + { + self::addServices([ + WebSocketCloseEvent::class => $event, + 'ws.close.event' => DI\get(WebSocketCloseEvent::class), + ]); + } + + private static function addServices(array $services): void + { + foreach ($services as $name => $service) { + ContainerHolder::getEventContainer()->set($name, $service); + } + } +} diff --git a/src/ZM/Container/ContainerServicesProvider.php b/src/ZM/Container/ContainerServicesProvider.php deleted file mode 100644 index 94d65d65..00000000 --- a/src/ZM/Container/ContainerServicesProvider.php +++ /dev/null @@ -1,116 +0,0 @@ -registerGlobalServices(WorkerContainer::getInstance()); - break; - case 'request': - $this->registerRequestServices(Container::getInstance(), ...$params); - break; - case 'message': - $this->registerConnectionServices(Container::getInstance()); - $this->registerMessageServices(Container::getInstance()); - break; - case 'connection': - $this->registerConnectionServices(Container::getInstance()); - break; - default: - break; - } - } - - /** - * 清理服务 - */ - public function cleanup(): void - { - container()->flush(); - } - - /** - * 注册全局服务 - */ - private function registerGlobalServices(ContainerInterface $container): void - { - // 注册路径类的容器快捷方式 - $container->instance('path.working', WORKING_DIR); - $container->instance('path.source', SOURCE_ROOT_DIR); - $container->alias('path.source', 'path.base'); - $container->instance('path.data', config('global.data_dir')); - $container->instance('path.framework', FRAMEWORK_ROOT_DIR); - - // 注册worker和驱动 - $container->instance('worker_id', ProcessManager::getProcessId()); - $container->instance(Driver::class, Framework::getInstance()->getDriver()); - - // 注册logger - $container->instance(LoggerInterface::class, logger()); - } - - /** - * 注册请求服务(HTTP请求) - */ - private function registerRequestServices(ContainerInterface $container, HttpRequestEvent $event): void - { - // $context = Context::$context[zm_cid()]; - $container->instance(HttpRequestEvent::class, $event); - $container->alias(HttpRequestEvent::class, 'http.request.event'); - $container->instance(ServerRequestInterface::class, $event->getRequest()); - $container->alias(ServerRequestInterface::class, 'http.request'); - // $container->instance(Request::class, $context['request']); - // $container->instance(Response::class, $context['response']); - $container->bind(ContextInterface::class, Context::class); - // $container->alias(ContextInterface::class, Context::class); - } - - /** - * 注册消息服务(WS消息) - */ - private function registerMessageServices(ContainerInterface $container): void - { - // $context = Context::$context[zm_cid()]; - // $container->instance(Frame::class, $context['frame']); // WS 消息帧 - // $container->bind(ContextInterface::class, Closure::fromCallable('ctx')); - // $container->alias(ContextInterface::class, Context::class); - } - - /** - * 注册链接服务 - */ - private function registerConnectionServices(ContainerInterface $container): void - { - // $context = Context::$context[zm_cid()]; - // $container->instance(ConnectionObject::class, $context['connection']); - } -} diff --git a/src/ZM/Container/ContainerTrait.php b/src/ZM/Container/ContainerTrait.php deleted file mode 100644 index a49c7729..00000000 --- a/src/ZM/Container/ContainerTrait.php +++ /dev/null @@ -1,728 +0,0 @@ -shouldLog()) { - $this->log('Container created'); - } - } - - /** - * 判断对应的类或接口是否已经注册 - * - * @param string $abstract 类或接口名 - */ - public function bound(string $abstract): bool - { - return array_key_exists($abstract, self::$bindings) - || array_key_exists($abstract, self::$instances) - || array_key_exists($abstract, $this->shared) - || $this->isAlias($abstract); - } - - /** - * 获取类别名(如存在) - * - * @param string $abstract 类或接口名 - * @return string 别名,不存在时返回传入的类或接口名 - */ - public function getAlias(string $abstract): string - { - if (!isset(self::$aliases[$abstract])) { - if (ClassAliasHelper::isAlias($abstract)) { - return $this->getAlias(ClassAliasHelper::getAlias($abstract)['class']); - } - return $abstract; - } - - return $this->getAlias(self::$aliases[$abstract]); - } - - /** - * 注册一个类别名 - * - * @param string $abstract 类或接口名 - * @param string $alias 别名 - */ - public function alias(string $abstract, string $alias): void - { - if ($alias === $abstract) { - throw new \InvalidArgumentException("[{$abstract}] is same as [{$alias}]"); - } - - self::$aliases[$alias] = $abstract; - - if ($this->shouldLog()) { - $this->log("[{$abstract}] is aliased as [{$alias}]"); - } - } - - /** - * 注册绑定 - * - * @param string $abstract 类或接口名 - * @param null|\Closure|string $concrete 返回类实例的闭包,或是类名 - * @param bool $shared 是否共享 - */ - public function bind(string $abstract, $concrete = null, bool $shared = false): void - { - $this->dropStaleInstances($abstract); - - // 如果没有提供闭包,则默认为自动解析类名 - if (is_null($concrete)) { - $concrete = $abstract; - } - - $concrete_name = ''; - if ($this->shouldLog()) { - $concrete_name = ReflectionUtil::variableToString($concrete); - } - - // 如果不是闭包,则认为是类名,此时将其包装在一个闭包中,以方便后续处理 - if (!$concrete instanceof \Closure) { - $concrete = $this->getClosure($abstract, $concrete); - } - - self::$bindings[$abstract] = compact('concrete', 'shared'); - - if ($this->shouldLog()) { - $this->log("[{$abstract}] is bound to [{$concrete_name}]" . ($shared ? ' (shared)' : '')); - } - } - - /** - * 注册绑定 - * - * 在已经绑定时不会重复注册 - * - * @param string $abstract 类或接口名 - * @param null|\Closure|string $concrete 返回类实例的闭包,或是类名 - * @param bool $shared 是否共享 - */ - public function bindIf(string $abstract, $concrete = null, bool $shared = false): void - { - if (!$this->bound($abstract)) { - $this->bind($abstract, $concrete, $shared); - } - } - - /** - * 注册一个单例绑定 - * - * @param string $abstract 类或接口名 - * @param null|\Closure|string $concrete 返回类实例的闭包,或是类名 - */ - public function singleton(string $abstract, $concrete = null): void - { - $this->bind($abstract, $concrete, true); - } - - /** - * 注册一个单例绑定 - * - * 在已经绑定时不会重复注册 - * - * @param string $abstract 类或接口名 - * @param null|\Closure|string $concrete 返回类实例的闭包,或是类名 - */ - public function singletonIf(string $abstract, $concrete = null): void - { - if (!$this->bound($abstract)) { - $this->singleton($abstract, $concrete); - } - } - - /** - * 注册一个已有的实例,效果等同于单例绑定 - * - * @param string $abstract 类或接口名 - * @param mixed $instance 实例 - * @return mixed - */ - public function instance(string $abstract, mixed $instance) - { - if (isset(self::$instances[$abstract])) { - return self::$instances[$abstract]; - } - - self::$instances[$abstract] = $instance; - - if ($this->shouldLog()) { - $class_name = ReflectionUtil::variableToString($instance); - $this->log("[{$abstract}] is bound to [{$class_name}] (instance)"); - } - - return $instance; - } - - /** - * 获取一个解析对应类实例的闭包 - * - * @param string $abstract 类或接口名 - */ - public function factory(string $abstract): \Closure - { - return fn () => $this->make($abstract); - } - - /** - * 清除所有绑定和实例 - */ - public function flush(): void - { - self::$aliases = []; - self::$bindings = []; - self::$instances = []; - - $this->shared = []; - $this->build_stack = []; - $this->with = []; - - if ($this->shouldLog()) { - $this->log('Container flushed'); - } - } - - /** - * 获取一个绑定的实例 - * - * @template T - * @param class-string $abstract 类或接口名 - * @param array $parameters 参数 - * @return \Closure|mixed|T 实例 - * @throws EntryResolutionException - */ - public function make(string $abstract, array $parameters = []) - { - $abstract = $this->getAlias($abstract); - - $needs_contextual_build = !empty($parameters); - - if (isset($this->shared[$abstract])) { - if ($this->shouldLog()) { - $this->log(sprintf( - '[%s] resolved (shared)%s', - $abstract, - $needs_contextual_build ? ' with ' . implode(', ', $parameters) : '' - )); - } - return $this->shared[$abstract]; - } - - // 如果已经存在在实例池中(通常意味着单例绑定),则直接返回该实例 - if (isset(self::$instances[$abstract]) && !$needs_contextual_build) { - if ($this->shouldLog()) { - $this->log("[{$abstract}] resolved (instance)"); - } - return self::$instances[$abstract]; - } - - $this->with[] = $parameters; - - $concrete = $this->getConcrete($abstract); - - // 构造该类的实例,并递归解析所有依赖 - if ($this->isBuildable($concrete, $abstract)) { - $object = $this->build($concrete); - } else { - $object = $this->make($concrete); - } - - // 如果该类存在扩展器(装饰器),则逐个应用到实例 - foreach ($this->getExtenders($abstract) as $extender) { - $object = $extender($object, $this); - } - - // 如果该类被注册为单例,则需要将其存放在实例池中,方便后续取用同一实例 - if (!$needs_contextual_build && $this->isShared($abstract)) { - $this->shared[$abstract] = $object; - if ($this->shouldLog()) { - $this->log("[{$abstract}] added to shared pool"); - } - } - - // 弹出本次构造的覆盖参数 - array_pop($this->with); - - if ($this->shouldLog()) { - $this->log(sprintf( - '[%s] resolved%s', - $abstract, - $needs_contextual_build ? ' with ' . implode(', ', $parameters) : '' - )); - } - - return $object; - } - - /** - * 实例化具体的类实例 - * - * @param \Closure|string $concrete 类名或对应的闭包 - * @return mixed - * @throws EntryResolutionException - */ - public function build(\Closure|string $concrete) - { - // 如果传入的是闭包,则直接执行并返回 - if ($concrete instanceof \Closure) { - return $concrete($this, $this->getLastParameterOverride()); - } - - try { - $reflection = new \ReflectionClass($concrete); - } catch (\ReflectionException $e) { - throw new EntryResolutionException("指定的类 {$concrete} 不存在", 0, $e); - } - - if (!$reflection->isInstantiable()) { - $this->notInstantiable($concrete); - } - - $this->build_stack[] = $concrete; - - $constructor = $reflection->getConstructor(); - - // 如果不存在构造函数,则代表不需要进一步解析,直接实例化即可 - if (is_null($constructor)) { - array_pop($this->build_stack); - return new $concrete(); - } - - $dependencies = $constructor->getParameters(); - - // 获取所有依赖的实例 - try { - $instances = $this->resolveDependencies($dependencies); - } catch (EntryResolutionException $e) { - array_pop($this->build_stack); - throw $e; - } - - array_pop($this->build_stack); - - return $reflection->newInstanceArgs($instances); - } - - /** - * 调用对应的方法,并自动注入依赖 - * - * @param callable|string $callback 对应的方法 - * @param array $parameters 参数 - * @param null|string $default_method 默认方法 - * @return mixed - */ - public function call(callable|string $callback, array $parameters = [], string $default_method = null) - { - if ($this->shouldLog()) { - if (count($parameters)) { - $str_parameters = array_map([ReflectionUtil::class, 'variableToString'], $parameters); - $str_parameters = implode(', ', $str_parameters); - } else { - $str_parameters = ''; - } - $this->log(sprintf( - 'Called %s%s(%s)', - ReflectionUtil::variableToString($callback), - $default_method ? '@' . $default_method : '', - $str_parameters - )); - } - return BoundMethod::call($this, $callback, $parameters, $default_method); - } - - /** - * Finds an entry of the container by its identifier and returns it. - * - * @param string $id identifier of the entry to look for - * - * @return mixed entry - * @throws NotFoundExceptionInterface no entry was found for **this** identifier - * @throws ContainerExceptionInterface error while retrieving the entry - */ - public function get(string $id) - { - try { - return $this->make($id); - } catch (\Exception $e) { - if ($this->has($id)) { - throw new EntryResolutionException('', 0, $e); - } - - throw new EntryNotFoundException('', 0, $e); - } - } - - /** - * Returns true if the container can return an entry for the given identifier. - * Returns false otherwise. - * - * `has($id)` returning true does not mean that `get($id)` will not throw an exception. - * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`. - * - * @param string $id identifier of the entry to look for - */ - public function has(string $id): bool - { - return $this->bound($id); - } - - /** - * 扩展一个类或接口 - * - * @param string $abstract 类或接口名 - * @param \Closure $closure 扩展闭包 - */ - public function extend(string $abstract, \Closure $closure): void - { - $abstract = $this->getAlias($abstract); - - // 如果该类已经被解析过,则直接将扩展器应用到该类的实例上 - // 否则,将扩展器存入扩展器池,等待解析 - if (isset(self::$instances[$abstract])) { - self::$instances[$abstract] = $closure(self::$instances[$abstract], $this); - } else { - self::$extenders[$abstract][] = $closure; - } - - if ($this->shouldLog()) { - $this->log("[{$abstract}] extended"); - } - } - - /** - * 获取日志前缀 - */ - public function getLogPrefix(): string - { - $id = spl_object_id($this); - // 级联容器的日志前缀为父容器的日志前缀加上当前容器的id - if (method_exists($this, 'getParent')) { - $parent = $this->getParent(); - $id = spl_object_id($parent) . '-' . $id; - } - return "[Container {$id}] "; - } - - /** - * 设置日志前缀 - */ - public function setLogPrefix(string $prefix): void - { - $this->log_prefix = $prefix; - } - - /** - * 获取对应类型的所有扩展器 - * - * @param string $abstract 类或接口名 - * @return \Closure[] - */ - protected function getExtenders(string $abstract): array - { - $abstract = $this->getAlias($abstract); - - return self::$extenders[$abstract] ?? []; - } - - /** - * 判断传入的是否为别名 - */ - protected function isAlias(string $name): bool - { - return array_key_exists($name, self::$aliases); - } - - /** - * 抛弃所有过时的实例和别名 - * - * @param string $abstract 类或接口名 - */ - protected function dropStaleInstances(string $abstract): void - { - unset( - self::$instances[$abstract], - self::$aliases[$abstract], - $this->shared[$abstract] - ); - } - - /** - * 获取一个解析对应类的闭包 - * - * @param string $abstract 类或接口名 - * @param string $concrete 实际类名 - */ - protected function getClosure(string $abstract, string $concrete): \Closure - { - return static function ($container, $parameters = []) use ($abstract, $concrete) { - $method = $abstract === $concrete ? 'build' : 'make'; - - return $container->{$method}($concrete, $parameters); - }; - } - - /** - * 获取最后一次的覆盖参数 - */ - protected function getLastParameterOverride(): array - { - return $this->with[count($this->with) - 1] ?? []; - } - - /** - * 抛出实例化异常 - * - * @throws EntryResolutionException - */ - protected function notInstantiable(string $concrete, string $reason = ''): void - { - if (!empty($this->build_stack)) { - $previous = implode(', ', $this->build_stack); - $message = "类 {$concrete} 无法实例化,其被 {$previous} 依赖"; - } else { - $message = "类 {$concrete} 无法实例化"; - } - - throw new EntryResolutionException("{$message}:{$reason}"); - } - - /** - * 解析依赖 - * - * @param \ReflectionParameter[] $dependencies - * @throws EntryResolutionException - */ - protected function resolveDependencies(array $dependencies): array - { - $results = []; - - foreach ($dependencies as $dependency) { - // 如果此依赖存在覆盖参数,则使用覆盖参数 - // 否则,将尝试解析参数 - if ($this->hasParameterOverride($dependency)) { - $results[] = $this->getParameterOverride($dependency); - continue; - } - - // 如果存在临时注入的依赖,则使用临时注入的依赖 - if ($this->hasParameterTypeOverride($dependency)) { - $results[] = $this->getParameterTypeOverride($dependency); - continue; - } - - // 如果类名为空,则代表此依赖是基本类型,且无法对其进行依赖解析 - $class_name = ReflectionUtil::getParameterClassName($dependency); - $results[] = is_null($class_name) - ? $this->resolvePrimitive($dependency) - : $this->resolveClass($dependency); - - if ($this->shouldLog()) { - if (is_null($class_name)) { - if ($dependency->hasType()) { - $class_name = $dependency->getType(); - } else { - $class_name = 'Primitive'; - } - } - $this->log("Dependency [{$class_name} {$dependency->name}] resolved"); - } - } - - return $results; - } - - /** - * 判断传入的参数是否存在覆盖参数 - */ - protected function hasParameterOverride(\ReflectionParameter $parameter): bool - { - return array_key_exists($parameter->name, $this->getLastParameterOverride()); - } - - /** - * 获取覆盖参数 - * - * @return mixed - */ - protected function getParameterOverride(\ReflectionParameter $parameter) - { - return $this->getLastParameterOverride()[$parameter->name]; - } - - /** - * 判断传入的参数是否存在临时注入的参数 - */ - protected function hasParameterTypeOverride(\ReflectionParameter $parameter): bool - { - if (!$parameter->hasType()) { - return false; - } - - $type = $parameter->getType(); - - if (!$type instanceof \ReflectionNamedType || $type->isBuiltin()) { - return false; - } - - return array_key_exists($type->getName(), $this->getLastParameterOverride()); - } - - /** - * 获取临时注入的参数 - * - * @return mixed - */ - protected function getParameterTypeOverride(\ReflectionParameter $parameter) - { - $type = $parameter->getType(); - - if (!$type instanceof \ReflectionNamedType) { - return []; - } - - return $this->getLastParameterOverride()[$type->getName()]; - } - - /** - * 解析基本类型 - * - * @return mixed 对应类型的默认值 - * @throws EntryResolutionException 如参数不存在默认值,则抛出异常 - */ - protected function resolvePrimitive(\ReflectionParameter $parameter) - { - if ($parameter->isDefaultValueAvailable()) { - return $parameter->getDefaultValue(); - } - - throw new EntryResolutionException("无法解析类 {$parameter->getDeclaringClass()->getName()} 的参数 {$parameter}"); - } - - /** - * 解析类 - * - * @return mixed - * @throws EntryResolutionException 如果无法解析类,则抛出异常 - */ - protected function resolveClass(\ReflectionParameter $parameter) - { - try { - // 尝试解析 - return $this->make(ReflectionUtil::getParameterClassName($parameter)); - } catch (EntryResolutionException $e) { - // 如果参数是可选的,则返回默认值 - if ($parameter->isDefaultValueAvailable()) { - array_pop($this->with); - return $parameter->getDefaultValue(); - } - - if ($parameter->isVariadic()) { - array_pop($this->with); - return []; - } - - throw $e; - } - } - - /** - * 获取类名的实际类型 - * - * @param string $abstract 类或接口名 - */ - protected function getConcrete(string $abstract): \Closure|string - { - if (isset(self::$bindings[$abstract])) { - return self::$bindings[$abstract]['concrete']; - } - - return $abstract; - } - - /** - * 判断传入的实际类型是否可以构造 - * - * @param mixed $concrete 实际类型 - * @param string $abstract 类或接口名 - */ - protected function isBuildable(mixed $concrete, string $abstract): bool - { - return $concrete === $abstract || $concrete instanceof \Closure; - } - - /** - * 判断传入的类型是否为共享实例 - * - * @param string $abstract 类或接口名 - */ - protected function isShared(string $abstract): bool - { - return isset($this->instances[$abstract]) - || (isset($this->bindings[$abstract]['shared']) - && $this->bindings[$abstract]['shared'] === true); - } - - /** - * 判断是否输出日志 - */ - protected function shouldLog(): bool - { - return true; - } - - /** - * 记录日志(自动附加容器日志前缀) - */ - protected function log(string $message): void - { - logger()->debug($this->getLogPrefix() . $message); - } -} diff --git a/src/ZM/Container/EntryNotFoundException.php b/src/ZM/Container/EntryNotFoundException.php deleted file mode 100644 index 175b88e5..00000000 --- a/src/ZM/Container/EntryNotFoundException.php +++ /dev/null @@ -1,11 +0,0 @@ -registerServices('request', $event); + ContainerRegistrant::registerHttpRequestServices($event); // TODO: 这里有个bug,如果是用的Workerman+Fiber协程的话,有个前置协程挂起,这里获取到的Event是被挂起的Event对象,触发两次事件才能归正 // 跑一遍 BindEvent 绑定了 HttpRequestEvent 的注解 $handler = new AnnotationHandler(BindEvent::class); @@ -79,6 +79,5 @@ class HttpEventListener $response = HttpUtil::handleStaticPage($event->getRequest()->getUri()->getPath()); $event->withResponse($response); } - container()->flush(); } } diff --git a/src/ZM/Event/Listener/WSEventListener.php b/src/ZM/Event/Listener/WSEventListener.php index 901ef5f7..839510c8 100644 --- a/src/ZM/Event/Listener/WSEventListener.php +++ b/src/ZM/Event/Listener/WSEventListener.php @@ -11,7 +11,7 @@ use OneBot\Driver\Event\WebSocket\WebSocketOpenEvent; use OneBot\Util\Singleton; use ZM\Annotation\AnnotationHandler; use ZM\Annotation\Framework\BindEvent; -use ZM\Container\ContainerServicesProvider; +use ZM\Container\ContainerRegistrant; use ZM\Exception\Handler; use ZM\Utils\ConnectionUtil; @@ -30,22 +30,17 @@ class WSEventListener return; } // 注册容器 - resolve(ContainerServicesProvider::class)->registerServices('connection'); - container()->instance(WebSocketOpenEvent::class, $event); - container()->alias(WebSocketOpenEvent::class, 'ws.open.event'); + ContainerRegistrant::registerWSOpenServices($event); // 调用注解 $handler = new AnnotationHandler(BindEvent::class); $handler->setRuleCallback(fn ($x) => is_a($x->event_class, WebSocketOpenEvent::class, true)); $handler->handleAll($event); - - resolve(ContainerServicesProvider::class)->cleanup(); } public function onWebSocketMessage(WebSocketMessageEvent $event): void { - container()->instance(WebSocketMessageEvent::class, $event); - container()->alias(WebSocketMessageEvent::class, 'ws.message.event'); + ContainerRegistrant::registerWSMessageServices($event); // 调用注解 try { $handler = new AnnotationHandler(BindEvent::class); @@ -54,8 +49,6 @@ class WSEventListener } catch (\Throwable $e) { logger()->error("处理 WebSocket 消息时出现异常:{$e->getMessage()}"); Handler::getInstance()->handle($e); - } finally { - resolve(ContainerServicesProvider::class)->cleanup(); } } @@ -66,14 +59,12 @@ class WSEventListener { logger()->info('关闭连接: ' . $event->getFd()); // 绑定容器 - container()->instance(WebSocketCloseEvent::class, $event); - container()->alias(WebSocketCloseEvent::class, 'ws.close.event'); + ContainerRegistrant::registerWSCloseServices($event); // 调用注解 $handler = new AnnotationHandler(BindEvent::class); $handler->setRuleCallback(fn ($x) => is_a($x->event_class, WebSocketCloseEvent::class, true)); $handler->handleAll($event); ConnectionUtil::removeConnection($event->getFd()); - resolve(ContainerServicesProvider::class)->cleanup(); } } diff --git a/src/ZM/Event/Listener/WorkerEventListener.php b/src/ZM/Event/Listener/WorkerEventListener.php index 20db20b6..0b7893ad 100644 --- a/src/ZM/Event/Listener/WorkerEventListener.php +++ b/src/ZM/Event/Listener/WorkerEventListener.php @@ -10,7 +10,6 @@ use ZM\Annotation\AnnotationHandler; use ZM\Annotation\AnnotationMap; use ZM\Annotation\AnnotationParser; use ZM\Annotation\Framework\Init; -use ZM\Container\ContainerServicesProvider; use ZM\Exception\ZMKnownException; use ZM\Framework; use ZM\Plugin\OneBot12Adapter; @@ -69,9 +68,6 @@ class WorkerEventListener logger()->info('WORKER#' . $i . ":\t" . ProcessStateManager::getProcessState(ZM_PROCESS_WORKER, $i)); } - // 设置容器,注册容器提供商 - resolve(ContainerServicesProvider::class)->registerServices('global'); - // 注册 Worker 进程遇到退出时的回调,安全退出 register_shutdown_function(function () { $error = error_get_last(); diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index f87cf971..8cc9cc16 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -21,6 +21,7 @@ use OneBot\Driver\Workerman\WorkermanDriver; use OneBot\Util\Singleton; use ZM\Command\Server\ServerStartCommand; use ZM\Config\ZMConfig; +use ZM\Container\ContainerBindingListener; use ZM\Event\Listener\HttpEventListener; use ZM\Event\Listener\ManagerEventListener; use ZM\Event\Listener\MasterEventListener; @@ -93,7 +94,7 @@ class Framework { // 顺序执行引导器 foreach ($this->bootstrappers as $bootstrapper) { - app($bootstrapper)->bootstrap($this->argv); + resolve($bootstrapper)->bootstrap($this->argv); } // 初始化 @OnSetup 事件 @@ -222,6 +223,8 @@ class Framework $this->printMotd(); } + ContainerBindingListener::listenForEvents(); + // 添加框架需要监听的顶层事件监听器 // worker 事件 ob_event_provider()->addEventListener(WorkerStartEvent::getName(), [WorkerEventListener::getInstance(), 'onWorkerStart999'], 999); diff --git a/src/ZM/Middleware/MiddlewareHandler.php b/src/ZM/Middleware/MiddlewareHandler.php index 4456d85a..1fa7b8cc 100644 --- a/src/ZM/Middleware/MiddlewareHandler.php +++ b/src/ZM/Middleware/MiddlewareHandler.php @@ -94,7 +94,9 @@ class MiddlewareHandler } } if ($return !== false) { - $result = container()->call($callback, $args); + // 也许我们不需要显式传入参数,而是绑定到容器后自动解析,这样可以避免参数不匹配的问题 + $result = container()->call($callback); +// $result = container()->call($callback, $args); } while (isset($this->stack[$stack_id]) && ($item = array_pop($this->stack[$stack_id])) !== null) { // 如果是 pipeline 形式的中间件,则使用闭包回去 diff --git a/src/ZM/Plugin/OneBot12Adapter.php b/src/ZM/Plugin/OneBot12Adapter.php index 58c4713a..30657007 100644 --- a/src/ZM/Plugin/OneBot12Adapter.php +++ b/src/ZM/Plugin/OneBot12Adapter.php @@ -19,7 +19,7 @@ use ZM\Annotation\OneBot\BotActionResponse; use ZM\Annotation\OneBot\BotCommand; use ZM\Annotation\OneBot\BotEvent; use ZM\Annotation\OneBot\CommandArgument; -use ZM\Container\ContainerServicesProvider; +use ZM\Container\ContainerRegistrant; use ZM\Context\BotContext; use ZM\Utils\ConnectionUtil; @@ -33,7 +33,7 @@ class OneBot12Adapter extends ZMPlugin case 'onebot12': // 处理所有 OneBot 12 的反向 WS 握手事件 $this->addEvent(WebSocketOpenEvent::class, [$this, 'handleWSReverseOpen']); - $this->addEvent(\WebSocketMessageEvent::class, [$this, 'handleWSReverseMessage']); + $this->addEvent(WebSocketMessageEvent::class, [$this, 'handleWSReverseMessage']); // 在 BotEvent 内处理 BotCommand // $cmd_event = BotEvent::make(type: 'message', level: 15)->on([$this, 'handleBotCommand']); // $this->addBotEvent($cmd_event); @@ -165,9 +165,6 @@ class OneBot12Adapter extends ZMPlugin return; } - // 处理 - resolve(ContainerServicesProvider::class)->registerServices('message'); - // 解析 Frame 到 UTF-8 JSON $body = $event->getFrame()->getData(); $body = json_decode($body, true); @@ -186,9 +183,7 @@ class OneBot12Adapter extends ZMPlugin } // 绑定容器 - container()->instance(OneBotEvent::class, $obj); - container()->alias(OneBotEvent::class, 'bot.event'); - container()->bind(BotContext::class, function () { return bot(); }); + ContainerRegistrant::registerOBEventServices($obj); // 调用 BotEvent 事件 $handler = new AnnotationHandler(BotEvent::class); @@ -206,8 +201,7 @@ class OneBot12Adapter extends ZMPlugin $resp->message = $body['message'] ?? ''; $resp->data = $body['data'] ?? null; - container()->instance(ActionResponse::class, $resp); - container()->alias(ActionResponse::class, 'bot.action.response'); + ContainerRegistrant::registerOBActionResponseServices($resp); // 调用 BotActionResponse 事件 $handler = new AnnotationHandler(BotActionResponse::class);