2022-08-01 16:31:54 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
2023-02-11 21:19:58 +08:00
|
|
|
|
use Choir\Http\HttpFactory;
|
2022-08-14 18:24:59 +08:00
|
|
|
|
use OneBot\Driver\Coroutine\Adaptive;
|
|
|
|
|
|
use OneBot\Driver\Coroutine\CoroutineInterface;
|
|
|
|
|
|
use OneBot\Driver\Process\ExecutionResult;
|
2022-08-13 17:00:29 +08:00
|
|
|
|
use OneBot\V12\Object\MessageSegment;
|
2023-02-11 21:19:58 +08:00
|
|
|
|
use Psr\Http\Message\ResponseInterface;
|
2022-08-01 16:31:54 +08:00
|
|
|
|
use Psr\Log\LoggerInterface;
|
2023-01-13 14:19:20 +08:00
|
|
|
|
use Psr\SimpleCache\CacheInterface;
|
2023-01-15 21:30:44 +08:00
|
|
|
|
use ZM\Config\Environment;
|
2022-08-23 17:51:20 +08:00
|
|
|
|
use ZM\Config\ZMConfig;
|
2022-12-25 17:42:32 +08:00
|
|
|
|
use ZM\Container\ContainerHolder;
|
2022-08-01 16:31:54 +08:00
|
|
|
|
use ZM\Logger\ConsoleLogger;
|
2022-08-13 17:00:29 +08:00
|
|
|
|
use ZM\Middleware\MiddlewareHandler;
|
2023-02-10 13:12:20 +08:00
|
|
|
|
use ZM\Schedule\Timer;
|
2022-08-27 19:47:45 +08:00
|
|
|
|
use ZM\Store\Database\DBException;
|
2022-12-30 16:15:22 +08:00
|
|
|
|
use ZM\Store\Database\DBQueryBuilder;
|
2022-08-27 19:47:45 +08:00
|
|
|
|
use ZM\Store\Database\DBWrapper;
|
2022-12-31 15:27:09 +08:00
|
|
|
|
use ZM\Store\KV\KVInterface;
|
2023-01-04 20:43:03 +08:00
|
|
|
|
use ZM\Store\KV\Redis\RedisWrapper;
|
2022-08-13 17:00:29 +08:00
|
|
|
|
|
|
|
|
|
|
// 防止重复引用引发报错
|
|
|
|
|
|
if (function_exists('zm_internal_errcode')) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-08-01 16:31:54 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据具体操作系统替换目录分隔符
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string $dir 目录
|
|
|
|
|
|
*/
|
|
|
|
|
|
function zm_dir(string $dir): string
|
|
|
|
|
|
{
|
2022-12-19 20:22:47 +08:00
|
|
|
|
if (str_starts_with($dir, 'phar://')) {
|
2022-08-01 16:31:54 +08:00
|
|
|
|
return $dir;
|
|
|
|
|
|
}
|
|
|
|
|
|
return str_replace('/', DIRECTORY_SEPARATOR, $dir);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-14 18:24:59 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 执行shell指令
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string $cmd 命令行
|
|
|
|
|
|
*/
|
|
|
|
|
|
function zm_exec(string $cmd): ExecutionResult
|
|
|
|
|
|
{
|
|
|
|
|
|
return Adaptive::exec($cmd);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* sleep 指定时间,单位为秒(最小单位为1毫秒,即0.001)
|
|
|
|
|
|
*/
|
2022-12-19 20:22:47 +08:00
|
|
|
|
function zm_sleep(float|int $time)
|
2022-08-14 18:24:59 +08:00
|
|
|
|
{
|
|
|
|
|
|
Adaptive::sleep($time);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-10 13:12:20 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 创建一个计时器(Timer::tick() 的别名)
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param int $ms 时间(毫秒)
|
|
|
|
|
|
* @param callable $callback 回调函数
|
|
|
|
|
|
* @param int $times 重复次数(如果为 0 或 -1,则永久循环,其他大于 0 的数为限定次数)
|
|
|
|
|
|
*/
|
|
|
|
|
|
function zm_timer_tick(int $ms, callable $callback, int $times = 0): int
|
|
|
|
|
|
{
|
|
|
|
|
|
return Timer::tick($ms, $callback, $times);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建一个延后一次性计时器,只在指定毫秒后执行一次即销毁(Timer::after() 的别名)
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param int $ms 时间(毫秒)
|
|
|
|
|
|
* @param callable $callback 回调函数
|
|
|
|
|
|
*/
|
|
|
|
|
|
function zm_timer_after(int $ms, callable $callback): int
|
|
|
|
|
|
{
|
|
|
|
|
|
return Timer::after($ms, $callback);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-14 18:24:59 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取协程接口
|
|
|
|
|
|
*/
|
|
|
|
|
|
function coroutine(): ?CoroutineInterface
|
|
|
|
|
|
{
|
|
|
|
|
|
return Adaptive::getCoroutine();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-01 16:31:54 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取内部错误码
|
|
|
|
|
|
*/
|
2022-12-19 20:22:47 +08:00
|
|
|
|
function zm_internal_errcode(int|string $code): string
|
2022-08-01 16:31:54 +08:00
|
|
|
|
{
|
|
|
|
|
|
return "[ErrCode:{$code}] ";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-12-20 20:10:40 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 返回当前炸毛实例的 ID
|
|
|
|
|
|
*/
|
2022-08-01 16:31:54 +08:00
|
|
|
|
function zm_instance_id(): string
|
|
|
|
|
|
{
|
|
|
|
|
|
if (defined('ZM_INSTANCE_ID')) {
|
|
|
|
|
|
return ZM_INSTANCE_ID;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!defined('ZM_START_TIME')) {
|
|
|
|
|
|
define('ZM_START_TIME', microtime(true));
|
|
|
|
|
|
}
|
|
|
|
|
|
$instance_id = dechex(crc32(strval(ZM_START_TIME)));
|
|
|
|
|
|
define('ZM_INSTANCE_ID', $instance_id);
|
|
|
|
|
|
return ZM_INSTANCE_ID;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 助手方法,返回一个 Logger 实例
|
|
|
|
|
|
*/
|
|
|
|
|
|
function logger(): LoggerInterface
|
|
|
|
|
|
{
|
|
|
|
|
|
global $ob_logger;
|
|
|
|
|
|
if ($ob_logger === null) {
|
|
|
|
|
|
return new ConsoleLogger();
|
|
|
|
|
|
}
|
|
|
|
|
|
return $ob_logger;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 判断传入的数组是否为关联数组
|
|
|
|
|
|
*/
|
|
|
|
|
|
function is_assoc_array(array $array): bool
|
|
|
|
|
|
{
|
|
|
|
|
|
return !empty($array) && array_keys($array) !== range(0, count($array) - 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-12-26 03:19:53 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 格式匹配
|
|
|
|
|
|
*/
|
|
|
|
|
|
function match_pattern(string $pattern, string $subject): bool
|
|
|
|
|
|
{
|
|
|
|
|
|
$pattern = str_replace(['\*', '\\\\.*'], ['.*', '\*'], preg_quote($pattern, '/'));
|
|
|
|
|
|
$pattern = '/^' . $pattern . '$/i';
|
|
|
|
|
|
return preg_match($pattern, $subject) === 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 匹配参数
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return array|false 成功时返回匹配到的参数数组,失败时返回false
|
|
|
|
|
|
*/
|
|
|
|
|
|
function match_args(string $pattern, string $subject)
|
|
|
|
|
|
{
|
|
|
|
|
|
$result = [];
|
|
|
|
|
|
if (match_pattern($pattern, $subject)) {
|
|
|
|
|
|
if (mb_strpos($pattern, '*') === false) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
$exp = explode('*', $pattern);
|
|
|
|
|
|
$i = 0;
|
|
|
|
|
|
foreach ($exp as $k => $v) {
|
|
|
|
|
|
if (empty($v) && $k === 0) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (empty($v) && $k === count($exp) - 1) {
|
|
|
|
|
|
$subject .= '^EOL';
|
|
|
|
|
|
$v = '^EOL';
|
|
|
|
|
|
}
|
|
|
|
|
|
$cur_var = '';
|
|
|
|
|
|
$ori = $i;
|
|
|
|
|
|
while (($a = mb_substr($subject, $i, mb_strlen($v))) !== $v && !empty($a)) {
|
|
|
|
|
|
$cur_var .= mb_substr($subject, $i, 1);
|
|
|
|
|
|
++$i;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($i !== $ori || $k === 1 || $k === count($exp) - 1) {
|
|
|
|
|
|
$result[] = $cur_var;
|
|
|
|
|
|
}
|
|
|
|
|
|
$i += mb_strlen($v);
|
|
|
|
|
|
}
|
|
|
|
|
|
return $result;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-13 17:00:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 构建消息段的助手函数
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string $type 类型
|
|
|
|
|
|
* @param array $data 字段
|
|
|
|
|
|
*/
|
|
|
|
|
|
function segment(string $type, array $data = []): MessageSegment
|
|
|
|
|
|
{
|
|
|
|
|
|
return new MessageSegment($type, $data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 中间件操作类的助手函数
|
|
|
|
|
|
*/
|
|
|
|
|
|
function middleware(): MiddlewareHandler
|
|
|
|
|
|
{
|
|
|
|
|
|
return MiddlewareHandler::getInstance();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ////////////////// 容器部分 //////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2022-12-25 17:42:32 +08:00
|
|
|
|
* 获取容器实例
|
2022-08-13 17:00:29 +08:00
|
|
|
|
*/
|
2022-12-25 17:42:32 +08:00
|
|
|
|
function container(): DI\Container
|
2022-08-13 17:00:29 +08:00
|
|
|
|
{
|
2022-12-25 17:42:32 +08:00
|
|
|
|
return ContainerHolder::getEventContainer();
|
2022-08-13 17:00:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解析类实例(使用容器)
|
|
|
|
|
|
*
|
2022-12-25 17:42:32 +08:00
|
|
|
|
* 这是 {@see container()}->make($abstract, $parameters) 的别名
|
|
|
|
|
|
*
|
2022-08-23 17:51:20 +08:00
|
|
|
|
* @template T
|
2022-11-08 17:33:25 +08:00
|
|
|
|
* @param class-string<T> $abstract
|
2022-08-13 17:00:29 +08:00
|
|
|
|
* @return Closure|mixed|T
|
|
|
|
|
|
*/
|
|
|
|
|
|
function resolve(string $abstract, array $parameters = [])
|
|
|
|
|
|
{
|
|
|
|
|
|
/* @noinspection PhpUnhandledExceptionInspection */
|
2022-12-25 17:42:32 +08:00
|
|
|
|
return container()->make($abstract, $parameters);
|
2022-08-01 16:31:54 +08:00
|
|
|
|
}
|
2022-08-21 16:08:20 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取 MySQL 调用的类
|
|
|
|
|
|
*
|
2022-08-27 19:47:45 +08:00
|
|
|
|
* @throws DBException
|
2022-08-21 16:08:20 +08:00
|
|
|
|
*/
|
2022-08-27 19:47:45 +08:00
|
|
|
|
function db(string $name = '')
|
2022-08-21 16:08:20 +08:00
|
|
|
|
{
|
2022-08-27 19:47:45 +08:00
|
|
|
|
return new DBWrapper($name);
|
2022-08-21 16:08:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取构建 MySQL 的类
|
|
|
|
|
|
*
|
2022-08-27 19:47:45 +08:00
|
|
|
|
* @throws DBException
|
2022-08-21 16:08:20 +08:00
|
|
|
|
*/
|
2022-12-30 16:15:22 +08:00
|
|
|
|
function sql_builder(string $name = ''): DBQueryBuilder
|
2022-08-21 16:08:20 +08:00
|
|
|
|
{
|
2022-08-27 19:47:45 +08:00
|
|
|
|
return (new DBWrapper($name))->createQueryBuilder();
|
2022-08-21 16:08:20 +08:00
|
|
|
|
}
|
2022-08-23 17:51:20 +08:00
|
|
|
|
|
2023-01-04 20:43:03 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取 Redis 操作类
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string $name 使用的 Redis 连接名称
|
|
|
|
|
|
*/
|
|
|
|
|
|
function redis(string $name = 'default'): RedisWrapper
|
|
|
|
|
|
{
|
|
|
|
|
|
return new RedisWrapper($name);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-23 17:51:20 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取 / 设置配置项
|
|
|
|
|
|
*
|
|
|
|
|
|
* 传入键名和(或)默认值,获取配置项
|
|
|
|
|
|
* 传入数组,设置配置项
|
|
|
|
|
|
* 不传参数,返回配置容器
|
|
|
|
|
|
*
|
2022-11-08 17:33:25 +08:00
|
|
|
|
* @param null|array|string $key 键名
|
|
|
|
|
|
* @param null|mixed $default 默认值
|
2022-08-23 18:18:20 +08:00
|
|
|
|
* @return mixed|void|ZMConfig
|
2022-08-23 17:51:20 +08:00
|
|
|
|
*/
|
2022-11-08 17:28:07 +08:00
|
|
|
|
function config(array|string $key = null, mixed $default = null)
|
2022-08-23 17:51:20 +08:00
|
|
|
|
{
|
2022-08-23 18:18:20 +08:00
|
|
|
|
$config = ZMConfig::getInstance();
|
2022-08-23 17:51:20 +08:00
|
|
|
|
if (is_null($key)) {
|
2022-08-23 18:18:20 +08:00
|
|
|
|
return $config;
|
2022-08-23 17:51:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (is_array($key)) {
|
2022-08-23 18:18:20 +08:00
|
|
|
|
$config->set($key);
|
|
|
|
|
|
return;
|
2022-08-23 17:51:20 +08:00
|
|
|
|
}
|
2022-08-23 18:18:20 +08:00
|
|
|
|
return $config->get($key, $default);
|
2022-08-23 17:51:20 +08:00
|
|
|
|
}
|
2022-12-20 20:10:40 +08:00
|
|
|
|
|
2023-03-05 14:35:59 +08:00
|
|
|
|
function bot(string $bot_id = '', string $platform = ''): ZM\Context\BotContext
|
2022-12-20 20:10:40 +08:00
|
|
|
|
{
|
2023-03-05 14:35:59 +08:00
|
|
|
|
return BotMap::getBotContext($bot_id, $platform);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function bot_connect(int $flag, int $fd)
|
|
|
|
|
|
{
|
|
|
|
|
|
return BotMap::getConnectContext($flag, $fd);
|
2022-12-20 20:10:40 +08:00
|
|
|
|
}
|
2022-12-31 15:27:09 +08:00
|
|
|
|
|
2023-01-13 14:19:20 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取一个 KV 库实例
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string $name KV 库名称
|
|
|
|
|
|
* @return CacheInterface
|
|
|
|
|
|
*/
|
2022-12-31 19:08:52 +08:00
|
|
|
|
function kv(string $name = ''): Psr\SimpleCache\CacheInterface
|
2022-12-31 15:27:09 +08:00
|
|
|
|
{
|
|
|
|
|
|
global $kv_class;
|
|
|
|
|
|
if (!$kv_class) {
|
2023-03-05 14:35:59 +08:00
|
|
|
|
$kv_class = config('global.kv.use', LightCache::class);
|
2022-12-31 15:27:09 +08:00
|
|
|
|
}
|
2022-12-31 19:08:52 +08:00
|
|
|
|
/* @phpstan-ignore-next-line */
|
|
|
|
|
|
return is_a($kv_class, KVInterface::class, true) ? $kv_class::open($name) : new $kv_class($name);
|
2022-12-31 15:27:09 +08:00
|
|
|
|
}
|
2023-01-15 21:30:44 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取环境变量
|
|
|
|
|
|
*/
|
|
|
|
|
|
function env(string $key, mixed $default = null): mixed
|
|
|
|
|
|
{
|
|
|
|
|
|
// TODO: 重新思考容器绑定的加载方式,从而在此处使用 interface
|
|
|
|
|
|
return resolve(Environment::class)->get($key, $default);
|
|
|
|
|
|
}
|
2023-02-11 21:19:58 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 【助手函数】HttpFactory 快速创建一个 Response
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param int $status_code 状态码
|
|
|
|
|
|
* @param null|string $reason 原因(留空则使用状态码本身的)
|
|
|
|
|
|
* @param array $headers 请求头
|
|
|
|
|
|
* @param mixed $body HTTP Body
|
|
|
|
|
|
* @param string $protocol HTTP 协议版本
|
|
|
|
|
|
*/
|
|
|
|
|
|
function zm_http_response(int $status_code = 200, ?string $reason = null, array $headers = [], mixed $body = null, string $protocol = '1.1'): ResponseInterface
|
|
|
|
|
|
{
|
|
|
|
|
|
return HttpFactory::createResponse($status_code, $reason, $headers, $body, $protocol);
|
|
|
|
|
|
}
|