change container to php-di

This commit is contained in:
sunxyw 2022-12-25 17:42:32 +08:00
parent 60ce722010
commit 05b3446f59
No known key found for this signature in database
GPG Key ID: F391C42B19AFFC98
23 changed files with 280 additions and 1218 deletions

View File

@ -21,6 +21,7 @@
"jelix/version": "^2.0", "jelix/version": "^2.0",
"koriym/attributes": "^1.0", "koriym/attributes": "^1.0",
"onebot/libonebot": "dev-develop", "onebot/libonebot": "dev-develop",
"php-di/php-di": "^7",
"psr/container": "^2.0", "psr/container": "^2.0",
"psy/psysh": "^0.11.8", "psy/psysh": "^0.11.8",
"symfony/console": "^6.0", "symfony/console": "^6.0",

26
config/container.php Normal file
View File

@ -0,0 +1,26 @@
<?php
use OneBot\Driver\Driver;
use OneBot\Driver\Process\ProcessManager;
use Psr\Log\LoggerInterface;
use ZM\Framework;
/**
* 这里是容器的配置文件,你可以在这里配置容器的绑定和其他一些参数。
* 选用的容器是 PHP-DI你可以在这里查看文档https://php-di.org/doc/
* 我们建议你在使用容器前先阅读以下章节:
* - 基本使用方式https://php-di.org/doc/container.html
* - 注册绑定方式https://php-di.org/doc/php-definitions.html#definition-types
* - 最佳使用实践https://php-di.org/doc/best-practices.html
* 同时,你也可以选择查阅我们(炸毛框架)的相关文档,其会包含一些简单的使用方法和示例。
* 关于框架在不同事件(或周期中)默认注册的绑定,请参阅我们的文档,或直接查看 {@see \ZM\Container\ContainerRegistrant}
*/
return [
// 这里定义的是全局容器的绑定,不建议在此处直接调用框架、应用内部的类或方法,因为这些类可能还没有被加载或初始化
// 你可以使用匿名函数来延迟加载
'definitions' => [
'worker_id' => fn() => ProcessManager::getProcessId(),
Driver::class => fn() => Framework::getInstance()->getDriver(),
LoggerInterface::class => fn() => logger(),
],
];

View File

@ -9,8 +9,7 @@ use OneBot\V12\Object\MessageSegment;
use OneBot\V12\Object\OneBotEvent; use OneBot\V12\Object\OneBotEvent;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use ZM\Config\ZMConfig; use ZM\Config\ZMConfig;
use ZM\Container\Container; use ZM\Container\ContainerHolder;
use ZM\Container\ContainerInterface;
use ZM\Logger\ConsoleLogger; use ZM\Logger\ConsoleLogger;
use ZM\Middleware\MiddlewareHandler; use ZM\Middleware\MiddlewareHandler;
use ZM\Store\Database\DBException; 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 * @template T
* @param class-string<T> $abstract * @param class-string<T> $abstract
* @return Closure|mixed|T * @return Closure|mixed|T
* @noinspection PhpDocMissingThrowsInspection
*/ */
function resolve(string $abstract, array $parameters = []) function resolve(string $abstract, array $parameters = [])
{ {
/* @noinspection PhpUnhandledExceptionInspection */ /* @noinspection PhpUnhandledExceptionInspection */
return Container::getInstance()->make($abstract, $parameters); return container()->make($abstract, $parameters);
}
/**
* 获取容器实例
*
* @template T
* @param null|class-string<T> $abstract
* @return Closure|ContainerInterface|mixed|T
*/
function app(string $abstract = null, array $parameters = [])
{
if (is_null($abstract)) {
return container();
}
return resolve($abstract, $parameters);
} }
/** /**

View File

@ -4,8 +4,11 @@ declare(strict_types=1);
namespace Module\Example; namespace Module\Example;
use OneBot\Driver\Event\WebSocket\WebSocketMessageEvent;
use OneBot\V12\Object\OneBotEvent;
use ZM\Annotation\Http\Route; use ZM\Annotation\Http\Route;
use ZM\Annotation\Middleware\Middleware; use ZM\Annotation\Middleware\Middleware;
use ZM\Annotation\OneBot\BotEvent;
use ZM\Middleware\TimerMiddleware; use ZM\Middleware\TimerMiddleware;
class Hello123 class Hello123
@ -16,4 +19,10 @@ class Hello123
{ {
return 'Hello ZhamaoThis is the first 3.0 page'; return 'Hello ZhamaoThis is the first 3.0 page';
} }
#[BotEvent()]
public function onOBEvent(OneBotEvent $event, WebSocketMessageEvent $messageEvent): void
{
logger()->info("收到了 {$event->getType()}.{$event->getDetailType()} 事件");
}
} }

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace ZM\Container;
use DI\Definition\Definition;
use DI\Definition\Source\DefinitionSource;
class AliasBasedAutowiring implements DefinitionSource
{
private DefinitionSource $source;
public function __construct(DefinitionSource $source)
{
$this->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();
}
}

View File

@ -1,99 +0,0 @@
<?php
declare(strict_types=1);
namespace ZM\Container;
use ZM\Utils\ReflectionUtil;
class BoundMethod
{
/**
* 调用指定闭包、类方法并注入依赖
*
* @param Container $container
* @return mixed
* @throws EntryResolutionException|\ReflectionException
* @throws \InvalidArgumentException
*/
public static function call(ContainerInterface $container, callable|string $callback, array $parameters = [], string $default_method = null)
{
if (is_string($callback) && !$default_method && method_exists($callback, '__invoke')) {
$default_method = '__invoke';
}
if (is_string($callback) && $default_method) {
$callback = [$callback, $default_method];
}
if (ReflectionUtil::isNonStaticMethod($callback)) {
$callback[0] = $container->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);
}
}
}

View File

@ -10,7 +10,7 @@ namespace ZM\Container;
class ClassAliasHelper class ClassAliasHelper
{ {
/** /**
* @var array{class:string,alias:string}[] * @var array<string, class-string>
*/ */
private static array $aliases = []; private static array $aliases = [];
@ -23,7 +23,7 @@ class ClassAliasHelper
public static function addAlias(string $class, string $alias): void public static function addAlias(string $class, string $alias): void
{ {
class_alias($class, $alias); class_alias($class, $alias);
self::$aliases[$alias] = ['class' => $class, 'alias' => $alias]; self::$aliases[$alias] = $class;
} }
/** /**
@ -40,9 +40,9 @@ class ClassAliasHelper
* 获取别名定义信息 * 获取别名定义信息
* *
* @param string $alias 别名 * @param string $alias 别名
* @return null|array{class:string,alias:string} 如果没有定义则返回 null * @return null|class-string 如果没有定义则返回 null
*/ */
public static function getAlias(string $alias): ?array public static function getAlias(string $alias): ?string
{ {
return self::$aliases[$alias] ?? null; return self::$aliases[$alias] ?? null;
} }
@ -51,16 +51,11 @@ class ClassAliasHelper
* 根据类名获取别名 * 根据类名获取别名
* *
* @param string $class 类名 * @param string $class 类名
* @return null|array{class:string,alias:string} 如果没有定义则返回 null * @return null|class-string 如果没有定义则返回 null
*/ */
public static function getAliasByClass(string $class): ?array public static function getAliasByClass(string $class): ?string
{ {
foreach (self::$aliases as $alias) { return array_search($class, self::$aliases, true) ?: null;
if ($alias['class'] === $class) {
return $alias;
}
}
return null;
} }
/** /**
@ -71,7 +66,7 @@ class ClassAliasHelper
*/ */
public static function getClass(string $alias): string public static function getClass(string $alias): string
{ {
return self::$aliases[$alias]['class'] ?? $alias; return self::$aliases[$alias] ?? $alias;
} }
/** /**

View File

@ -1,61 +0,0 @@
<?php
declare(strict_types=1);
namespace ZM\Container;
use OneBot\Util\Singleton;
class Container implements ContainerInterface
{
use Singleton;
use ContainerTrait {
ContainerTrait::make as protected traitMake;
}
/**
* 获取父容器
*/
public function getParent(): ContainerInterface
{
return WorkerContainer::getInstance();
}
/**
* 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) || $this->getParent()->has($id);
}
/**
* 获取一个绑定的实例
*
* @template T
* @param class-string<T> $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);
}
}

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace ZM\Container;
use OneBot\Driver\Event\Http\HttpRequestEvent;
use OneBot\Driver\Event\WebSocket\WebSocketCloseEvent;
use OneBot\Driver\Event\WebSocket\WebSocketMessageEvent;
use OneBot\Driver\Event\WebSocket\WebSocketOpenEvent;
class ContainerBindingListener
{
private static array $events = [
WebSocketOpenEvent::class,
WebSocketMessageEvent::class,
WebSocketCloseEvent::class,
HttpRequestEvent::class,
];
public static function listenForEvents(): void
{
// 监听感兴趣的事件,方便做容器初始化和销毁
foreach (self::$events as $event) {
ob_event_provider()->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();
}
}

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace ZM\Container;
use DI;
use DI\Container;
use DI\ContainerBuilder;
class ContainerHolder
{
private static ?Container $container = null;
public static function getEventContainer(): Container
{
if (self::$container === null) {
self::$container = self::buildContainer();
}
return self::$container;
}
public static function clearEventContainer(): void
{
self::$container = null;
}
private static function buildContainer(): Container
{
$builder = new ContainerBuilder();
$sources = [
new DI\Definition\Source\ReflectionBasedAutowiring(),
new DI\Definition\Source\AttributeBasedAutowiring(),
new DI\Definition\Source\DefinitionArray(config('container.definitions', [])),
];
$source = new DI\Definition\Source\SourceChain($sources);
$builder->addDefinitions(new AliasBasedAutowiring($source));
return $builder->build();
}
}

View File

@ -1,109 +0,0 @@
<?php
declare(strict_types=1);
namespace ZM\Container;
use Psr\Container\ContainerInterface as PsrContainerInterface;
/**
* Interface ContainerInterface
*
* Illuminate WorkerContainer 简化而来,兼容 PSR-11
*/
interface ContainerInterface extends PsrContainerInterface
{
/**
* 判断对应的类或接口是否已经注册
*
* @param string $abstract 类或接口名
*/
public function bound(string $abstract): bool;
/**
* 注册一个类别名
*
* @param string $abstract 类或接口名
* @param string $alias 别名
*/
public function alias(string $abstract, string $alias): void;
/**
* 注册绑定
*
* @param string $abstract 类或接口名
* @param null|\Closure|string $concrete 返回类实例的闭包,或是类名
* @param bool $shared 是否共享
*/
public function bind(string $abstract, $concrete = null, bool $shared = false): void;
/**
* 注册绑定
*
* 在已经绑定时不会重复注册
*
* @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 返回类实例的闭包,或是类名
*/
public function singleton(string $abstract, $concrete = null): void;
/**
* 注册一个单例绑定
*
* 在已经绑定时不会重复注册
*
* @param string $abstract 类或接口名
* @param null|\Closure|string $concrete 返回类实例的闭包,或是类名
*/
public function singletonIf(string $abstract, $concrete = null): void;
/**
* 注册一个已有的实例,效果等同于单例绑定
*
* @param string $abstract 类或接口名
* @param mixed $instance 实例
* @return mixed
*/
public function instance(string $abstract, $instance);
/**
* 获取一个解析对应类实例的闭包
*
* @param string $abstract 类或接口名
*/
public function factory(string $abstract): \Closure;
/**
* 清除所有绑定和实例
*/
public function flush(): void;
/**
* 获取一个绑定的实例
*
* @template T
* @param class-string<T> $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);
}

View File

@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
namespace ZM\Container;
use DI;
use OneBot\Driver\Event\Http\HttpRequestEvent;
use OneBot\Driver\Event\WebSocket\WebSocketCloseEvent;
use OneBot\Driver\Event\WebSocket\WebSocketMessageEvent;
use OneBot\Driver\Event\WebSocket\WebSocketOpenEvent;
use OneBot\V12\Object\ActionResponse;
use OneBot\V12\Object\OneBotEvent;
use Psr\Http\Message\ServerRequestInterface;
use ZM\Context\BotContext;
use ZM\Context\Context;
use ZM\Context\ContextInterface;
class ContainerRegistrant
{
/**
* 应在收到 OneBot 事件时调用
*/
public static function registerOBEventServices(OneBotEvent $event): void
{
self::addServices([
OneBotEvent::class => $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);
}
}
}

View File

@ -1,116 +0,0 @@
<?php
declare(strict_types=1);
namespace ZM\Container;
use Closure;
use OneBot\Driver\Driver;
use OneBot\Driver\Event\Http\HttpRequestEvent;
use OneBot\Driver\Process\ProcessManager;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
use ZM\Context\Context;
use ZM\Context\ContextInterface;
use ZM\Framework;
class ContainerServicesProvider
{
/**
* 注册服务
*
* ```
* 作用域:
* global: worker start
* request: request
* message: message
* connection: open, close, message
* ```
*
* @param string $scope 作用域
*/
public function registerServices(string $scope, ...$params): void
{
switch ($scope) {
case 'global':
$this->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']);
}
}

View File

@ -1,728 +0,0 @@
<?php
declare(strict_types=1);
namespace ZM\Container;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use ZM\Utils\ReflectionUtil;
trait ContainerTrait
{
protected array $shared = [];
/**
* @var array[]
*/
protected array $build_stack = [];
/**
* @var array[]
*/
protected array $with = [];
/**
* 日志前缀
*/
protected string $log_prefix;
/**
* @var array[]
*/
private static array $bindings = [];
/**
* @var object[]
*/
private static array $instances = [];
/**
* @var string[]
*/
private static array $aliases = [];
/**
* @var \Closure[][]
*/
private static array $extenders = [];
public function __construct()
{
if ($this->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<T> $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);
}
}

View File

@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace ZM\Container;
use Psr\Container\NotFoundExceptionInterface;
class EntryNotFoundException extends \Exception implements NotFoundExceptionInterface
{
}

View File

@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace ZM\Container;
use Psr\Container\ContainerExceptionInterface;
class EntryResolutionException extends \Exception implements ContainerExceptionInterface
{
}

View File

@ -1,13 +0,0 @@
<?php
declare(strict_types=1);
namespace ZM\Container;
use OneBot\Util\Singleton;
class WorkerContainer implements ContainerInterface
{
use Singleton;
use ContainerTrait;
}

View File

@ -11,7 +11,7 @@ use OneBot\Util\Singleton;
use ZM\Annotation\AnnotationHandler; use ZM\Annotation\AnnotationHandler;
use ZM\Annotation\Framework\BindEvent; use ZM\Annotation\Framework\BindEvent;
use ZM\Annotation\Http\Route; use ZM\Annotation\Http\Route;
use ZM\Container\ContainerServicesProvider; use ZM\Container\ContainerRegistrant;
use ZM\Exception\ConfigException; use ZM\Exception\ConfigException;
use ZM\Utils\HttpUtil; use ZM\Utils\HttpUtil;
@ -29,7 +29,7 @@ class HttpEventListener
public function onRequest999(HttpRequestEvent $event) public function onRequest999(HttpRequestEvent $event)
{ {
// 注册容器 // 注册容器
resolve(ContainerServicesProvider::class)->registerServices('request', $event); ContainerRegistrant::registerHttpRequestServices($event);
// TODO: 这里有个bug如果是用的Workerman+Fiber协程的话有个前置协程挂起这里获取到的Event是被挂起的Event对象触发两次事件才能归正 // TODO: 这里有个bug如果是用的Workerman+Fiber协程的话有个前置协程挂起这里获取到的Event是被挂起的Event对象触发两次事件才能归正
// 跑一遍 BindEvent 绑定了 HttpRequestEvent 的注解 // 跑一遍 BindEvent 绑定了 HttpRequestEvent 的注解
$handler = new AnnotationHandler(BindEvent::class); $handler = new AnnotationHandler(BindEvent::class);
@ -79,6 +79,5 @@ class HttpEventListener
$response = HttpUtil::handleStaticPage($event->getRequest()->getUri()->getPath()); $response = HttpUtil::handleStaticPage($event->getRequest()->getUri()->getPath());
$event->withResponse($response); $event->withResponse($response);
} }
container()->flush();
} }
} }

View File

@ -11,7 +11,7 @@ use OneBot\Driver\Event\WebSocket\WebSocketOpenEvent;
use OneBot\Util\Singleton; use OneBot\Util\Singleton;
use ZM\Annotation\AnnotationHandler; use ZM\Annotation\AnnotationHandler;
use ZM\Annotation\Framework\BindEvent; use ZM\Annotation\Framework\BindEvent;
use ZM\Container\ContainerServicesProvider; use ZM\Container\ContainerRegistrant;
use ZM\Exception\Handler; use ZM\Exception\Handler;
use ZM\Utils\ConnectionUtil; use ZM\Utils\ConnectionUtil;
@ -30,22 +30,17 @@ class WSEventListener
return; return;
} }
// 注册容器 // 注册容器
resolve(ContainerServicesProvider::class)->registerServices('connection'); ContainerRegistrant::registerWSOpenServices($event);
container()->instance(WebSocketOpenEvent::class, $event);
container()->alias(WebSocketOpenEvent::class, 'ws.open.event');
// 调用注解 // 调用注解
$handler = new AnnotationHandler(BindEvent::class); $handler = new AnnotationHandler(BindEvent::class);
$handler->setRuleCallback(fn ($x) => is_a($x->event_class, WebSocketOpenEvent::class, true)); $handler->setRuleCallback(fn ($x) => is_a($x->event_class, WebSocketOpenEvent::class, true));
$handler->handleAll($event); $handler->handleAll($event);
resolve(ContainerServicesProvider::class)->cleanup();
} }
public function onWebSocketMessage(WebSocketMessageEvent $event): void public function onWebSocketMessage(WebSocketMessageEvent $event): void
{ {
container()->instance(WebSocketMessageEvent::class, $event); ContainerRegistrant::registerWSMessageServices($event);
container()->alias(WebSocketMessageEvent::class, 'ws.message.event');
// 调用注解 // 调用注解
try { try {
$handler = new AnnotationHandler(BindEvent::class); $handler = new AnnotationHandler(BindEvent::class);
@ -54,8 +49,6 @@ class WSEventListener
} catch (\Throwable $e) { } catch (\Throwable $e) {
logger()->error("处理 WebSocket 消息时出现异常:{$e->getMessage()}"); logger()->error("处理 WebSocket 消息时出现异常:{$e->getMessage()}");
Handler::getInstance()->handle($e); Handler::getInstance()->handle($e);
} finally {
resolve(ContainerServicesProvider::class)->cleanup();
} }
} }
@ -66,14 +59,12 @@ class WSEventListener
{ {
logger()->info('关闭连接: ' . $event->getFd()); logger()->info('关闭连接: ' . $event->getFd());
// 绑定容器 // 绑定容器
container()->instance(WebSocketCloseEvent::class, $event); ContainerRegistrant::registerWSCloseServices($event);
container()->alias(WebSocketCloseEvent::class, 'ws.close.event');
// 调用注解 // 调用注解
$handler = new AnnotationHandler(BindEvent::class); $handler = new AnnotationHandler(BindEvent::class);
$handler->setRuleCallback(fn ($x) => is_a($x->event_class, WebSocketCloseEvent::class, true)); $handler->setRuleCallback(fn ($x) => is_a($x->event_class, WebSocketCloseEvent::class, true));
$handler->handleAll($event); $handler->handleAll($event);
ConnectionUtil::removeConnection($event->getFd()); ConnectionUtil::removeConnection($event->getFd());
resolve(ContainerServicesProvider::class)->cleanup();
} }
} }

View File

@ -10,7 +10,6 @@ use ZM\Annotation\AnnotationHandler;
use ZM\Annotation\AnnotationMap; use ZM\Annotation\AnnotationMap;
use ZM\Annotation\AnnotationParser; use ZM\Annotation\AnnotationParser;
use ZM\Annotation\Framework\Init; use ZM\Annotation\Framework\Init;
use ZM\Container\ContainerServicesProvider;
use ZM\Exception\ZMKnownException; use ZM\Exception\ZMKnownException;
use ZM\Framework; use ZM\Framework;
use ZM\Plugin\OneBot12Adapter; use ZM\Plugin\OneBot12Adapter;
@ -69,9 +68,6 @@ class WorkerEventListener
logger()->info('WORKER#' . $i . ":\t" . ProcessStateManager::getProcessState(ZM_PROCESS_WORKER, $i)); logger()->info('WORKER#' . $i . ":\t" . ProcessStateManager::getProcessState(ZM_PROCESS_WORKER, $i));
} }
// 设置容器,注册容器提供商
resolve(ContainerServicesProvider::class)->registerServices('global');
// 注册 Worker 进程遇到退出时的回调,安全退出 // 注册 Worker 进程遇到退出时的回调,安全退出
register_shutdown_function(function () { register_shutdown_function(function () {
$error = error_get_last(); $error = error_get_last();

View File

@ -21,6 +21,7 @@ use OneBot\Driver\Workerman\WorkermanDriver;
use OneBot\Util\Singleton; use OneBot\Util\Singleton;
use ZM\Command\Server\ServerStartCommand; use ZM\Command\Server\ServerStartCommand;
use ZM\Config\ZMConfig; use ZM\Config\ZMConfig;
use ZM\Container\ContainerBindingListener;
use ZM\Event\Listener\HttpEventListener; use ZM\Event\Listener\HttpEventListener;
use ZM\Event\Listener\ManagerEventListener; use ZM\Event\Listener\ManagerEventListener;
use ZM\Event\Listener\MasterEventListener; use ZM\Event\Listener\MasterEventListener;
@ -93,7 +94,7 @@ class Framework
{ {
// 顺序执行引导器 // 顺序执行引导器
foreach ($this->bootstrappers as $bootstrapper) { foreach ($this->bootstrappers as $bootstrapper) {
app($bootstrapper)->bootstrap($this->argv); resolve($bootstrapper)->bootstrap($this->argv);
} }
// 初始化 @OnSetup 事件 // 初始化 @OnSetup 事件
@ -222,6 +223,8 @@ class Framework
$this->printMotd(); $this->printMotd();
} }
ContainerBindingListener::listenForEvents();
// 添加框架需要监听的顶层事件监听器 // 添加框架需要监听的顶层事件监听器
// worker 事件 // worker 事件
ob_event_provider()->addEventListener(WorkerStartEvent::getName(), [WorkerEventListener::getInstance(), 'onWorkerStart999'], 999); ob_event_provider()->addEventListener(WorkerStartEvent::getName(), [WorkerEventListener::getInstance(), 'onWorkerStart999'], 999);

View File

@ -94,7 +94,9 @@ class MiddlewareHandler
} }
} }
if ($return !== false) { 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) { while (isset($this->stack[$stack_id]) && ($item = array_pop($this->stack[$stack_id])) !== null) {
// 如果是 pipeline 形式的中间件,则使用闭包回去 // 如果是 pipeline 形式的中间件,则使用闭包回去

View File

@ -19,7 +19,7 @@ use ZM\Annotation\OneBot\BotActionResponse;
use ZM\Annotation\OneBot\BotCommand; use ZM\Annotation\OneBot\BotCommand;
use ZM\Annotation\OneBot\BotEvent; use ZM\Annotation\OneBot\BotEvent;
use ZM\Annotation\OneBot\CommandArgument; use ZM\Annotation\OneBot\CommandArgument;
use ZM\Container\ContainerServicesProvider; use ZM\Container\ContainerRegistrant;
use ZM\Context\BotContext; use ZM\Context\BotContext;
use ZM\Utils\ConnectionUtil; use ZM\Utils\ConnectionUtil;
@ -33,7 +33,7 @@ class OneBot12Adapter extends ZMPlugin
case 'onebot12': case 'onebot12':
// 处理所有 OneBot 12 的反向 WS 握手事件 // 处理所有 OneBot 12 的反向 WS 握手事件
$this->addEvent(WebSocketOpenEvent::class, [$this, 'handleWSReverseOpen']); $this->addEvent(WebSocketOpenEvent::class, [$this, 'handleWSReverseOpen']);
$this->addEvent(\WebSocketMessageEvent::class, [$this, 'handleWSReverseMessage']); $this->addEvent(WebSocketMessageEvent::class, [$this, 'handleWSReverseMessage']);
// 在 BotEvent 内处理 BotCommand // 在 BotEvent 内处理 BotCommand
// $cmd_event = BotEvent::make(type: 'message', level: 15)->on([$this, 'handleBotCommand']); // $cmd_event = BotEvent::make(type: 'message', level: 15)->on([$this, 'handleBotCommand']);
// $this->addBotEvent($cmd_event); // $this->addBotEvent($cmd_event);
@ -165,9 +165,6 @@ class OneBot12Adapter extends ZMPlugin
return; return;
} }
// 处理
resolve(ContainerServicesProvider::class)->registerServices('message');
// 解析 Frame 到 UTF-8 JSON // 解析 Frame 到 UTF-8 JSON
$body = $event->getFrame()->getData(); $body = $event->getFrame()->getData();
$body = json_decode($body, true); $body = json_decode($body, true);
@ -186,9 +183,7 @@ class OneBot12Adapter extends ZMPlugin
} }
// 绑定容器 // 绑定容器
container()->instance(OneBotEvent::class, $obj); ContainerRegistrant::registerOBEventServices($obj);
container()->alias(OneBotEvent::class, 'bot.event');
container()->bind(BotContext::class, function () { return bot(); });
// 调用 BotEvent 事件 // 调用 BotEvent 事件
$handler = new AnnotationHandler(BotEvent::class); $handler = new AnnotationHandler(BotEvent::class);
@ -206,8 +201,7 @@ class OneBot12Adapter extends ZMPlugin
$resp->message = $body['message'] ?? ''; $resp->message = $body['message'] ?? '';
$resp->data = $body['data'] ?? null; $resp->data = $body['data'] ?? null;
container()->instance(ActionResponse::class, $resp); ContainerRegistrant::registerOBActionResponseServices($resp);
container()->alias(ActionResponse::class, 'bot.action.response');
// 调用 BotActionResponse 事件 // 调用 BotActionResponse 事件
$handler = new AnnotationHandler(BotActionResponse::class); $handler = new AnnotationHandler(BotActionResponse::class);