mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-03-17 20:54:52 +08:00
change container to php-di
This commit is contained in:
parent
60ce722010
commit
05b3446f59
@ -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
26
config/container.php
Normal 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(),
|
||||||
|
],
|
||||||
|
];
|
||||||
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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 Zhamao!This is the first 3.0 page!';
|
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()} 事件");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
34
src/ZM/Container/AliasBasedAutowiring.php
Normal file
34
src/ZM/Container/AliasBasedAutowiring.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
39
src/ZM/Container/ContainerBindingListener.php
Normal file
39
src/ZM/Container/ContainerBindingListener.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/ZM/Container/ContainerHolder.php
Normal file
40
src/ZM/Container/ContainerHolder.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
97
src/ZM/Container/ContainerRegistrant.php
Normal file
97
src/ZM/Container/ContainerRegistrant.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Container;
|
|
||||||
|
|
||||||
use Psr\Container\NotFoundExceptionInterface;
|
|
||||||
|
|
||||||
class EntryNotFoundException extends \Exception implements NotFoundExceptionInterface
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Container;
|
|
||||||
|
|
||||||
use Psr\Container\ContainerExceptionInterface;
|
|
||||||
|
|
||||||
class EntryResolutionException extends \Exception implements ContainerExceptionInterface
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace ZM\Container;
|
|
||||||
|
|
||||||
use OneBot\Util\Singleton;
|
|
||||||
|
|
||||||
class WorkerContainer implements ContainerInterface
|
|
||||||
{
|
|
||||||
use Singleton;
|
|
||||||
use ContainerTrait;
|
|
||||||
}
|
|
||||||
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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 形式的中间件,则使用闭包回去
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user