mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-21 23:55:35 +08:00
Merge branch 'master' into master
This commit is contained in:
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Custom\Command;
|
||||
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class CustomCommand extends Command
|
||||
{
|
||||
// the name of the command (the part after "bin/console")
|
||||
protected static $defaultName = 'custom';
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("custom description | 自定义命令的描述字段");
|
||||
$this->addOption("failure", null, null, "以错误码为1返回结果");
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
if ($input->getOption("failure")) {
|
||||
$output->writeln("<error>Hello error! I am wrong message.</error>");
|
||||
return Command::FAILURE;
|
||||
} else {
|
||||
$output->writeln("<comment>Hello world! I am successful message.</comment>");
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpFullyQualifiedNameUsageInspection */ #plain
|
||||
|
||||
//这里写你的全局函数
|
||||
function pgo(callable $func, $name = "default") {
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
namespace Module\Example;
|
||||
|
||||
use ZM\Annotation\Http\Middleware;
|
||||
use ZM\Annotation\Swoole\OnSwooleEvent;
|
||||
use ZM\Annotation\Swoole\OnCloseEvent;
|
||||
use ZM\Annotation\Swoole\OnOpenEvent;
|
||||
use ZM\Annotation\Swoole\OnRequestEvent;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Store\Redis\ZMRedis;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
/**
|
||||
@@ -19,29 +20,6 @@ use ZM\Utils\ZMUtil;
|
||||
*/
|
||||
class Hello
|
||||
{
|
||||
/**
|
||||
* 一个简单的redis连接池使用demo,将下方user_id改为你自己的QQ号即可(为了不被不法分子利用)
|
||||
* @CQCommand("redis_test",user_id=627577391)
|
||||
*/
|
||||
public function testCase() {
|
||||
$a = new ZMRedis();
|
||||
$redis = $a->get();
|
||||
$r1 = ctx()->getArgs(ZM_MATCH_FIRST, "请说出你想设置的操作[r/w]");
|
||||
switch ($r1) {
|
||||
case "r":
|
||||
$k = ctx()->getArgs(ZM_MATCH_FIRST, "请说出你想读取的键名");
|
||||
$result = $redis->get($k);
|
||||
ctx()->reply("结果:" . $result);
|
||||
break;
|
||||
case "w":
|
||||
$k = ctx()->getArgs(ZM_MATCH_FIRST, "请说出你想写入的键名");
|
||||
$v = ctx()->getArgs(ZM_MATCH_FIRST, "请说出你想写入的字符串");
|
||||
$result = $redis->set($k, $v);
|
||||
ctx()->reply("结果:" . ($result ? "成功" : "失败"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用命令 .reload 发给机器人远程重载,注意将 user_id 换成你自己的 QQ
|
||||
* @CQCommand(".reload",user_id=627577391)
|
||||
@@ -77,9 +55,9 @@ class Hello
|
||||
*/
|
||||
public function randNum() {
|
||||
// 获取第一个数字类型的参数
|
||||
$num1 = ctx()->getArgs(ZM_MATCH_NUMBER, "请输入第一个数字");
|
||||
$num1 = ctx()->getNumArg("请输入第一个数字");
|
||||
// 获取第二个数字类型的参数
|
||||
$num2 = ctx()->getArgs(ZM_MATCH_NUMBER, "请输入第二个数字");
|
||||
$num2 = ctx()->getNumArg("请输入第二个数字");
|
||||
$a = min(intval($num1), intval($num2));
|
||||
$b = max(intval($num1), intval($num2));
|
||||
// 回复用户结果
|
||||
@@ -111,12 +89,12 @@ class Hello
|
||||
* @return string
|
||||
*/
|
||||
public function paramGet($param) {
|
||||
return "Your name: {$param["name"]}";
|
||||
return "Hello, ".$param["name"];
|
||||
}
|
||||
|
||||
/**
|
||||
* 在机器人连接后向终端输出信息
|
||||
* @OnSwooleEvent("open",rule="connectIsQQ()")
|
||||
* @OnOpenEvent("qq")
|
||||
* @param $conn
|
||||
*/
|
||||
public function onConnect(ConnectionObject $conn) {
|
||||
@@ -125,7 +103,7 @@ class Hello
|
||||
|
||||
/**
|
||||
* 在机器人断开连接后向终端输出信息
|
||||
* @OnSwooleEvent("close",rule="connectIsQQ()")
|
||||
* @OnCloseEvent("qq")
|
||||
* @param ConnectionObject $conn
|
||||
*/
|
||||
public function onDisconnect(ConnectionObject $conn) {
|
||||
@@ -134,7 +112,7 @@ class Hello
|
||||
|
||||
/**
|
||||
* 阻止 Chrome 自动请求 /favicon.ico 导致的多条请求并发和干扰
|
||||
* @OnSwooleEvent("request",rule="ctx()->getRequest()->server['request_uri'] == '/favicon.ico'",level=200)
|
||||
* @OnRequestEvent(rule="ctx()->getRequest()->server['request_uri'] == '/favicon.ico'",level=200)
|
||||
*/
|
||||
public function onRequest() {
|
||||
EventDispatcher::interrupt();
|
||||
@@ -142,7 +120,7 @@ class Hello
|
||||
|
||||
/**
|
||||
* 框架会默认关闭未知的WebSocket链接,因为这个绑定的事件,你可以根据你自己的需求进行修改
|
||||
* @OnSwooleEvent(type="open",rule="connectIsDefault()")
|
||||
* @OnOpenEvent("default")
|
||||
*/
|
||||
public function closeUnknownConn() {
|
||||
Console::info("Unknown connection , I will close it.");
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Module\Middleware;
|
||||
|
||||
use Exception;
|
||||
use ZM\Annotation\Http\HandleAfter;
|
||||
use ZM\Annotation\Http\HandleBefore;
|
||||
use ZM\Annotation\Http\HandleException;
|
||||
@@ -37,8 +38,11 @@ class TimerMiddleware implements MiddlewareInterface
|
||||
|
||||
/**
|
||||
* @HandleException(\Exception::class)
|
||||
* @param Exception $e
|
||||
* @throws Exception
|
||||
*/
|
||||
public function onException() {
|
||||
public function onException(Exception $e) {
|
||||
Console::error("Using " . round((microtime(true) - $this->starttime) * 1000, 2) . " ms but an Exception occurred.");
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,70 +34,59 @@ class CQ
|
||||
return " ";
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送emoji表情
|
||||
* @param $id
|
||||
* @return string
|
||||
*/
|
||||
public static function emoji($id) {
|
||||
if (is_numeric($id)) {
|
||||
return "[CQ:emoji,id=" . $id . "]";
|
||||
}
|
||||
Console::warning("传入的emoji id($id)错误!");
|
||||
return " ";
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送原创表情,存放在酷Q目录的data/bface/下
|
||||
* @param $id
|
||||
* @return string
|
||||
*/
|
||||
public static function bface($id) {
|
||||
return "[CQ:bface,id=" . $id . "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送小表情
|
||||
* @param $id
|
||||
* @return string
|
||||
*/
|
||||
public static function sface($id) {
|
||||
if (is_numeric($id)) {
|
||||
return "[CQ:sface,id=" . $id . "]";
|
||||
}
|
||||
Console::warning("传入的sface id($id)错误!");
|
||||
return " ";
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送图片
|
||||
* cache为<FALSE>时禁用CQ-HTTP-API插件的缓存
|
||||
* @param $file
|
||||
* @param bool $cache
|
||||
* @param bool $flash
|
||||
* @param bool $proxy
|
||||
* @param int $timeout
|
||||
* @return string
|
||||
*/
|
||||
public static function image($file, $cache = true) {
|
||||
if ($cache === false)
|
||||
return "[CQ:image,file=" . $file . ",cache=0]";
|
||||
else
|
||||
return "[CQ:image,file=" . $file . "]";
|
||||
public static function image($file, $cache = true, $flash = false, $proxy = true, $timeout = -1) {
|
||||
return
|
||||
"[CQ:image,file=" . self::encode($file, true) .
|
||||
(!$cache ? ",cache=0" : "") .
|
||||
($flash ? ",type=flash" : "") .
|
||||
(!$proxy ? ",proxy=false" : "") .
|
||||
($timeout != -1 ? (",timeout=" . intval($timeout)) : "") .
|
||||
"]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送语音
|
||||
* cache为<FALSE>时禁用CQ-HTTP-API插件的缓存
|
||||
* magic为<TRUE>时标记为变声
|
||||
* @param $file
|
||||
* @param bool $magic
|
||||
* @param bool $cache
|
||||
* @param bool $proxy
|
||||
* @param int $timeout
|
||||
* @return string
|
||||
*/
|
||||
public static function record($file, $magic = false, $cache = true) {
|
||||
if ($cache === false) $c = ",cache=0";
|
||||
else $c = "";
|
||||
if ($magic === true) $m = ",magic=true";
|
||||
else $m = "";
|
||||
return "[CQ:record,file=" . $file . $c . $m . "]";
|
||||
public static function record($file, $magic = false, $cache = true, $proxy = true, $timeout = -1) {
|
||||
return
|
||||
"[CQ:record,file=" . self::encode($file, true) .
|
||||
(!$cache ? ",cache=0" : "") .
|
||||
($magic ? ",magic=1" : "") .
|
||||
(!$proxy ? ",proxy=false" : "") .
|
||||
($timeout != -1 ? (",timeout=" . intval($timeout)) : "") .
|
||||
"]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短视频
|
||||
* @param $file
|
||||
* @param bool $cache
|
||||
* @param bool $proxy
|
||||
* @param int $timeout
|
||||
* @return string
|
||||
*/
|
||||
public static function video($file, $cache = true, $proxy = true, $timeout = -1) {
|
||||
return
|
||||
"[CQ:video,file=" . self::encode($file, true) .
|
||||
(!$cache ? ",cache=0" : "") .
|
||||
(!$proxy ? ",proxy=false" : "") .
|
||||
($timeout != -1 ? (",timeout=" . intval($timeout)) : "") .
|
||||
"]";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,6 +113,69 @@ class CQ
|
||||
return "[CQ:shake]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送新的戳一戳
|
||||
* @param $type
|
||||
* @param $id
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public static function poke($type, $id, $name = "") {
|
||||
return "[CQ:poke,type=$type,id=$id" . ($name != "" ? (",name=".self::encode($name, true)) : "") . "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送匿名消息
|
||||
* @param int $ignore
|
||||
* @return string
|
||||
*/
|
||||
public static function anonymous($ignore = 1) {
|
||||
return "[CQ:anonymous" . ($ignore != 1 ? ",ignore=0" : "") . "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送链接分享(只能在单条回复中单独使用)
|
||||
* @param $url
|
||||
* @param $title
|
||||
* @param null $content
|
||||
* @param null $image
|
||||
* @return string
|
||||
*/
|
||||
public static function share($url, $title, $content = null, $image = null) {
|
||||
if ($content === null) $c = "";
|
||||
else $c = ",content=" . self::encode($content, true);
|
||||
if ($image === null) $i = "";
|
||||
else $i = ",image=" . self::encode($image, true);
|
||||
return "[CQ:share,url=" . self::encode($url, true) . ",title=" . self::encode($title, true) . $c . $i . "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送好友或群推荐名片
|
||||
* @param $type
|
||||
* @param $id
|
||||
* @return string
|
||||
*/
|
||||
public static function contact($type, $id) {
|
||||
return "[CQ:contact,type=$type,id=$id]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送位置
|
||||
* @param $lat
|
||||
* @param $lon
|
||||
* @param string $title
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
public static function location($lat, $lon, $title = "", $content = "") {
|
||||
return "[CQ:location" .
|
||||
",lat=".self::encode($lat, true) .
|
||||
",lon=".self::encode($lon, true).
|
||||
($title != "" ? (",title=".self::encode($title, true)) : "") .
|
||||
($content != "" ? (",content=".self::encode($content, true)) : "") .
|
||||
"]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送音乐分享(只能在单条回复中单独使用)
|
||||
* qq、163、xiami为内置分享,需要先通过搜索功能获取id后使用
|
||||
@@ -136,10 +188,10 @@ class CQ
|
||||
* $image 为音乐卡片的图片链接地址(可忽略)
|
||||
* @param $type
|
||||
* @param $id_or_url
|
||||
* @param string $audio
|
||||
* @param string $title
|
||||
* @param string $content
|
||||
* @param string $image
|
||||
* @param null $audio
|
||||
* @param null $title
|
||||
* @param null $content
|
||||
* @param null $image
|
||||
* @return string
|
||||
*/
|
||||
public static function music($type, $id_or_url, $audio = null, $title = null, $content = null, $image = null) {
|
||||
@@ -154,42 +206,54 @@ class CQ
|
||||
return " ";
|
||||
}
|
||||
if ($content === null) $c = "";
|
||||
else $c = ",content=" . $content;
|
||||
else $c = ",content=" . self::encode($content, true);
|
||||
if ($image === null) $i = "";
|
||||
else $i = ",image=" . $image;
|
||||
return "[CQ:music,type=custom,url=" . $id_or_url . ",audio=" . $audio . ",title=" . $title . $c . $i . "]";
|
||||
else $i = ",image=" . self::encode($image, true);
|
||||
return "[CQ:music,type=custom,url=" .
|
||||
self::encode($id_or_url, true) .
|
||||
",audio=" . self::encode($audio, true) . ",title=" . self::encode($title, true) . $c . $i .
|
||||
"]";
|
||||
default:
|
||||
Console::warning("传入的music type($type)错误!");
|
||||
return " ";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送链接分享(只能在单条回复中单独使用)
|
||||
* @param $url
|
||||
* @param $title
|
||||
* @param null $content
|
||||
* @param null $image
|
||||
* @return string
|
||||
*/
|
||||
public static function share($url, $title, $content = null, $image = null) {
|
||||
if ($content === null) $c = "";
|
||||
else $c = ",content=" . $content;
|
||||
if ($image === null) $i = "";
|
||||
else $i = ",image=" . $image;
|
||||
return "[CQ:share,url=" . $url . ",title=" . $title . $c . $i . "]";
|
||||
public static function forward($id) {
|
||||
return "[CQ:forward,id=$id]";
|
||||
}
|
||||
|
||||
public static function node($user_id, $nickname, $content) {
|
||||
return "[CQ:node,user_id=$user_id,nickname=".self::encode($nickname, true).",content=" . self::encode($content, true) . "]";
|
||||
}
|
||||
|
||||
public static function xml($data) {
|
||||
return "[CQ:xml,data=" . self::encode($data, true) . "]";
|
||||
}
|
||||
|
||||
public static function json($data, $resid = 0) {
|
||||
return "[CQ:json,data=" . self::encode($data, true) . ",resid=" . intval($resid) . "]";
|
||||
}
|
||||
|
||||
public static function _custom(string $type_name, $params) {
|
||||
$code = "[CQ:" . $type_name;
|
||||
foreach ($params as $k => $v) {
|
||||
$code .= "," . $k . "=" . self::escape($v, true);
|
||||
}
|
||||
$code .= "]";
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反转义字符串中的CQ码敏感符号
|
||||
* @param $str
|
||||
* @param $msg
|
||||
* @param bool $is_content
|
||||
* @return mixed
|
||||
*/
|
||||
public static function decode($str) {
|
||||
$str = str_replace("&", "&", $str);
|
||||
$str = str_replace("[", "[", $str);
|
||||
$str = str_replace("]", "]", $str);
|
||||
return $str;
|
||||
public static function decode($msg, $is_content = false) {
|
||||
$msg = str_replace(["&", "[", "]"], ["&", "[", "]"], $msg);
|
||||
if ($is_content) $msg = str_replace(",", ",", $msg);
|
||||
return $msg;
|
||||
}
|
||||
|
||||
public static function replace($str) {
|
||||
@@ -199,42 +263,97 @@ class CQ
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义CQ码
|
||||
* 转义CQ码的特殊字符,同encode
|
||||
* @param $msg
|
||||
* @param bool $is_content
|
||||
* @return mixed
|
||||
*/
|
||||
public static function escape($msg) {
|
||||
$msg = str_replace("&", "&", $msg);
|
||||
$msg = str_replace("[", "[", $msg);
|
||||
$msg = str_replace("]", "]", $msg);
|
||||
public static function escape($msg, $is_content = false) {
|
||||
$msg = str_replace(["&", "[", "]"], ["&", "[", "]"], $msg);
|
||||
if ($is_content) $msg = str_replace(",", ",", $msg);
|
||||
return $msg;
|
||||
}
|
||||
|
||||
public static function encode($str) {
|
||||
return self::escape($str);
|
||||
/**
|
||||
* 转义CQ码的特殊字符
|
||||
* @param $msg
|
||||
* @param false $is_content
|
||||
* @return mixed
|
||||
*/
|
||||
public static function encode($msg, $is_content = false) {
|
||||
$msg = str_replace(["&", "[", "]"], ["&", "[", "]"], $msg);
|
||||
if ($is_content) $msg = str_replace(",", ",", $msg);
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除消息中所有的CQ码并返回移除CQ码后的消息
|
||||
* @param $msg
|
||||
* @return string
|
||||
*/
|
||||
public static function removeCQ($msg) {
|
||||
while (($cq = self::getCQ($msg)) !== null) {
|
||||
$msg = str_replace(mb_substr($msg, $cq["start"], $cq["end"] - $cq["start"] + 1), "", $msg);
|
||||
$final = "";
|
||||
$last_end = 0;
|
||||
foreach(self::getAllCQ($msg) as $k => $v) {
|
||||
$final .= mb_substr($msg, $last_end, $v["start"] - $last_end);
|
||||
$last_end = $v["end"] + 1;
|
||||
}
|
||||
return $msg;
|
||||
$final .= mb_substr($msg, $last_end);
|
||||
return $final;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息中第一个CQ码
|
||||
* @param $msg
|
||||
* @return array|null
|
||||
*/
|
||||
public static function getCQ($msg) {
|
||||
if (($start = mb_strpos($msg, '[')) === false) return null;
|
||||
if (($end = mb_strpos($msg, ']')) === false) return null;
|
||||
$msg = mb_substr($msg, $start + 1, $end - $start - 1);
|
||||
if (mb_substr($msg, 0, 3) != "CQ:") return null;
|
||||
$msg = mb_substr($msg, 3);
|
||||
$msg2 = explode(",", $msg);
|
||||
$type = array_shift($msg2);
|
||||
$array = [];
|
||||
foreach ($msg2 as $k => $v) {
|
||||
$ss = explode("=", $v);
|
||||
$sk = array_shift($ss);
|
||||
$array[$sk] = implode("=", $ss);
|
||||
if (($head = mb_strpos($msg, "[CQ:")) !== false) {
|
||||
$key_offset = mb_substr($msg, $head);
|
||||
$close = mb_strpos($key_offset, "]");
|
||||
if ($close === false) return null;
|
||||
$content = mb_substr($msg, $head + 4, $close + $head - mb_strlen($msg));
|
||||
$exp = explode(",", $content);
|
||||
$cq["type"] = array_shift($exp);
|
||||
foreach ($exp as $k => $v) {
|
||||
$ss = explode("=", $v);
|
||||
$sk = array_shift($ss);
|
||||
$cq["params"][$sk] = self::decode(implode("=", $ss), true);
|
||||
}
|
||||
$cq["start"] = $head;
|
||||
$cq["end"] = $close + $head;
|
||||
return $cq;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return ["type" => $type, "params" => $array, "start" => $start, "end" => $end];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息中所有的CQ码
|
||||
* @param $msg
|
||||
* @return array
|
||||
*/
|
||||
public static function getAllCQ($msg) {
|
||||
$cqs = [];
|
||||
$offset = 0;
|
||||
while (($head = mb_strpos(($submsg = mb_substr($msg, $offset)), "[CQ:")) !== false) {
|
||||
$key_offset = mb_substr($submsg, $head);
|
||||
$tmpmsg = mb_strpos($key_offset, "]");
|
||||
if ($tmpmsg === false) break; // 没闭合,不算CQ码
|
||||
$content = mb_substr($submsg, $head + 4, $tmpmsg + $head - mb_strlen($submsg));
|
||||
$exp = explode(",", $content);
|
||||
$cq = [];
|
||||
$cq["type"] = array_shift($exp);
|
||||
foreach ($exp as $k => $v) {
|
||||
$ss = explode("=", $v);
|
||||
$sk = array_shift($ss);
|
||||
$cq["params"][$sk] = self::decode(implode("=", $ss), true);
|
||||
}
|
||||
$cq["start"] = $offset + $head;
|
||||
$cq["end"] = $offset + $tmpmsg + $head;
|
||||
$offset += $tmpmsg + 1;
|
||||
$cqs[] = $cq;
|
||||
}
|
||||
return $cqs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,16 @@
|
||||
|
||||
namespace ZM\API;
|
||||
|
||||
use Co;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\Lock\SpinLock;
|
||||
use ZM\Store\ZMAtomic;
|
||||
use ZM\Utils\CoMessage;
|
||||
|
||||
trait CQAPI
|
||||
{
|
||||
/**
|
||||
* @param ConnectionObject $connection
|
||||
* @param $connection
|
||||
* @param $reply
|
||||
* @param |null $function
|
||||
* @return bool|array
|
||||
@@ -35,21 +34,20 @@ trait CQAPI
|
||||
$r[$api_id] = [
|
||||
"data" => $reply,
|
||||
"time" => microtime(true),
|
||||
"self_id" => $connection->getOption("connect_id")
|
||||
"self_id" => $connection->getOption("connect_id"),
|
||||
"echo" => $api_id
|
||||
];
|
||||
if ($function === true) $r[$api_id]["coroutine"] = Co::getuid();
|
||||
LightCacheInside::set("wait_api", "wait_api", $r);
|
||||
SpinLock::unlock("wait_api");
|
||||
if (server()->push($connection->getFd(), json_encode($reply))) {
|
||||
if ($function === true) {
|
||||
Co::suspend();
|
||||
return CoMessage::yieldByWS($r[$api_id], ["echo"], 60);
|
||||
} else {
|
||||
SpinLock::lock("wait_api");
|
||||
$r = LightCacheInside::get("wait_api", "wait_api");
|
||||
$data = $r[$api_id];
|
||||
unset($r[$api_id]);
|
||||
LightCacheInside::set("wait_api", "wait_api", $r);
|
||||
SpinLock::unlock("wait_api");
|
||||
return isset($data['result']) ? $data['result'] : null;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -59,7 +59,7 @@ class ZMRobot
|
||||
public static function getAllRobot() {
|
||||
$r = ManagerGM::getAllByName('qq');
|
||||
$obj = [];
|
||||
foreach($r as $v) {
|
||||
foreach ($r as $v) {
|
||||
$obj[] = new ZMRobot($v);
|
||||
}
|
||||
return $obj;
|
||||
@@ -228,9 +228,9 @@ class ZMRobot
|
||||
/**
|
||||
* 群组单人禁言
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_ban-%E7%BE%A4%E7%BB%84%E5%8D%95%E4%BA%BA%E7%A6%81%E8%A8%80
|
||||
* @param int $group_id
|
||||
* @param int $user_id
|
||||
* @param int $duration
|
||||
* @param $group_id
|
||||
* @param $user_id
|
||||
* @param $duration
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupBan($group_id, $user_id, $duration = 1800) {
|
||||
|
||||
@@ -9,10 +9,10 @@ use ZM\Console\Console;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionMethod;
|
||||
use ZM\Annotation\Http\{HandleAfter, HandleBefore, Controller, HandleException, Middleware, MiddlewareClass, RequestMapping};
|
||||
use ZM\Annotation\Http\{HandleAfter, HandleBefore, HandleException, Middleware, MiddlewareClass, RequestMapping};
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
use ZM\Annotation\Module\Closed;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Http\RouteManager;
|
||||
|
||||
class AnnotationParser
|
||||
{
|
||||
@@ -33,7 +33,7 @@ class AnnotationParser
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->start_time = microtime(true);
|
||||
$this->loadAnnotationClasses();
|
||||
//$this->loadAnnotationClasses();
|
||||
$this->req_mapping[0] = [
|
||||
'id' => 0,
|
||||
'pid' => -1,
|
||||
@@ -47,7 +47,7 @@ class AnnotationParser
|
||||
*/
|
||||
public function registerMods() {
|
||||
foreach ($this->path_list as $path) {
|
||||
Console::debug("parsing annotation in ".$path[0]);
|
||||
Console::debug("parsing annotation in " . $path[0]);
|
||||
$all_class = getAllClasses($path[0], $path[1]);
|
||||
$this->reader = new AnnotationReader();
|
||||
foreach ($all_class as $v) {
|
||||
@@ -88,8 +88,9 @@ class AnnotationParser
|
||||
|
||||
//预处理1:将适用于每一个函数的注解到类注解重新注解到每个函数下面
|
||||
if ($vs instanceof ErgodicAnnotation) {
|
||||
foreach ($this->annotation_map[$v]["methods"] as $method) {
|
||||
foreach (($this->annotation_map[$v]["methods"] ?? []) as $method) {
|
||||
$copy = clone $vs;
|
||||
/** @noinspection PhpUndefinedFieldInspection */
|
||||
$copy->method = $method->getName();
|
||||
$this->annotation_map[$v]["methods_annotations"][$method->getName()][] = $copy;
|
||||
}
|
||||
@@ -100,20 +101,20 @@ class AnnotationParser
|
||||
unset($this->annotation_map[$v]);
|
||||
continue 2;
|
||||
} elseif ($vs instanceof MiddlewareClass) {
|
||||
Console::verbose("正在注册中间件 " . $reflection_class->getName());
|
||||
Console::debug("正在注册中间件 " . $reflection_class->getName());
|
||||
$rs = $this->registerMiddleware($vs, $reflection_class);
|
||||
$this->middlewares[$rs["name"]] = $rs;
|
||||
}
|
||||
}
|
||||
|
||||
//预处理3:处理每个函数上面的特殊注解,就是需要操作一些东西的
|
||||
foreach ($this->annotation_map[$v]["methods_annotations"] as $method_name => $methods_annotations) {
|
||||
foreach (($this->annotation_map[$v]["methods_annotations"] ?? []) as $method_name => $methods_annotations) {
|
||||
foreach ($methods_annotations as $method_anno) {
|
||||
/** @var AnnotationBase $method_anno */
|
||||
$method_anno->class = $v;
|
||||
$method_anno->method = $method_name;
|
||||
if ($method_anno instanceof RequestMapping) {
|
||||
$this->registerRequestMapping($method_anno, $method_name, $v, $methods_annotations); //TODO: 用symfony的routing重写
|
||||
RouteManager::importRouteByAnnotation($method_anno, $method_name, $v, $methods_annotations);
|
||||
} elseif ($method_anno instanceof Middleware) {
|
||||
$this->middleware_map[$method_anno->class][$method_anno->method][] = $method_anno->middleware;
|
||||
}
|
||||
@@ -121,11 +122,6 @@ class AnnotationParser
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//预处理4:生成路由树(换成symfony后就不需要了)
|
||||
$tree = $this->genTree($this->req_mapping);
|
||||
$this->req_mapping = $tree[0];
|
||||
|
||||
Console::debug("解析注解完毕!");
|
||||
}
|
||||
|
||||
@@ -135,11 +131,11 @@ class AnnotationParser
|
||||
public function generateAnnotationEvents() {
|
||||
$o = [];
|
||||
foreach ($this->annotation_map as $module => $obj) {
|
||||
foreach ($obj["class_annotations"] as $class_annotation) {
|
||||
foreach (($obj["class_annotations"] ?? []) as $class_annotation) {
|
||||
if ($class_annotation instanceof ErgodicAnnotation) continue;
|
||||
else $o[get_class($class_annotation)][] = $class_annotation;
|
||||
}
|
||||
foreach ($obj["methods_annotations"] as $method_name => $methods_annotations) {
|
||||
foreach (($obj["methods_annotations"] ?? []) as $method_name => $methods_annotations) {
|
||||
foreach ($methods_annotations as $annotation) {
|
||||
$o[get_class($annotation)][] = $annotation;
|
||||
}
|
||||
@@ -175,109 +171,6 @@ class AnnotationParser
|
||||
|
||||
//private function below
|
||||
|
||||
private function registerRequestMapping(RequestMapping $vss, $method, $class, $methods_annotations) {
|
||||
$prefix = '';
|
||||
foreach ($methods_annotations as $annotation) {
|
||||
if ($annotation instanceof Controller) {
|
||||
$prefix = $annotation->prefix;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$array = $this->req_mapping;
|
||||
$uid = count($array);
|
||||
$prefix_exp = explode("/", $prefix);
|
||||
$route_exp = explode("/", $vss->route);
|
||||
foreach ($prefix_exp as $k => $v) {
|
||||
if ($v == "" || $v == ".." || $v == ".") {
|
||||
unset($prefix_exp[$k]);
|
||||
}
|
||||
}
|
||||
foreach ($route_exp as $k => $v) {
|
||||
if ($v == "" || $v == ".." || $v == ".") {
|
||||
unset($route_exp[$k]);
|
||||
}
|
||||
}
|
||||
if ($prefix_exp == [] && $route_exp == []) {
|
||||
$array[0]['method'] = $method;
|
||||
$array[0]['class'] = $class;
|
||||
$array[0]['request_method'] = $vss->request_method;
|
||||
$array[0]['route'] = $vss->route;
|
||||
$this->req_mapping = $array;
|
||||
return;
|
||||
}
|
||||
$pid = 0;
|
||||
while (($shift = array_shift($prefix_exp)) !== null) {
|
||||
foreach ($array as $k => $v) {
|
||||
if ($v["name"] == $shift && $pid == ($v["pid"] ?? -1)) {
|
||||
$pid = $v["id"];
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
$array[$uid++] = [
|
||||
'id' => $uid - 1,
|
||||
'pid' => $pid,
|
||||
'name' => $shift
|
||||
];
|
||||
$pid = $uid - 1;
|
||||
}
|
||||
while (($shift = array_shift($route_exp)) !== null) {
|
||||
/*if (mb_substr($shift, 0, 1) == "{" && mb_substr($shift, -1, 1) == "}") {
|
||||
$p->removeAllRoute();
|
||||
Console::info("移除本节点其他所有路由中");
|
||||
}*/
|
||||
foreach ($array as $k => $v) {
|
||||
if ($v["name"] == $shift && $pid == ($v["pid"] ?? -1)) {
|
||||
$pid = $v["id"];
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
if (mb_substr($shift, 0, 1) == "{" && mb_substr($shift, -1, 1) == "}") {
|
||||
foreach ($array as $k => $v) {
|
||||
if ($pid == $v["id"]) {
|
||||
$array[$k]["param_route"] = $uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
$array[$uid++] = [
|
||||
'id' => $uid - 1,
|
||||
'pid' => $pid,
|
||||
'name' => $shift
|
||||
];
|
||||
$pid = $uid - 1;
|
||||
}
|
||||
$array[$uid - 1]['method'] = $method;
|
||||
$array[$uid - 1]['class'] = $class;
|
||||
$array[$uid - 1]['request_method'] = $vss->request_method;
|
||||
$array[$uid - 1]['route'] = $vss->route;
|
||||
$this->req_mapping = $array;
|
||||
}
|
||||
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
private function loadAnnotationClasses() {
|
||||
$class = getAllClasses(WORKING_DIR . "/src/ZM/Annotation/", "ZM\\Annotation");
|
||||
foreach ($class as $v) {
|
||||
$s = WORKING_DIR . '/src/' . str_replace("\\", "/", $v) . ".php";
|
||||
//Console::debug("Requiring annotation " . $s);
|
||||
require_once $s;
|
||||
}
|
||||
$class = getAllClasses(DataProvider::getWorkingDir() . "/src/Custom/Annotation/", "Custom\\Annotation");
|
||||
foreach ($class as $v) {
|
||||
$s = DataProvider::getWorkingDir() . '/src/' . str_replace("\\", "/", $v) . ".php";
|
||||
Console::debug("Requiring custom annotation " . $s);
|
||||
require_once $s;
|
||||
}
|
||||
}
|
||||
|
||||
private function genTree($items) {
|
||||
$tree = array();
|
||||
foreach ($items as $item)
|
||||
if (isset($items[$item['pid']]))
|
||||
$items[$item['pid']]['son'][] = &$items[$item['id']];
|
||||
else
|
||||
$tree[] = &$items[$item['id']];
|
||||
return $tree;
|
||||
}
|
||||
|
||||
private function registerMiddleware(MiddlewareClass $vs, ReflectionClass $reflection_class) {
|
||||
$result = [
|
||||
"class" => "\\" . $reflection_class->getName(),
|
||||
@@ -297,7 +190,7 @@ class AnnotationParser
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function sortByLevel(&$events, string $class_name, $prefix = "") {
|
||||
public function sortByLevel(&$events, string $class_name, $prefix = "") {
|
||||
if (is_a($class_name, Level::class, true)) {
|
||||
$class_name .= $prefix;
|
||||
usort($events[$class_name], function ($a, $b) {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
namespace ZM\Annotation\CQ;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
namespace ZM\Annotation\Http;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
|
||||
|
||||
21
src/ZM/Annotation/Swoole/OnCloseEvent.php
Normal file
21
src/ZM/Annotation/Swoole/OnCloseEvent.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Swoole;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
* Class OnCloseEvent
|
||||
* @package ZM\Annotation\Swoole
|
||||
*/
|
||||
class OnCloseEvent extends OnSwooleEventBase
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $connect_type = "default";
|
||||
}
|
||||
21
src/ZM/Annotation/Swoole/OnMessageEvent.php
Normal file
21
src/ZM/Annotation/Swoole/OnMessageEvent.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Swoole;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
* Class OnMessageEvent
|
||||
* @package ZM\Annotation\Swoole
|
||||
*/
|
||||
class OnMessageEvent extends OnSwooleEventBase
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $connect_type = "default";
|
||||
}
|
||||
21
src/ZM/Annotation/Swoole/OnOpenEvent.php
Normal file
21
src/ZM/Annotation/Swoole/OnOpenEvent.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Swoole;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
* Class OnOpenEvent
|
||||
* @package ZM\Annotation\Swoole
|
||||
*/
|
||||
class OnOpenEvent extends OnSwooleEventBase
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $connect_type = "default";
|
||||
}
|
||||
24
src/ZM/Annotation/Swoole/OnPipeMessageEvent.php
Normal file
24
src/ZM/Annotation/Swoole/OnPipeMessageEvent.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Swoole;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
|
||||
/**
|
||||
* Class OnPipeMessageEvent
|
||||
* @package ZM\Annotation\Swoole
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
class OnPipeMessageEvent extends AnnotationBase
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Required()
|
||||
*/
|
||||
public $action;
|
||||
}
|
||||
17
src/ZM/Annotation/Swoole/OnRequestEvent.php
Normal file
17
src/ZM/Annotation/Swoole/OnRequestEvent.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Swoole;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
* Class OnRequestEvent
|
||||
* @package ZM\Annotation\Swoole
|
||||
*/
|
||||
class OnRequestEvent extends OnSwooleEventBase
|
||||
{
|
||||
}
|
||||
@@ -10,7 +10,7 @@ use ZM\Annotation\AnnotationBase;
|
||||
* Class OnWorkerStart
|
||||
* @package ZM\Annotation\Swoole
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
class OnStart extends AnnotationBase
|
||||
{
|
||||
|
||||
@@ -5,17 +5,14 @@ namespace ZM\Annotation\Swoole;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
use ZM\Annotation\Interfaces\Rule;
|
||||
|
||||
/**
|
||||
* Class OnSwooleEvent
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
* @Target("METHOD")
|
||||
* @package ZM\Annotation\Swoole
|
||||
*/
|
||||
class OnSwooleEvent extends AnnotationBase implements Rule, Level
|
||||
class OnSwooleEvent extends OnSwooleEventBase
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@@ -23,14 +20,6 @@ class OnSwooleEvent extends AnnotationBase implements Rule, Level
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/** @var string */
|
||||
public $rule = "";
|
||||
|
||||
/** @var int */
|
||||
public $level = 20;
|
||||
|
||||
public $callback = null;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -44,33 +33,4 @@ class OnSwooleEvent extends AnnotationBase implements Rule, Level
|
||||
public function setType(string $type) {
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRule(): string {
|
||||
return $this->rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $rule
|
||||
*/
|
||||
public function setRule(string $rule) {
|
||||
$this->rule = $rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLevel(): int {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $level
|
||||
*/
|
||||
public function setLevel(int $level) {
|
||||
$this->level = $level;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
49
src/ZM/Annotation/Swoole/OnSwooleEventBase.php
Normal file
49
src/ZM/Annotation/Swoole/OnSwooleEventBase.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Swoole;
|
||||
|
||||
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
use ZM\Annotation\Interfaces\Rule;
|
||||
|
||||
abstract class OnSwooleEventBase extends AnnotationBase implements Level, Rule
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $rule = "";
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $level = 20;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRule(): string {
|
||||
return $this->rule !== "" ? $this->rule : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $rule
|
||||
*/
|
||||
public function setRule(string $rule) {
|
||||
$this->rule = $rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLevel(): int {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $level
|
||||
*/
|
||||
public function setLevel(int $level) {
|
||||
$this->level = $level;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ class BuildCommand extends Command
|
||||
$target_dir = $input->getOption("target") ?? (__DIR__ . '/../../../resources/');
|
||||
if (mb_strpos($target_dir, "../")) $target_dir = realpath($target_dir);
|
||||
if ($target_dir === false) {
|
||||
$output->writeln(TermColor::color8(31) . "Error: No such file or directory (".__DIR__ . '/../../../resources/'.")" . TermColor::RESET);
|
||||
$output->writeln(TermColor::color8(31) . "Error: No such file or directory (" . __DIR__ . '/../../../resources/' . ")" . TermColor::RESET);
|
||||
return Command::FAILURE;
|
||||
}
|
||||
$output->writeln("Target: " . $target_dir . " , Version: " . ($version = json_decode(file_get_contents(__DIR__ . "/../../../composer.json"), true)["version"]));
|
||||
@@ -51,7 +51,7 @@ class BuildCommand extends Command
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function build ($target_dir, $filename) {
|
||||
private function build($target_dir, $filename) {
|
||||
@unlink($target_dir . $filename);
|
||||
$phar = new Phar($target_dir . $filename);
|
||||
$phar->startBuffering();
|
||||
|
||||
31
src/ZM/Command/DaemonCommand.php
Normal file
31
src/ZM/Command/DaemonCommand.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command;
|
||||
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Utils\DataProvider;
|
||||
|
||||
abstract class DaemonCommand extends Command
|
||||
{
|
||||
protected $daemon_file = null;
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$pid_path = DataProvider::getWorkingDir() . "/.daemon_pid";
|
||||
if (!file_exists($pid_path)) {
|
||||
$output->writeln("<comment>没有检测到正在运行的守护进程!</comment>");
|
||||
die();
|
||||
}
|
||||
$file = json_decode(file_get_contents($pid_path), true);
|
||||
if ($file === null || posix_getsid(intval($file["pid"])) === false) {
|
||||
$output->writeln("<comment>未检测到正在运行的守护进程!</comment>");
|
||||
unlink($pid_path);
|
||||
die();
|
||||
}
|
||||
$this->daemon_file = $file;
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
24
src/ZM/Command/DaemonReloadCommand.php
Normal file
24
src/ZM/Command/DaemonReloadCommand.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DaemonReloadCommand extends DaemonCommand
|
||||
{
|
||||
protected static $defaultName = 'daemon:reload';
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("重载守护进程下的用户代码(仅限--daemon模式可用)");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
parent::execute($input, $output);
|
||||
system("kill -USR1 " . intval($this->daemon_file["pid"]));
|
||||
$output->writeln("<info>成功重载!</info>");
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
30
src/ZM/Command/DaemonStatusCommand.php
Normal file
30
src/ZM/Command/DaemonStatusCommand.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DaemonStatusCommand extends DaemonCommand
|
||||
{
|
||||
protected static $defaultName = 'daemon:status';
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("查看守护进程框架的运行状态(仅限--daemon模式可用)");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
parent::execute($input, $output);
|
||||
$output->writeln("<info>框架运行中,pid:" . $this->daemon_file["pid"] . "</info>");
|
||||
$output->writeln("<comment>----- 以下是stdout内容 -----</comment>");
|
||||
$stdout = file_get_contents($this->daemon_file["stdout"]);
|
||||
$stdout = explode("\n", $stdout);
|
||||
for ($i = 10; $i > 0; --$i) {
|
||||
if (isset($stdout[count($stdout) - $i]))
|
||||
echo $stdout[count($stdout) - $i] . PHP_EOL;
|
||||
}
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
26
src/ZM/Command/DaemonStopCommand.php
Normal file
26
src/ZM/Command/DaemonStopCommand.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Utils\DataProvider;
|
||||
|
||||
class DaemonStopCommand extends DaemonCommand
|
||||
{
|
||||
protected static $defaultName = 'daemon:stop';
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("停止守护进程下运行的框架(仅限--daemon模式可用)");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
parent::execute($input, $output);
|
||||
system("kill -TERM " . intval($this->daemon_file["pid"]));
|
||||
unlink(DataProvider::getWorkingDir() . "/.daemon_pid");
|
||||
$output->writeln("<info>成功停止!</info>");
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,7 @@ class InitCommand extends Command
|
||||
echo("Error occurred. Please check your updates.\n");
|
||||
return Command::FAILURE;
|
||||
}
|
||||
$output->writeln("<info>Done!</info>");
|
||||
return Command::SUCCESS;
|
||||
} elseif (LOAD_MODE === 2) { //从phar启动的框架包,初始化的模式
|
||||
$phar_link = new Phar(__DIR__);
|
||||
|
||||
@@ -36,8 +36,8 @@ class PureHttpCommand extends Command
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$tty_width = explode(" ", trim(exec("stty size")))[1];
|
||||
if(realpath($input->getArgument('dir') ?? '.') === false) {
|
||||
$output->writeln("<error>Directory error(".($input->getArgument('dir') ?? '.')."): no such file or directory.</error>");
|
||||
if (realpath($input->getArgument('dir') ?? '.') === false) {
|
||||
$output->writeln("<error>Directory error(" . ($input->getArgument('dir') ?? '.') . "): no such file or directory.</error>");
|
||||
return self::FAILURE;
|
||||
}
|
||||
$global = ZMConfig::get("global");
|
||||
@@ -60,15 +60,15 @@ class PureHttpCommand extends Command
|
||||
"document_root" => realpath($input->getArgument('dir') ?? '.'),
|
||||
"document_index" => $index
|
||||
]);
|
||||
echo "\r".Coroutine::stats()["coroutine_peak_num"];
|
||||
echo "\r" . Coroutine::stats()["coroutine_peak_num"];
|
||||
});
|
||||
$server->on("start", function ($server) {
|
||||
Process::signal(SIGINT, function () use ($server) {
|
||||
Console::warning("Server interrupted by keyboard.");
|
||||
for ($i = 0; $i < 32; ++$i) {
|
||||
$num = ZMAtomic::$atomics["request"][$i]->get();
|
||||
if($num != 0)
|
||||
echo "[$i]: ".$num."\n";
|
||||
if ($num != 0)
|
||||
echo "[$i]: " . $num . "\n";
|
||||
}
|
||||
$server->shutdown();
|
||||
$server->stop();
|
||||
|
||||
@@ -11,7 +11,6 @@ use ZM\Framework;
|
||||
|
||||
class RunServerCommand extends Command
|
||||
{
|
||||
// the name of the command (the part after "bin/console")
|
||||
protected static $defaultName = 'server';
|
||||
|
||||
protected function configure() {
|
||||
@@ -31,27 +30,17 @@ class RunServerCommand extends Command
|
||||
]);
|
||||
$this->setDescription("Run zhamao-framework | 启动框架");
|
||||
$this->setHelp("直接运行可以启动");
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
if(($opt = $input->getOption("env")) !== null) {
|
||||
if(!in_array($opt, ["production", "staging", "development", ""])) {
|
||||
if (($opt = $input->getOption("env")) !== null) {
|
||||
if (!in_array($opt, ["production", "staging", "development", ""])) {
|
||||
$output->writeln("<error> \"--env\" option only accept production, development, staging and [empty] ! </error>");
|
||||
return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
// ... put here the code to run in your command
|
||||
// this method must return an integer number with the "exit status code"
|
||||
// of the command. You can also use these constants to make code more readable
|
||||
if (LOAD_MODE == 0) echo "* This is repository mode.\n";
|
||||
(new Framework($input->getOptions()))->start();
|
||||
// return this if there was no problem running the command
|
||||
// (it's equivalent to returning int(0))
|
||||
return Command::SUCCESS;
|
||||
|
||||
// or return this if some error happened during the execution
|
||||
// (it's equivalent to returning int(1))
|
||||
// return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ namespace ZM;
|
||||
|
||||
|
||||
use Exception;
|
||||
use ZM\Command\DaemonReloadCommand;
|
||||
use ZM\Command\DaemonStatusCommand;
|
||||
use ZM\Command\DaemonStopCommand;
|
||||
use ZM\Command\InitCommand;
|
||||
use ZM\Command\PureHttpCommand;
|
||||
use ZM\Command\RunServerCommand;
|
||||
@@ -40,7 +43,6 @@ class ConsoleApplication extends Application
|
||||
* @noinspection RedundantSuppression
|
||||
*/
|
||||
require_once WORKING_DIR . "/vendor/autoload.php";
|
||||
echo "* This is repository mode.\n";
|
||||
$composer = json_decode(file_get_contents(DataProvider::getWorkingDir() . "/composer.json"), true);
|
||||
if (!isset($composer["autoload"]["psr-4"]["Module\\"])) {
|
||||
echo "框架源码模式需要在autoload文件中添加Module目录为自动加载,是否添加?[Y/n] ";
|
||||
@@ -64,6 +66,9 @@ class ConsoleApplication extends Application
|
||||
}
|
||||
|
||||
$this->addCommands([
|
||||
new DaemonStatusCommand(),
|
||||
new DaemonReloadCommand(),
|
||||
new DaemonStopCommand(),
|
||||
new RunServerCommand(), //运行主服务的指令控制器
|
||||
new InitCommand(), //初始化用的,用于项目初始化和phar初始化
|
||||
new PureHttpCommand() //纯HTTP服务器指令
|
||||
@@ -75,6 +80,7 @@ class ConsoleApplication extends Application
|
||||
if (!($obj instanceof Command)) throw new TypeError("Command register class must be extended by Symfony\\Component\\Console\\Command\\Command");
|
||||
$this->add($obj);
|
||||
}*/
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -145,7 +145,7 @@ class Context implements ContextInterface
|
||||
if ($prompt != "") $this->reply($prompt);
|
||||
|
||||
try {
|
||||
$r = CoMessage::yieldByWS($this->getData(), ["user_id", "self_id", "message_type", onebot_target_id_name($this->getMessageType())]);
|
||||
$r = CoMessage::yieldByWS($this->getData(), ["user_id", "self_id", "message_type", onebot_target_id_name($this->getMessageType())], $timeout);
|
||||
} catch (Exception $e) {
|
||||
$r = false;
|
||||
}
|
||||
@@ -242,6 +242,14 @@ class Context implements ContextInterface
|
||||
*/
|
||||
public function getFullArg($prompt_msg = "") { return $this->getArgs(ZM_MATCH_ALL, $prompt_msg); }
|
||||
|
||||
/**
|
||||
* @param string $prompt_msg
|
||||
* @return int|mixed|string
|
||||
* @throws InvalidArgumentException
|
||||
* @throws WaitTimeoutException
|
||||
*/
|
||||
public function getNumArg($prompt_msg = "") { return $this->getArgs(ZM_MATCH_NUMBER, $prompt_msg); }
|
||||
|
||||
public function cloneFromParent() {
|
||||
set_coroutine_params(self::$context[Co::getPcid()] ?? self::$context[$this->cid]);
|
||||
return context();
|
||||
|
||||
@@ -117,6 +117,8 @@ interface ContextInterface
|
||||
|
||||
public function cloneFromParent();
|
||||
|
||||
public function getNumArg($prompt_msg = "");
|
||||
|
||||
public function copy();
|
||||
|
||||
public function getOption();
|
||||
|
||||
@@ -31,7 +31,6 @@ class DB
|
||||
|
||||
/**
|
||||
* @param $table_name
|
||||
* @param bool $enable_cache
|
||||
* @return Table
|
||||
* @throws DbException
|
||||
*/
|
||||
@@ -39,7 +38,7 @@ class DB
|
||||
if (Table::getTableInstance($table_name) === null) {
|
||||
if (in_array($table_name, self::$table_list))
|
||||
return new Table($table_name);
|
||||
elseif(SqlPoolStorage::$sql_pool !== null){
|
||||
elseif (SqlPoolStorage::$sql_pool !== null) {
|
||||
throw new DbException("Table " . $table_name . " not exist in database.");
|
||||
} else {
|
||||
throw new DbException("Database connection not exist or connect failed. Please check sql configuration");
|
||||
@@ -85,7 +84,8 @@ class DB
|
||||
* @throws DbException
|
||||
*/
|
||||
public static function rawQuery(string $line, $params = [], $fetch_mode = ZM_DEFAULT_FETCH_MODE) {
|
||||
Console::debug("MySQL: ".$line." | ". implode(", ", array($params)));
|
||||
if (!is_array($params)) $params = [$params];
|
||||
Console::debug("MySQL: " . $line . " | " . implode(", ", $params));
|
||||
try {
|
||||
$conn = SqlPoolStorage::$sql_pool->get();
|
||||
if ($conn === false) {
|
||||
@@ -95,6 +95,7 @@ class DB
|
||||
$ps = $conn->prepare($line);
|
||||
if ($ps === false) {
|
||||
SqlPoolStorage::$sql_pool->put(null);
|
||||
/** @noinspection PhpUndefinedFieldInspection */
|
||||
throw new DbException("SQL语句查询错误," . $line . ",错误信息:" . $conn->error);
|
||||
} else {
|
||||
if (!($ps instanceof PDOStatement) && !($ps instanceof PDOStatementProxy)) {
|
||||
@@ -115,7 +116,7 @@ class DB
|
||||
return $ps->fetchAll($fetch_mode);
|
||||
}
|
||||
} catch (DbException $e) {
|
||||
if(mb_strpos($e->getMessage(), "has gone away") !== false) {
|
||||
if (mb_strpos($e->getMessage(), "has gone away") !== false) {
|
||||
zm_sleep(0.2);
|
||||
Console::warning("Gone away of MySQL! retrying!");
|
||||
return self::rawQuery($line, $params);
|
||||
@@ -123,7 +124,7 @@ class DB
|
||||
Console::warning($e->getMessage());
|
||||
throw $e;
|
||||
} catch (PDOException $e) {
|
||||
if(mb_strpos($e->getMessage(), "has gone away") !== false) {
|
||||
if (mb_strpos($e->getMessage(), "has gone away") !== false) {
|
||||
zm_sleep(0.2);
|
||||
Console::warning("Gone away of MySQL! retrying!");
|
||||
return self::rawQuery($line, $params);
|
||||
|
||||
@@ -28,6 +28,6 @@ class InsertBody
|
||||
* @throws DbException
|
||||
*/
|
||||
public function save() {
|
||||
DB::rawQuery('INSERT INTO ' . $this->table->getTableName() . ' VALUES ('.implode(',', array_fill(0, count($this->row), '?')).')', $this->row);
|
||||
DB::rawQuery('INSERT INTO ' . $this->table->getTableName() . ' VALUES (' . implode(',', array_fill(0, count($this->row), '?')) . ')', $this->row);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
namespace ZM\DB;
|
||||
|
||||
|
||||
|
||||
class Table
|
||||
{
|
||||
private $table_name;
|
||||
@@ -28,7 +27,7 @@ class Table
|
||||
return new SelectBody($this, $what == [] ? ["*"] : $what);
|
||||
}
|
||||
|
||||
public function where($column, $operation_or_value, $value = null){
|
||||
public function where($column, $operation_or_value, $value = null) {
|
||||
return (new SelectBody($this, ["*"]))->where($column, $operation_or_value, $value);
|
||||
}
|
||||
|
||||
@@ -47,7 +46,7 @@ class Table
|
||||
return new DeleteBody($this);
|
||||
}
|
||||
|
||||
public function statement($line){
|
||||
public function statement() {
|
||||
$this->cache = [];
|
||||
//TODO: 无返回的statement语句
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ class UpdateBody
|
||||
* @var array
|
||||
*/
|
||||
private $set_value;
|
||||
|
||||
/**
|
||||
* UpdateBody constructor.
|
||||
* @param Table $table
|
||||
@@ -31,19 +32,19 @@ class UpdateBody
|
||||
/**
|
||||
* @throws DbException
|
||||
*/
|
||||
public function save(){
|
||||
public function save() {
|
||||
$arr = [];
|
||||
$msg = [];
|
||||
foreach($this->set_value as $k => $v) {
|
||||
$msg []= $k .' = ?';
|
||||
$arr[]=$v;
|
||||
foreach ($this->set_value as $k => $v) {
|
||||
$msg [] = $k . ' = ?';
|
||||
$arr[] = $v;
|
||||
}
|
||||
if(($msg ?? []) == []) throw new DbException('update value sets can not be empty!');
|
||||
$line = 'UPDATE '.$this->table->getTableName().' SET '.implode(', ', $msg);
|
||||
if($this->where_thing != []) {
|
||||
if (($msg ?? []) == []) throw new DbException('update value sets can not be empty!');
|
||||
$line = 'UPDATE ' . $this->table->getTableName() . ' SET ' . implode(', ', $msg);
|
||||
if ($this->where_thing != []) {
|
||||
list($sql, $param) = $this->getWhereSQL();
|
||||
$arr = array_merge($arr, $param);
|
||||
$line .= ' WHERE '.$sql;
|
||||
$line .= ' WHERE ' . $sql;
|
||||
}
|
||||
return DB::rawQuery($line, $arr);
|
||||
}
|
||||
|
||||
@@ -15,17 +15,17 @@ trait WhereBody
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getWhereSQL(){
|
||||
protected function getWhereSQL() {
|
||||
$param = [];
|
||||
$msg = '';
|
||||
foreach($this->where_thing as $k => $v) {
|
||||
foreach($v as $ks => $vs) {
|
||||
if($param != []) {
|
||||
$msg .= ' AND '.$ks ." $k ?";
|
||||
foreach ($this->where_thing as $k => $v) {
|
||||
foreach ($v as $ks => $vs) {
|
||||
if ($param != []) {
|
||||
$msg .= ' AND ' . $ks . " $k ?";
|
||||
} else {
|
||||
$msg .= "$ks $k ?";
|
||||
}
|
||||
$param []=$vs;
|
||||
$param [] = $vs;
|
||||
}
|
||||
}
|
||||
if ($msg == '') $msg = 1;
|
||||
|
||||
@@ -5,8 +5,8 @@ namespace ZM\Event;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use Error;
|
||||
use Exception;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Exception\InterruptException;
|
||||
use ZM\Exception\ZMException;
|
||||
@@ -17,6 +17,12 @@ use ZM\Utils\ZMUtil;
|
||||
|
||||
class EventDispatcher
|
||||
{
|
||||
const STATUS_NORMAL = 0; //正常结束
|
||||
const STATUS_INTERRUPTED = 1; //被interrupt了,不管在什么地方
|
||||
const STATUS_EXCEPTION = 2; //执行过程中抛出了异常
|
||||
const STATUS_BEFORE_FAILED = 3; //中间件HandleBefore返回了false,所以不执行此方法
|
||||
const STATUS_RULE_FAILED = 4; //判断事件执行的规则函数判定为false,所以不执行此方法
|
||||
|
||||
/** @var string */
|
||||
private $class;
|
||||
/** @var null|callable */
|
||||
@@ -27,6 +33,10 @@ class EventDispatcher
|
||||
private $log = false;
|
||||
/** @var int */
|
||||
private $eid = 0;
|
||||
/** @var int */
|
||||
public $status = self::STATUS_NORMAL;
|
||||
/** @var mixed */
|
||||
public $store = null;
|
||||
|
||||
/**
|
||||
* @param null $return_var
|
||||
@@ -74,41 +84,49 @@ class EventDispatcher
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed ...$params
|
||||
* @throws Exception
|
||||
*/
|
||||
public function dispatchEvents(...$params) {
|
||||
try {
|
||||
foreach ((EventManager::$events[$this->class] ?? []) as $v) {
|
||||
$result = $this->dispatchEvent($v, $this->rule, ...$params);
|
||||
$this->dispatchEvent($v, $this->rule, ...$params);
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . $v->method . " 分发结束。");
|
||||
if ($result !== false && is_callable($this->return_func)) {
|
||||
if ($this->status == self::STATUS_BEFORE_FAILED || $this->status == self::STATUS_RULE_FAILED) continue;
|
||||
if (is_callable($this->return_func) && $this->status === self::STATUS_NORMAL) {
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . $v->method . " 正在执行返回值处理函数 ...");
|
||||
($this->return_func)($result);
|
||||
($this->return_func)($this->store);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
if ($this->status === self::STATUS_RULE_FAILED) $this->status = self::STATUS_NORMAL;
|
||||
} catch (InterruptException $e) {
|
||||
return $e->return_var;
|
||||
} catch (AnnotationException $e) {
|
||||
return false;
|
||||
$this->store = $e->return_var;
|
||||
$this->status = self::STATUS_INTERRUPTED;
|
||||
} catch (Exception | Error $e) {
|
||||
$this->status = self::STATUS_EXCEPTION;
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AnnotationBase|null $v
|
||||
* @param mixed $v
|
||||
* @param null $rule_func
|
||||
* @param mixed ...$params
|
||||
* @throws AnnotationException
|
||||
* @return bool
|
||||
* @throws InterruptException
|
||||
* @return mixed
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function dispatchEvent(?AnnotationBase $v, $rule_func = null, ...$params) {
|
||||
public function dispatchEvent($v, $rule_func = null, ...$params) {
|
||||
$q_c = $v->class;
|
||||
$q_f = $v->method;
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在判断 " . $q_c . "::" . $q_f . " 方法下的 rule ...");
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在判断 " . $q_c . "::" . $q_f . " 方法下的 ruleFunc ...");
|
||||
if ($rule_func !== null && !$rule_func($v)) {
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 rule 判断为 false, 拒绝执行此方法。");
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 ruleFunc 判断为 false, 拒绝执行此方法。");
|
||||
$this->status = self::STATUS_RULE_FAILED;
|
||||
return false;
|
||||
}
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 rule 为真,继续执行方法本身 ...");
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 ruleFunc 为真,继续执行方法本身 ...");
|
||||
if (isset(EventManager::$middleware_map[$q_c][$q_f])) {
|
||||
$middlewares = EventManager::$middleware_map[$q_c][$q_f];
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法还绑定了 Middleware:" . implode(", ", $middlewares));
|
||||
@@ -138,7 +156,7 @@ class EventDispatcher
|
||||
try {
|
||||
$q_o = ZMUtil::getModInstance($q_c);
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在执行方法 " . $q_c . "::" . $q_f . " ...");
|
||||
$result = $q_o->$q_f(...$params);
|
||||
$this->store = $q_o->$q_f(...$params);
|
||||
} catch (Exception $e) {
|
||||
if ($e instanceof InterruptException) {
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 检测到事件阻断调用,正在跳出事件分发器 ...");
|
||||
@@ -166,13 +184,17 @@ class EventDispatcher
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 后置事件执行完毕!");
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
$this->status = self::STATUS_NORMAL;
|
||||
return true;
|
||||
}
|
||||
$this->status = self::STATUS_BEFORE_FAILED;
|
||||
return false;
|
||||
} else {
|
||||
$q_o = ZMUtil::getModInstance($q_c);
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在执行方法 " . $q_c . "::" . $q_f . " ...");
|
||||
return $q_o->$q_f(...$params);
|
||||
$this->store = $q_o->$q_f(...$params);
|
||||
$this->status = self::STATUS_NORMAL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,11 @@ use Exception;
|
||||
use Swoole\Timer;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\AnnotationParser;
|
||||
use ZM\Annotation\Swoole\OnSave;
|
||||
use ZM\Annotation\Swoole\OnTick;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Store\LightCache;
|
||||
use ZM\Store\ZMAtomic;
|
||||
|
||||
class EventManager
|
||||
@@ -22,6 +25,7 @@ class EventManager
|
||||
|
||||
public static function addEvent($event_name, ?AnnotationBase $event_obj) {
|
||||
self::$events[$event_name][] = $event_obj;
|
||||
(new AnnotationParser())->sortByLevel(self::$events, $event_name);
|
||||
}
|
||||
|
||||
public static function loadEventByParser(AnnotationParser $parser) {
|
||||
@@ -37,7 +41,7 @@ class EventManager
|
||||
public static function registerTimerTick() {
|
||||
$dispatcher = new EventDispatcher(OnTick::class);
|
||||
foreach (self::$events[OnTick::class] ?? [] as $vss) {
|
||||
if (server()->worker_id !== $vss->worker_id) return;
|
||||
if (server()->worker_id !== $vss->worker_id && $vss->worker_id != -1) return;
|
||||
//echo server()->worker_id.PHP_EOL;
|
||||
$plain_class = $vss->class;
|
||||
Console::debug("Added Middleware-based timer: " . $plain_class . " -> " . $vss->method);
|
||||
@@ -58,5 +62,11 @@ class EventManager
|
||||
}
|
||||
});
|
||||
}
|
||||
$conf = ZMConfig::get("global", "worker_cache") ?? ["worker" => 0];
|
||||
if (server()->worker_id == $conf["worker"]) {
|
||||
zm_timer_tick(ZMConfig::get("global", "light_cache")["auto_save_interval"] * 1000, function () {
|
||||
LightCache::savePersistence();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php /** @noinspection PhpComposerExtensionStubsInspection */
|
||||
<?php /** @noinspection PhpUnreachableStatementInspection */
|
||||
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
|
||||
|
||||
namespace ZM\Event;
|
||||
@@ -9,13 +11,18 @@ use Error;
|
||||
use Exception;
|
||||
use PDO;
|
||||
use ReflectionException;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Database\PDOConfig;
|
||||
use Swoole\Database\PDOPool;
|
||||
use Swoole\Event;
|
||||
use Swoole\Process;
|
||||
use Swoole\Timer;
|
||||
use ZM\Annotation\AnnotationParser;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Annotation\Swoole\OnCloseEvent;
|
||||
use ZM\Annotation\Swoole\OnMessageEvent;
|
||||
use ZM\Annotation\Swoole\OnOpenEvent;
|
||||
use ZM\Annotation\Swoole\OnPipeMessageEvent;
|
||||
use ZM\Annotation\Swoole\OnRequestEvent;
|
||||
use ZM\Annotation\Swoole\OnStart;
|
||||
use ZM\Annotation\Swoole\OnSwooleEvent;
|
||||
use ZM\Config\ZMConfig;
|
||||
@@ -30,12 +37,15 @@ use ZM\Context\Context;
|
||||
use ZM\Context\ContextInterface;
|
||||
use ZM\DB\DB;
|
||||
use ZM\Exception\DbException;
|
||||
use ZM\Exception\InterruptException;
|
||||
use ZM\Framework;
|
||||
use ZM\Http\Response;
|
||||
use ZM\Module\QQBot;
|
||||
use ZM\Store\LightCache;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\MySQL\SqlPoolStorage;
|
||||
use ZM\Store\Redis\ZMRedisPool;
|
||||
use ZM\Store\WorkerCache;
|
||||
use ZM\Store\ZMBuf;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\HttpUtil;
|
||||
@@ -53,7 +63,12 @@ class ServerEventHandler
|
||||
if ($terminal_id !== null) {
|
||||
ZMBuf::$terminal = $r = STDIN;
|
||||
Event::add($r, function () use ($r) {
|
||||
$var = trim(fgets($r));
|
||||
$fget = fgets($r);
|
||||
if ($fget === false) {
|
||||
Event::del($r);
|
||||
return;
|
||||
}
|
||||
$var = trim($fget);
|
||||
try {
|
||||
Terminal::executeCommand($var, $r);
|
||||
} catch (Exception $e) {
|
||||
@@ -65,11 +80,18 @@ class ServerEventHandler
|
||||
}
|
||||
Process::signal(SIGINT, function () use ($r) {
|
||||
echo "\r";
|
||||
Console::warning("Server interrupted by keyboard on Master.");
|
||||
Console::warning("Server interrupted(SIGINT) on Master.");
|
||||
if ((Framework::$server->inotify ?? null) !== null)
|
||||
/** @noinspection PhpUndefinedFieldInspection */ Event::del(Framework::$server->inotify);
|
||||
ZMUtil::stop();
|
||||
});
|
||||
if (Framework::$argv["daemon"]) {
|
||||
$daemon_data = json_encode([
|
||||
"pid" => \server()->master_pid,
|
||||
"stdout" => ZMConfig::get("global")["swoole"]["log_file"]
|
||||
], 128 | 256);
|
||||
file_put_contents(DataProvider::getWorkingDir() . "/.daemon_pid", $daemon_data);
|
||||
}
|
||||
if (Framework::$argv["watch"]) {
|
||||
if (extension_loaded('inotify')) {
|
||||
Console::warning("Enabled File watcher, do not use in production.");
|
||||
@@ -78,11 +100,11 @@ class ServerEventHandler
|
||||
$this->addWatcher(DataProvider::getWorkingDir() . "/src", $fd);
|
||||
Event::add($fd, function () use ($fd) {
|
||||
$r = inotify_read($fd);
|
||||
var_dump($r);
|
||||
dump($r);
|
||||
ZMUtil::reload();
|
||||
});
|
||||
} else {
|
||||
Console::warning("You have not loaded inotify extension.");
|
||||
Console::warning("You have not loaded \"inotify\" extension, please install first.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,6 +122,9 @@ class ServerEventHandler
|
||||
* @param $worker_id
|
||||
*/
|
||||
public function onWorkerStop(Server $server, $worker_id) {
|
||||
if ($worker_id == (ZMConfig::get("worker_cache")["worker"] ?? 0)) {
|
||||
LightCache::savePersistence();
|
||||
}
|
||||
Console::debug(($server->taskworker ? "Task" : "") . "Worker #$worker_id 已停止");
|
||||
}
|
||||
|
||||
@@ -114,7 +139,7 @@ class ServerEventHandler
|
||||
Console::debug("正在关闭 " . ($server->taskworker ? "Task" : "") . "Worker 进程 " . Console::setColor("#" . \server()->worker_id, "gold") . TermColor::frontColor256(59) . ", pid=" . posix_getpid());
|
||||
server()->stop($worker_id);
|
||||
});
|
||||
unset(Context::$context[Co::getCid()]);
|
||||
unset(Context::$context[Coroutine::getCid()]);
|
||||
if ($server->taskworker === false) {
|
||||
try {
|
||||
register_shutdown_function(function () use ($server) {
|
||||
@@ -194,7 +219,7 @@ class ServerEventHandler
|
||||
|
||||
// 开箱即用的Redis
|
||||
$redis = ZMConfig::get("global", "redis_config");
|
||||
if($redis !== null && $redis["host"] != "") {
|
||||
if ($redis !== null && $redis["host"] != "") {
|
||||
if (!extension_loaded("redis")) Console::error("Can not find redis extension.\n");
|
||||
else ZMRedisPool::init($redis);
|
||||
}
|
||||
@@ -211,7 +236,8 @@ class ServerEventHandler
|
||||
return server()->worker_id === $v->worker_id || $v->worker_id === -1;
|
||||
});
|
||||
$dispatcher->dispatchEvents($server, $worker_id);
|
||||
Console::debug("@OnStart 执行完毕");
|
||||
if ($dispatcher->status === EventDispatcher::STATUS_NORMAL) Console::debug("@OnStart 执行完毕");
|
||||
else Console::warning("@OnStart 执行异常!");
|
||||
} catch (Exception $e) {
|
||||
Console::error("Worker加载出错!停止服务!");
|
||||
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
|
||||
@@ -221,7 +247,7 @@ class ServerEventHandler
|
||||
Console::error("PHP Error: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
|
||||
Console::error("Maybe it caused by your own code if in your own Module directory.");
|
||||
Console::log($e->getTraceAsString(), 'gray');
|
||||
ZMUtil::stop();
|
||||
posix_kill($server->master_pid, SIGINT);
|
||||
}
|
||||
} else {
|
||||
// 这里是TaskWorker初始化的内容部分
|
||||
@@ -238,7 +264,7 @@ class ServerEventHandler
|
||||
Console::error("PHP Error: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
|
||||
Console::error("Maybe it caused by your own code if in your own Module directory.");
|
||||
Console::log($e->getTraceAsString(), 'gray');
|
||||
ZMUtil::stop();
|
||||
posix_kill($server->master_pid, SIGINT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -250,10 +276,17 @@ class ServerEventHandler
|
||||
*/
|
||||
public function onMessage($server, Frame $frame) {
|
||||
|
||||
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd.": ".TermColor::ITALIC.$frame->data.TermColor::RESET);
|
||||
unset(Context::$context[Co::getCid()]);
|
||||
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd . ": " . TermColor::ITALIC . $frame->data . TermColor::RESET);
|
||||
unset(Context::$context[Coroutine::getCid()]);
|
||||
$conn = ManagerGM::get($frame->fd);
|
||||
set_coroutine_params(["server" => $server, "frame" => $frame, "connection" => $conn]);
|
||||
$dispatcher1 = new EventDispatcher(OnMessageEvent::class);
|
||||
$dispatcher1->setRuleFunction(function ($v) {
|
||||
/** @noinspection PhpUnreachableStatementInspection */
|
||||
return ctx()->getConnection()->getName() == $v->connect_type && eval("return " . $v->getRule() . ";");
|
||||
});
|
||||
|
||||
|
||||
$dispatcher = new EventDispatcher(OnSwooleEvent::class);
|
||||
$dispatcher->setRuleFunction(function ($v) {
|
||||
if ($v->getRule() == '') {
|
||||
@@ -268,6 +301,7 @@ class ServerEventHandler
|
||||
});
|
||||
try {
|
||||
//$starttime = microtime(true);
|
||||
$dispatcher1->dispatchEvents($conn);
|
||||
$dispatcher->dispatchEvents($conn);
|
||||
//Console::success("Used ".round((microtime(true) - $starttime) * 1000, 3)." ms!");
|
||||
} catch (Exception $e) {
|
||||
@@ -287,12 +321,20 @@ class ServerEventHandler
|
||||
* @param $request
|
||||
* @param $response
|
||||
*/
|
||||
public function onRequest($request, $response) {
|
||||
public function onRequest(?Request $request, ?\Swoole\Http\Response $response) {
|
||||
$response = new Response($response);
|
||||
foreach (ZMConfig::get("global")["http_header"] as $k => $v) {
|
||||
$response->setHeader($k, $v);
|
||||
}
|
||||
unset(Context::$context[Co::getCid()]);
|
||||
Console::debug("Calling Swoole \"request\" event from fd=" . $request->fd);
|
||||
set_coroutine_params(["request" => $request, "response" => $response]);
|
||||
|
||||
$dis1 = new EventDispatcher(OnRequestEvent::class);
|
||||
$dis1->setRuleFunction(function ($v) {
|
||||
return eval("return " . $v->getRule() . ";") ? true : false;
|
||||
});
|
||||
|
||||
$dis = new EventDispatcher(OnSwooleEvent::class);
|
||||
$dis->setRuleFunction(function ($v) {
|
||||
if ($v->getRule() == '') {
|
||||
@@ -305,8 +347,9 @@ class ServerEventHandler
|
||||
});
|
||||
|
||||
try {
|
||||
$no_interrupt = $dis->dispatchEvents($request, $response);
|
||||
if ($no_interrupt !== null) {
|
||||
$dis1->dispatchEvents($request, $response);
|
||||
$dis->dispatchEvents($request, $response);
|
||||
if ($dis->status === EventDispatcher::STATUS_NORMAL && $dis1->status === EventDispatcher::STATUS_NORMAL) {
|
||||
$result = HttpUtil::parseUri($request, $response, $request->server["request_uri"], $node, $params);
|
||||
if ($result === true) {
|
||||
ctx()->setCache("params", $params);
|
||||
@@ -318,14 +361,16 @@ class ServerEventHandler
|
||||
$div->request_method = $node["request_method"];
|
||||
$div->class = $node["class"];
|
||||
//Console::success("正在执行路由:".$node["method"]);
|
||||
$r = $dispatcher->dispatchEvent($div, null, $params, $request, $response);
|
||||
if (is_string($r) && !$response->isEnd()) $response->end($r);
|
||||
$dispatcher->dispatchEvent($div, null, $params, $request, $response);
|
||||
if (is_string($dispatcher->store) && !$response->isEnd()) $response->end($dispatcher->store);
|
||||
}
|
||||
}
|
||||
if (!$response->isEnd()) {
|
||||
//Console::warning('返回了404');
|
||||
HttpUtil::responseCodePage($response, 404);
|
||||
}
|
||||
} catch (InterruptException $e) {
|
||||
// do nothing
|
||||
} catch (Exception $e) {
|
||||
$response->status(500);
|
||||
Console::info($request->server["remote_addr"] . ":" . $request->server["remote_port"] .
|
||||
@@ -337,7 +382,7 @@ class ServerEventHandler
|
||||
else
|
||||
$response->end("Internal server error.");
|
||||
}
|
||||
Console::error("Internal server exception (500), caused by " . get_class($e).": ".$e->getMessage());
|
||||
Console::error("Internal server exception (500), caused by " . get_class($e) . ": " . $e->getMessage());
|
||||
Console::log($e->getTraceAsString(), "gray");
|
||||
} catch (Error $e) {
|
||||
$response->status(500);
|
||||
@@ -351,7 +396,7 @@ class ServerEventHandler
|
||||
else
|
||||
$response->end("Internal server error.");
|
||||
}
|
||||
Console::error("Internal server error (500), caused by " . get_class($e).": ".$e->getMessage());
|
||||
Console::error("Internal server error (500), caused by " . get_class($e) . ": " . $e->getMessage());
|
||||
Console::log($e->getTraceAsString(), "gray");
|
||||
}
|
||||
}
|
||||
@@ -364,13 +409,26 @@ class ServerEventHandler
|
||||
public function onOpen($server, Request $request) {
|
||||
Console::debug("Calling Swoole \"open\" event from fd=" . $request->fd);
|
||||
unset(Context::$context[Co::getCid()]);
|
||||
$type = strtolower($request->get["type"] ?? $request->header["x-client-role"] ?? "");
|
||||
$type = strtolower($request->header["x-client-role"] ?? $request->get["type"] ?? "");
|
||||
$access_token = explode(" ", $request->header["authorization"] ?? $request->get["token"] ?? "")[1] ?? "";
|
||||
if (($a = ZMConfig::get("global", "access_token")) != "") {
|
||||
if ($access_token !== $a) {
|
||||
$server->close($request->fd);
|
||||
Console::warning("Unauthorized access_token: " . $access_token);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$type_conn = ManagerGM::getTypeClassName($type);
|
||||
ManagerGM::pushConnect($request->fd, $type_conn);
|
||||
$conn = ManagerGM::get($request->fd);
|
||||
set_coroutine_params(["server" => $server, "request" => $request, "connection" => $conn, "fd" => $request->fd]);
|
||||
$conn->setOption("connect_id", strval($request->header["x-self-id"] ?? ""));
|
||||
|
||||
$dispatcher1 = new EventDispatcher(OnOpenEvent::class);
|
||||
$dispatcher1->setRuleFunction(function ($v) {
|
||||
return ctx()->getConnection()->getName() == $v->connect_type && eval("return " . $v->getRule() . ";");
|
||||
});
|
||||
|
||||
$dispatcher = new EventDispatcher(OnSwooleEvent::class);
|
||||
$dispatcher->setRuleFunction(function ($v) {
|
||||
if ($v->getRule() == '') {
|
||||
@@ -387,6 +445,7 @@ class ServerEventHandler
|
||||
LightCacheInside::set("connect", "conn_fd", $request->fd);
|
||||
}
|
||||
}
|
||||
$dispatcher1->dispatchEvents($conn);
|
||||
$dispatcher->dispatchEvents($conn);
|
||||
} catch (Exception $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
@@ -412,6 +471,10 @@ class ServerEventHandler
|
||||
Console::debug("Calling Swoole \"close\" event from fd=" . $fd);
|
||||
set_coroutine_params(["server" => $server, "connection" => $conn, "fd" => $fd]);
|
||||
|
||||
$dispatcher1 = new EventDispatcher(OnCloseEvent::class);
|
||||
$dispatcher1->setRuleFunction(function ($v) {
|
||||
return $v->connect_type == ctx()->getConnection()->getName() && eval("return " . $v->getRule() . ";");
|
||||
});
|
||||
|
||||
$dispatcher = new EventDispatcher(OnSwooleEvent::class);
|
||||
$dispatcher->setRuleFunction(function ($v) {
|
||||
@@ -429,6 +492,7 @@ class ServerEventHandler
|
||||
LightCacheInside::set("connect", "conn_fd", -1);
|
||||
}
|
||||
}
|
||||
$dispatcher1->dispatchEvents($conn);
|
||||
$dispatcher->dispatchEvents($conn);
|
||||
} catch (Exception $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
@@ -444,9 +508,10 @@ class ServerEventHandler
|
||||
|
||||
/**
|
||||
* @SwooleHandler("pipeMessage")
|
||||
* @param $server
|
||||
* @param Server $server
|
||||
* @param $src_worker_id
|
||||
* @param $data
|
||||
* @throws Exception
|
||||
*/
|
||||
public function onPipeMessage(Server $server, $src_worker_id, $data) {
|
||||
//var_dump($data, $server->worker_id);
|
||||
@@ -457,28 +522,80 @@ class ServerEventHandler
|
||||
$obj = $data["data"];
|
||||
Co::resume($obj["coroutine"]);
|
||||
break;
|
||||
case "stop":
|
||||
Console::verbose('正在清理 #' . $server->worker_id . ' 的计时器');
|
||||
Timer::clearAll();
|
||||
case "getWorkerCache":
|
||||
$r = WorkerCache::get($data["key"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "terminate":
|
||||
$server->stop();
|
||||
case "setWorkerCache":
|
||||
$r = WorkerCache::set($data["key"], $data["value"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case 'echo':
|
||||
Console::success('接收到来自 #' . $src_worker_id . ' 的消息');
|
||||
case "unsetWorkerCache":
|
||||
$r = WorkerCache::unset($data["key"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case 'send':
|
||||
$server->sendMessage(json_encode(["action" => "echo"]), $data["target"]);
|
||||
case "hasKeyWorkerCache":
|
||||
$r = WorkerCache::hasKey($data["key"], $data["subkey"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "asyncAddWorkerCache":
|
||||
WorkerCache::add($data["key"], $data["value"], true);
|
||||
break;
|
||||
case "asyncSubWorkerCache":
|
||||
WorkerCache::sub($data["key"], $data["value"], true);
|
||||
break;
|
||||
case "asyncSetWorkerCache":
|
||||
WorkerCache::set($data["key"], $data["value"], true);
|
||||
break;
|
||||
case "asyncUnsetWorkerCache":
|
||||
WorkerCache::unset($data["key"], true);
|
||||
break;
|
||||
case "addWorkerCache":
|
||||
$r = WorkerCache::add($data["key"], $data["value"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "subWorkerCache":
|
||||
$r = WorkerCache::sub($data["key"], $data["value"]);
|
||||
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
|
||||
$server->sendMessage(json_encode($action, 256), $src_worker_id);
|
||||
break;
|
||||
case "returnWorkerCache":
|
||||
WorkerCache::$transfer[$data["cid"]] = $data["value"];
|
||||
zm_resume($data["cid"]);
|
||||
break;
|
||||
default:
|
||||
echo $data . PHP_EOL;
|
||||
$dispatcher = new EventDispatcher(OnPipeMessageEvent::class);
|
||||
$dispatcher->setRuleFunction(function (OnPipeMessageEvent $v) use ($data) {
|
||||
return $v->action == $data["action"];
|
||||
});
|
||||
$dispatcher->dispatchEvents($data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @SwooleHandler("task")
|
||||
* @param Server|null $server
|
||||
* @param Server\Task $task
|
||||
* @return mixed
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function onTask() {
|
||||
public function onTask(?Server $server, Server\Task $task) {
|
||||
$data = $task->data;
|
||||
switch ($data["action"]) {
|
||||
case "runMethod":
|
||||
$c = $data["class"];
|
||||
$ss = new $c();
|
||||
$method = $data["method"];
|
||||
$ps = $data["params"];
|
||||
$task->finish($ss->$method(...$ps));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -505,7 +622,16 @@ class ServerEventHandler
|
||||
//加载各个模块的注解类,以及反射
|
||||
Console::debug("检索Module中");
|
||||
$parser = new AnnotationParser();
|
||||
$parser->addRegisterPath(DataProvider::getWorkingDir() . "/src/Module/", "Module");
|
||||
$path = DataProvider::getWorkingDir() . "/src/";
|
||||
$dir = scandir($path);
|
||||
unset($dir[0], $dir[1]);
|
||||
$composer = json_decode(file_get_contents(DataProvider::getWorkingDir() . "/composer.json"), true);
|
||||
foreach ($dir as $v) {
|
||||
if (is_dir($path . "/" . $v) && isset($composer["autoload"]["psr-4"][$v . "\\"]) && !in_array($composer["autoload"]["psr-4"][$v . "\\"], $composer["extra"]["exclude_annotate"] ?? [])) {
|
||||
Console::verbose("Add " . $v . " to register path");
|
||||
$parser->addRegisterPath(DataProvider::getWorkingDir() . "/src/" . $v . "/", $v);
|
||||
}
|
||||
}
|
||||
$parser->registerMods();
|
||||
EventManager::loadEventByParser($parser); //加载事件
|
||||
|
||||
@@ -518,14 +644,14 @@ class ServerEventHandler
|
||||
|
||||
//加载插件
|
||||
$plugins = ZMConfig::get("global", "modules") ?? [];
|
||||
if (!isset($plugins["onebot"])) $plugins["onebot"] = ["status" => true, "single_bot_mode" => false];
|
||||
if (!isset($plugins["onebot"])) $plugins["onebot"] = ["status" => true, "single_bot_mode" => false, "message_level" => 99999];
|
||||
|
||||
if ($plugins["onebot"]) {
|
||||
$obj = new OnSwooleEvent();
|
||||
$obj->class = QQBot::class;
|
||||
$obj->method = 'handle';
|
||||
$obj->type = 'message';
|
||||
$obj->level = 99999;
|
||||
$obj->level = $plugins["onebot"]["message_level"] ?? 99999;
|
||||
$obj->rule = 'connectIsQQ()';
|
||||
EventManager::addEvent(OnSwooleEvent::class, $obj);
|
||||
if ($plugins["onebot"]["single_bot_mode"]) {
|
||||
@@ -536,7 +662,6 @@ class ServerEventHandler
|
||||
}
|
||||
|
||||
//TODO: 编写加载外部插件的方式
|
||||
$this->loadExternalModules($plugins);
|
||||
}
|
||||
|
||||
private function addWatcher($maindir, $fd) {
|
||||
@@ -550,11 +675,4 @@ class ServerEventHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function loadExternalModules($plugins) {
|
||||
foreach ($plugins as $k => $v) {
|
||||
if ($k == "onebot") continue;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,9 +55,9 @@ class Framework
|
||||
ZMAtomic::init();
|
||||
try {
|
||||
$sw = ZMConfig::get("global");
|
||||
if(!is_dir($sw["zm_data"])) mkdir($sw["zm_data"]);
|
||||
if(!is_dir($sw["config_dir"])) mkdir($sw["config_dir"]);
|
||||
if(!is_dir($sw["crash_dir"])) mkdir($sw["crash_dir"]);
|
||||
if (!is_dir($sw["zm_data"])) mkdir($sw["zm_data"]);
|
||||
if (!is_dir($sw["config_dir"])) mkdir($sw["config_dir"]);
|
||||
if (!is_dir($sw["crash_dir"])) mkdir($sw["crash_dir"]);
|
||||
ManagerGM::init(ZMConfig::get("global", "swoole")["max_connection"] ?? 2048, 0.5, [
|
||||
[
|
||||
"key" => "connect_id",
|
||||
@@ -94,6 +94,7 @@ class Framework
|
||||
"version" => ZM_VERSION,
|
||||
"config" => $args["env"] === null ? 'global.php' : $args["env"]
|
||||
];
|
||||
if (APP_VERSION !== "unknown") $out["app_version"] = APP_VERSION;
|
||||
if (isset(ZMConfig::get("global", "swoole")["task_worker_num"])) {
|
||||
$out["task_worker_num"] = ZMConfig::get("global", "swoole")["task_worker_num"];
|
||||
}
|
||||
@@ -129,6 +130,40 @@ class Framework
|
||||
LightCache::init($r);
|
||||
LightCacheInside::init();
|
||||
SpinLock::init($r["size"]);
|
||||
set_error_handler(function ($error_no, $error_msg, $error_file, $error_line) {
|
||||
switch ($error_no) {
|
||||
case E_WARNING:
|
||||
$level_tips = 'PHP Warning: ';
|
||||
break;
|
||||
case E_NOTICE:
|
||||
$level_tips = 'PHP Notice: ';
|
||||
break;
|
||||
case E_DEPRECATED:
|
||||
$level_tips = 'PHP Deprecated: ';
|
||||
break;
|
||||
case E_USER_ERROR:
|
||||
$level_tips = 'User Error: ';
|
||||
break;
|
||||
case E_USER_WARNING:
|
||||
$level_tips = 'User Warning: ';
|
||||
break;
|
||||
case E_USER_NOTICE:
|
||||
$level_tips = 'User Notice: ';
|
||||
break;
|
||||
case E_USER_DEPRECATED:
|
||||
$level_tips = 'User Deprecated: ';
|
||||
break;
|
||||
case E_STRICT:
|
||||
$level_tips = 'PHP Strict: ';
|
||||
break;
|
||||
default:
|
||||
$level_tips = 'Unkonw Type Error: ';
|
||||
break;
|
||||
} // do some handle
|
||||
$error = $level_tips . $error_msg . ' in ' . $error_file . ' on ' . $error_line;
|
||||
Console::warning($error); // 如果 return false 则错误会继续递交给 PHP 标准错误处理 /
|
||||
return true;
|
||||
}, E_ALL | E_STRICT);
|
||||
} catch (Exception $e) {
|
||||
Console::error("Framework初始化出现错误,请检查!");
|
||||
Console::error($e->getMessage());
|
||||
@@ -174,11 +209,9 @@ class Framework
|
||||
}
|
||||
}
|
||||
foreach ($event_list as $k => $v) {
|
||||
self::$server->on($k, function (...$param) use ($v) {
|
||||
$c = ZMUtil::getModInstance($v->class);
|
||||
$m = $v->method;
|
||||
$c->$m(...$param);
|
||||
});
|
||||
$c = ZMUtil::getModInstance($v->class);
|
||||
$m = $v->method;
|
||||
self::$server->on($k, function (...$param) use ($c, $m) { $c->$m(...$param); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,16 +223,7 @@ class Framework
|
||||
private function parseCliArgs($args) {
|
||||
$coroutine_mode = true;
|
||||
global $terminal_id;
|
||||
$terminal_id = call_user_func(function () {
|
||||
try {
|
||||
$data = random_bytes(16);
|
||||
} catch (Exception $e) {
|
||||
return "";
|
||||
}
|
||||
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
|
||||
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
|
||||
return strtoupper(vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)));
|
||||
});
|
||||
$terminal_id = uuidgen();
|
||||
foreach ($args as $x => $y) {
|
||||
switch ($x) {
|
||||
case 'disable-coroutine':
|
||||
@@ -217,11 +241,13 @@ class Framework
|
||||
case 'daemon':
|
||||
if ($y) {
|
||||
$this->server_set["daemonize"] = 1;
|
||||
Console::$theme = "no-color";
|
||||
Console::log("已启用守护进程,输出重定向到 " . $this->server_set["log_file"]);
|
||||
$terminal_id = null;
|
||||
}
|
||||
break;
|
||||
case 'disable-console-input':
|
||||
case 'no-interaction':
|
||||
if ($y) $terminal_id = null;
|
||||
break;
|
||||
case 'log-error':
|
||||
@@ -234,6 +260,7 @@ class Framework
|
||||
if ($y) Console::setLevel(2);
|
||||
break;
|
||||
case 'log-verbose':
|
||||
case 'verbose':
|
||||
if ($y) Console::setLevel(3);
|
||||
break;
|
||||
case 'log-debug':
|
||||
@@ -244,6 +271,10 @@ class Framework
|
||||
Console::$theme = $y;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//Console::info("Calculating ".$x);
|
||||
//dump($y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($coroutine_mode) Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL);
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
namespace ZM\Http;
|
||||
|
||||
|
||||
use ZM\Console\Console;
|
||||
|
||||
class Response
|
||||
{
|
||||
|
||||
@@ -94,7 +92,8 @@ class Response
|
||||
*/
|
||||
public function status($http_code, $reason = null) {
|
||||
$this->status_code = $http_code;
|
||||
return $this->response->status($http_code, $reason);
|
||||
if (!$this->is_end) return $this->response->status($http_code, $reason);
|
||||
else return false;
|
||||
}
|
||||
|
||||
public function getStatusCode() {
|
||||
@@ -107,7 +106,8 @@ class Response
|
||||
* @return mixed
|
||||
*/
|
||||
public function setStatusCode($http_code, $reason = null) {
|
||||
return $this->response->setStatusCode($http_code, $reason);
|
||||
if (!$this->is_end) return $this->response->setStatusCode($http_code, $reason);
|
||||
else return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,7 +117,8 @@ class Response
|
||||
* @return mixed
|
||||
*/
|
||||
public function header($key, $value, $ucwords = null) {
|
||||
return $this->response->header($key, $value, $ucwords);
|
||||
if (!$this->is_end) return $this->response->header($key, $value, $ucwords);
|
||||
else return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,7 +128,7 @@ class Response
|
||||
* @return mixed
|
||||
*/
|
||||
public function setHeader($key, $value, $ucwords = null) {
|
||||
return $this->response->setHeader($key, $value, $ucwords);
|
||||
return !$this->is_end ? $this->response->setHeader($key, $value, $ucwords) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,13 +160,17 @@ class Response
|
||||
* @return mixed
|
||||
*/
|
||||
public function end($content = null) {
|
||||
$this->is_end = true;
|
||||
return $this->response->end($content);
|
||||
if (!$this->is_end) {
|
||||
$this->is_end = true;
|
||||
return $this->response->end($content);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function isEnd() { return $this->is_end; }
|
||||
|
||||
public function endWithStatus($status_code = 200, $content = null){
|
||||
public function endWithStatus($status_code = 200, $content = null) {
|
||||
$this->status($status_code);
|
||||
$this->end($content);
|
||||
}
|
||||
|
||||
37
src/ZM/Http/RouteManager.php
Normal file
37
src/ZM/Http/RouteManager.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Http;
|
||||
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use ZM\Annotation\Http\Controller;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Console\Console;
|
||||
|
||||
class RouteManager
|
||||
{
|
||||
/** @var null|RouteCollection */
|
||||
public static $routes = null;
|
||||
|
||||
public static function importRouteByAnnotation(RequestMapping $vss, $method, $class, $methods_annotations) {
|
||||
if (self::$routes === null) self::$routes = new RouteCollection();
|
||||
|
||||
// 拿到所属方法的类上面有没有控制器的注解
|
||||
$prefix = '';
|
||||
foreach ($methods_annotations as $annotation) {
|
||||
if ($annotation instanceof Controller) {
|
||||
$prefix = $annotation->prefix;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$tail = trim($vss->route, "/");
|
||||
$route_name = $prefix . ($tail === "" ? "" : "/") . $tail;
|
||||
Console::debug("添加路由:" . $route_name);
|
||||
$route = new Route($route_name, ['_class' => $class, '_method' => $method]);
|
||||
$route->setMethods($vss->request_method);
|
||||
|
||||
self::$routes->add(md5($route_name), $route);
|
||||
}
|
||||
}
|
||||
@@ -13,14 +13,14 @@ class StaticFileHandler
|
||||
public function __construct($filename, $path) {
|
||||
$full_path = realpath($path . "/" . $filename);
|
||||
$response = ctx()->getResponse();
|
||||
Console::debug("Full path: ".$full_path);
|
||||
Console::debug("Full path: " . $full_path);
|
||||
if ($full_path !== false) {
|
||||
if (strpos($full_path, $path) !== 0) {
|
||||
$response->status(403);
|
||||
$response->end("403 Forbidden");
|
||||
return true;
|
||||
} else {
|
||||
if(is_file($full_path)) {
|
||||
if (is_file($full_path)) {
|
||||
$exp = strtolower(pathinfo($full_path)['extension'] ?? "unknown");
|
||||
$response->setHeader("Content-Type", ZMConfig::get("file_header")[$exp] ?? "application/octet-stream");
|
||||
$response->end(file_get_contents($full_path));
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace ZM\Module;
|
||||
|
||||
use Swoole\Coroutine;
|
||||
use Exception;
|
||||
use ZM\Annotation\CQ\CQAPIResponse;
|
||||
use ZM\Annotation\CQ\CQBefore;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
@@ -14,8 +14,6 @@ use ZM\Annotation\CQ\CQRequest;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Exception\InterruptException;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\Lock\SpinLock;
|
||||
use ZM\Utils\CoMessage;
|
||||
|
||||
/**
|
||||
@@ -26,47 +24,59 @@ class QQBot
|
||||
{
|
||||
/**
|
||||
* @throws InterruptException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function handle() {
|
||||
try {
|
||||
$data = json_decode(context()->getFrame()->data, true);
|
||||
set_coroutine_params(["data" => $data]);
|
||||
if (isset($data["post_type"])) {
|
||||
//echo TermColor::ITALIC.json_encode($data, 128|256).TermColor::RESET.PHP_EOL;
|
||||
set_coroutine_params(["data" => $data]);
|
||||
ctx()->setCache("level", 0);
|
||||
//Console::debug("Calling CQ Event from fd=" . ctx()->getConnection()->getFd());
|
||||
$this->dispatchBeforeEvents($data); // >= 200 的level before在这里执行
|
||||
if (CoMessage::resumeByWS()) {
|
||||
EventDispatcher::interrupt();
|
||||
if ($data["post_type"] != "meta_event") {
|
||||
$r = $this->dispatchBeforeEvents($data); // before在这里执行,元事件不执行before为减少不必要的调试日志
|
||||
if ($r->store === "block") EventDispatcher::interrupt();
|
||||
}
|
||||
//Console::warning("最上数据包:".json_encode($data));
|
||||
$this->dispatchEvents($data);
|
||||
} else {
|
||||
$this->dispatchAPIResponse($data);
|
||||
}
|
||||
if (isset($data["echo"]) || isset($data["post_type"])) {
|
||||
if (CoMessage::resumeByWS()) EventDispatcher::interrupt();
|
||||
}
|
||||
if (isset($data["post_type"])) $this->dispatchEvents($data);
|
||||
else $this->dispatchAPIResponse($data);
|
||||
} /** @noinspection PhpRedundantCatchClauseInspection */ catch (WaitTimeoutException $e) {
|
||||
$e->module->finalReply($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return EventDispatcher
|
||||
* @throws Exception
|
||||
*/
|
||||
public function dispatchBeforeEvents($data) {
|
||||
$before = new EventDispatcher(CQBefore::class);
|
||||
$before->setRuleFunction(function ($v) use ($data) {
|
||||
if ($v->level < 200) EventDispatcher::interrupt();
|
||||
elseif ($v->cq_event != $data["post_type"]) return false;
|
||||
return true;
|
||||
return $v->cq_event == $data["post_type"];
|
||||
});
|
||||
$before->setReturnFunction(function ($result) {
|
||||
if (!$result) EventDispatcher::interrupt();
|
||||
if (!$result) EventDispatcher::interrupt("block");
|
||||
});
|
||||
$before->dispatchEvents($data);
|
||||
return $before;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @throws InterruptException
|
||||
*/
|
||||
private function dispatchEvents($data) {
|
||||
//Console::warning("最xia数据包:".json_encode($data));
|
||||
switch ($data["post_type"]) {
|
||||
case "message":
|
||||
$word = explodeMsg(str_replace("\r", "", context()->getMessage()));
|
||||
if (empty($word)) $word = [""];
|
||||
if (count(explode("\n", $word[0])) >= 2) {
|
||||
$enter = explode("\n", context()->getMessage());
|
||||
$first = split_explode(" ", array_shift($enter));
|
||||
@@ -75,16 +85,15 @@ class QQBot
|
||||
$word[$k] = trim($word[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
//分发CQCommand事件
|
||||
$dispatcher = new EventDispatcher(CQCommand::class);
|
||||
$dispatcher->setRuleFunction(function (CQCommand $v) use ($word) {
|
||||
if(array_diff([$v->match, $v->pattern, $v->regex, $v->keyword, $v->end_with, $v->start_with], [""]) == []) return false;
|
||||
if (array_diff([$v->match, $v->pattern, $v->regex, $v->keyword, $v->end_with, $v->start_with], [""]) == []) return false;
|
||||
elseif (($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == ctx()->getUserId())) &&
|
||||
($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == (ctx()->getGroupId() ?? 0))) &&
|
||||
($v->message_type == '' || ($v->message_type != '' && $v->message_type == ctx()->getMessageType()))
|
||||
) {
|
||||
if(($word[0] != "" && $v->match == $word[0]) || in_array($word[0], $v->alias)) {
|
||||
if (($word[0] != "" && $v->match == $word[0]) || in_array($word[0], $v->alias)) {
|
||||
array_shift($word);
|
||||
ctx()->setCache("match", $word);
|
||||
return true;
|
||||
@@ -97,14 +106,14 @@ class QQBot
|
||||
} elseif ($v->keyword != "" && mb_strpos(ctx()->getMessage(), $v->keyword) !== false) {
|
||||
ctx()->setCache("match", explode($v->keyword, ctx()->getMessage()));
|
||||
return true;
|
||||
}elseif ($v->pattern != "") {
|
||||
} elseif ($v->pattern != "") {
|
||||
$match = matchArgs($v->pattern, ctx()->getMessage());
|
||||
if($match !== false) {
|
||||
if ($match !== false) {
|
||||
ctx()->setCache("match", $match);
|
||||
return true;
|
||||
}
|
||||
} elseif ($v->regex != "") {
|
||||
if(preg_match("/" . $v->regex . "/u", ctx()->getMessage(), $word2) != 0) {
|
||||
if (preg_match("/" . $v->regex . "/u", ctx()->getMessage(), $word2) != 0) {
|
||||
ctx()->setCache("match", $word2);
|
||||
return true;
|
||||
}
|
||||
@@ -114,10 +123,10 @@ class QQBot
|
||||
});
|
||||
$dispatcher->setReturnFunction(function ($result) {
|
||||
if (is_string($result)) ctx()->reply($result);
|
||||
EventDispatcher::interrupt();
|
||||
if (ctx()->getCache("has_reply") === true) EventDispatcher::interrupt();
|
||||
});
|
||||
$r = $dispatcher->dispatchEvents();
|
||||
if ($r === null) EventDispatcher::interrupt();
|
||||
$dispatcher->dispatchEvents();
|
||||
if ($dispatcher->status == EventDispatcher::STATUS_INTERRUPTED) EventDispatcher::interrupt();
|
||||
|
||||
//分发CQMessage事件
|
||||
$msg_dispatcher = new EventDispatcher(CQMessage::class);
|
||||
@@ -137,8 +146,7 @@ class QQBot
|
||||
//Console::success("当前数据包:".json_encode(ctx()->getData()));
|
||||
$dispatcher = new EventDispatcher(CQMetaEvent::class);
|
||||
$dispatcher->setRuleFunction(function (CQMetaEvent $v) {
|
||||
return ($v->meta_event_type == '' || ($v->meta_event_type != '' && $v->meta_event_type == ctx()->getData()["meta_event_type"])) &&
|
||||
($v->sub_type == '' || ($v->sub_type != '' && $v->sub_type == (ctx()->getData()["sub_type"] ?? '')));
|
||||
return ($v->meta_event_type == '' || ($v->meta_event_type != '' && $v->meta_event_type == ctx()->getData()["meta_event_type"]));
|
||||
});
|
||||
//eval(BP);
|
||||
$dispatcher->dispatchEvents(ctx()->getData());
|
||||
@@ -167,45 +175,16 @@ class QQBot
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $req
|
||||
* @throws Exception
|
||||
*/
|
||||
private function dispatchAPIResponse($req) {
|
||||
$status = $req["status"];
|
||||
$retcode = $req["retcode"];
|
||||
$data = $req["data"];
|
||||
if (isset($req["echo"]) && is_numeric($req["echo"])) {
|
||||
$r = LightCacheInside::get("wait_api", "wait_api");
|
||||
if (isset($r[$req["echo"]])) {
|
||||
$origin = $r[$req["echo"]];
|
||||
$self_id = $origin["self_id"];
|
||||
$response = [
|
||||
"status" => $status,
|
||||
"retcode" => $retcode,
|
||||
"data" => $data,
|
||||
"self_id" => $self_id,
|
||||
"echo" => $req["echo"]
|
||||
];
|
||||
set_coroutine_params(["cq_response" => $response]);
|
||||
$dispatcher = new EventDispatcher(CQAPIResponse::class);
|
||||
$dispatcher->setRuleFunction(function (CQAPIResponse $response) {
|
||||
return $response->retcode == ctx()->getCQResponse()["retcode"];
|
||||
});
|
||||
$dispatcher->dispatchEvents($response);
|
||||
|
||||
$origin_ctx = ctx()->copy();
|
||||
set_coroutine_params($origin_ctx);
|
||||
if (($origin["coroutine"] ?? false) !== false) {
|
||||
SpinLock::lock("wait_api");
|
||||
$r = LightCacheInside::get("wait_api", "wait_api");
|
||||
$r[$req["echo"]]["result"] = $response;
|
||||
LightCacheInside::set("wait_api", "wait_api", $r);
|
||||
SpinLock::unlock("wait_api");
|
||||
Coroutine::resume($origin['coroutine']);
|
||||
}
|
||||
SpinLock::lock("wait_api");
|
||||
$r = LightCacheInside::get("wait_api", "wait_api");
|
||||
unset($r[$req["echo"]]);
|
||||
LightCacheInside::set("wait_api", "wait_api", $r);
|
||||
SpinLock::unlock("wait_api");
|
||||
}
|
||||
}
|
||||
set_coroutine_params(["cq_response" => $req]);
|
||||
$dispatcher = new EventDispatcher(CQAPIResponse::class);
|
||||
$dispatcher->setRuleFunction(function (CQAPIResponse $response) {
|
||||
return $response->retcode == ctx()->getCQResponse()["retcode"];
|
||||
});
|
||||
$dispatcher->dispatchEvents($req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,11 @@ namespace ZM\Store;
|
||||
|
||||
use Exception;
|
||||
use Swoole\Table;
|
||||
use ZM\Annotation\Swoole\OnSave;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Exception\ZMException;
|
||||
|
||||
class LightCache
|
||||
{
|
||||
@@ -19,6 +23,11 @@ class LightCache
|
||||
|
||||
public static $last_error = '';
|
||||
|
||||
/**
|
||||
* @param $config
|
||||
* @return bool|mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function init($config) {
|
||||
self::$config = $config;
|
||||
self::$kv_table = new Table($config["size"], $config["hash_conflict_proportion"]);
|
||||
@@ -50,11 +59,11 @@ class LightCache
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return null|string
|
||||
* @throws Exception
|
||||
* @return null|mixed
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function get(string $key) {
|
||||
if (self::$kv_table === null) throw new Exception("not initialized LightCache");
|
||||
if (self::$kv_table === null) throw new ZMException("not initialized LightCache");
|
||||
self::checkExpire($key);
|
||||
$r = self::$kv_table->get($key);
|
||||
return $r === false ? null : self::parseGet($r);
|
||||
@@ -63,10 +72,10 @@ class LightCache
|
||||
/**
|
||||
* @param string $key
|
||||
* @return mixed|null
|
||||
* @throws Exception
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function getExpire(string $key) {
|
||||
if (self::$kv_table === null) throw new Exception("not initialized LightCache");
|
||||
if (self::$kv_table === null) throw new ZMException("not initialized LightCache");
|
||||
self::checkExpire($key);
|
||||
$r = self::$kv_table->get($key, "expire");
|
||||
return $r === false ? null : $r - time();
|
||||
@@ -77,10 +86,10 @@ class LightCache
|
||||
* @param string|array|int $value
|
||||
* @param int $expire
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function set(string $key, $value, int $expire = -1) {
|
||||
if (self::$kv_table === null) throw new Exception("not initialized LightCache");
|
||||
if (self::$kv_table === null) throw new ZMException("not initialized LightCache");
|
||||
if (is_array($value)) {
|
||||
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
if (strlen($value) >= self::$config["max_strlen"]) return false;
|
||||
@@ -93,7 +102,7 @@ class LightCache
|
||||
$data_type = "bool";
|
||||
$value = json_encode($value);
|
||||
} else {
|
||||
throw new Exception("Only can set string, array and int");
|
||||
throw new ZMException("Only can set string, array and int");
|
||||
}
|
||||
try {
|
||||
return self::$kv_table->set($key, [
|
||||
@@ -110,10 +119,10 @@ class LightCache
|
||||
* @param string $key
|
||||
* @param $value
|
||||
* @return bool|mixed
|
||||
* @throws Exception
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function update(string $key, $value) {
|
||||
if (self::$kv_table === null) throw new Exception("not initialized LightCache.");
|
||||
if (self::$kv_table === null) throw new ZMException("not initialized LightCache.");
|
||||
if (is_array($value)) {
|
||||
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
if (strlen($value) >= self::$config["max_strlen"]) return false;
|
||||
@@ -123,7 +132,7 @@ class LightCache
|
||||
} elseif (is_int($value)) {
|
||||
$data_type = "int";
|
||||
} else {
|
||||
throw new Exception("Only can set string, array and int");
|
||||
throw new ZMException("Only can set string, array and int");
|
||||
}
|
||||
try {
|
||||
if (self::$kv_table->get($key) === false) return false;
|
||||
@@ -169,7 +178,16 @@ class LightCache
|
||||
return $r;
|
||||
}
|
||||
|
||||
public static function savePersistence() {
|
||||
public static function savePersistence($only_worker = false) {
|
||||
|
||||
// 下面将OnSave激活一下
|
||||
if (server()->worker_id == (ZMConfig::get("global", "worker_cache")["worker"] ?? 0)) {
|
||||
$dispatcher = new EventDispatcher(OnSave::class);
|
||||
$dispatcher->dispatchEvents();
|
||||
}
|
||||
|
||||
if($only_worker) return;
|
||||
|
||||
if (self::$kv_table === null) return;
|
||||
$r = [];
|
||||
foreach (self::$kv_table as $k => $v) {
|
||||
@@ -178,11 +196,13 @@ class LightCache
|
||||
$r[$k] = self::parseGet($v);
|
||||
}
|
||||
}
|
||||
if(self::$config["persistence_path"] == "") return;
|
||||
if (self::$config["persistence_path"] == "") return;
|
||||
if (file_exists(self::$config["persistence_path"])) {
|
||||
$r = file_put_contents(self::$config["persistence_path"], json_encode($r, 128 | 256));
|
||||
if ($r === false) Console::error("Not saved, please check your \"persistence_path\"!");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static function checkExpire($key) {
|
||||
|
||||
@@ -23,9 +23,10 @@ class LightCacheInside
|
||||
$result = self::$kv_table["wait_api"]->create() && self::$kv_table["connect"]->create();
|
||||
if ($result === false) {
|
||||
self::$last_error = '系统内存不足,申请失败';
|
||||
return $result;
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,18 +15,16 @@ class SpinLock
|
||||
|
||||
private static $delay = 1;
|
||||
|
||||
public static function init($key_cnt, $delay = 1)
|
||||
{
|
||||
public static function init($key_cnt, $delay = 1) {
|
||||
self::$kv_lock = new Table($key_cnt, 0.7);
|
||||
self::$delay = $delay;
|
||||
self::$kv_lock->column('lock_num', Table::TYPE_INT, 8);
|
||||
return self::$kv_lock->create();
|
||||
}
|
||||
|
||||
public static function lock(string $key)
|
||||
{
|
||||
public static function lock(string $key) {
|
||||
while (($r = self::$kv_lock->incr($key, 'lock_num')) > 1) { //此资源已经被锁上了
|
||||
if(Coroutine::getCid() != -1) System::sleep(self::$delay / 1000);
|
||||
if (Coroutine::getCid() != -1) System::sleep(self::$delay / 1000);
|
||||
else usleep(self::$delay * 1000);
|
||||
}
|
||||
}
|
||||
@@ -41,4 +39,10 @@ class SpinLock
|
||||
public static function unlock(string $key) {
|
||||
return self::$kv_lock->set($key, ['lock_num' => 0]);
|
||||
}
|
||||
|
||||
public static function transaction(string $key, callable $function) {
|
||||
SpinLock::lock($key);
|
||||
$function();
|
||||
SpinLock::unlock($key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class ZMRedis
|
||||
* @throws NotInitializedException
|
||||
*/
|
||||
public static function call(callable $callable) {
|
||||
if(ZMRedisPool::$pool === null) throw new NotInitializedException("Redis pool is not initialized.");
|
||||
if (ZMRedisPool::$pool === null) throw new NotInitializedException("Redis pool is not initialized.");
|
||||
$r = ZMRedisPool::$pool->get();
|
||||
$result = $callable($r);
|
||||
if (isset($r->wasted)) ZMRedisPool::$pool->put(null);
|
||||
@@ -29,7 +29,7 @@ class ZMRedis
|
||||
* @throws NotInitializedException
|
||||
*/
|
||||
public function __construct() {
|
||||
if(ZMRedisPool::$pool === null) throw new NotInitializedException("Redis pool is not initialized.");
|
||||
if (ZMRedisPool::$pool === null) throw new NotInitializedException("Redis pool is not initialized.");
|
||||
$this->conn = ZMRedisPool::$pool->get();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,13 +24,13 @@ class ZMRedisPool
|
||||
);
|
||||
try {
|
||||
$r = self::$pool->get()->ping('123');
|
||||
if(strpos(strtolower($r), "123") !== false) {
|
||||
if (strpos(strtolower($r), "123") !== false) {
|
||||
Console::debug("成功连接redis连接池!");
|
||||
} else {
|
||||
var_dump($r);
|
||||
}
|
||||
} catch (RedisException $e) {
|
||||
Console::error("Redis init failed! ".$e->getMessage());
|
||||
Console::error("Redis init failed! " . $e->getMessage());
|
||||
self::$pool = null;
|
||||
}
|
||||
}
|
||||
|
||||
96
src/ZM/Store/WorkerCache.php
Normal file
96
src/ZM/Store/WorkerCache.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Store;
|
||||
|
||||
|
||||
use ZM\Config\ZMConfig;
|
||||
|
||||
class WorkerCache
|
||||
{
|
||||
public static $config = null;
|
||||
|
||||
public static $store = [];
|
||||
|
||||
public static $transfer = [];
|
||||
|
||||
public static function get($key) {
|
||||
$config = self::$config ?? ZMConfig::get("global", "worker_cache") ?? ["worker" => 0];
|
||||
if ($config["worker"] === server()->worker_id) {
|
||||
return self::$store[$key] ?? null;
|
||||
} else {
|
||||
$action = ["action" => "getWorkerCache", "key" => $key, "cid" => zm_cid()];
|
||||
server()->sendMessage(json_encode($action, JSON_UNESCAPED_UNICODE), $config["worker"]);
|
||||
zm_yield();
|
||||
$p = self::$transfer[zm_cid()] ?? null;
|
||||
unset(self::$transfer[zm_cid()]);
|
||||
return $p;
|
||||
}
|
||||
}
|
||||
|
||||
public static function set($key, $value, $async = false) {
|
||||
$config = self::$config ?? ZMConfig::get("global", "worker_cache") ?? ["worker" => 0];
|
||||
if ($config["worker"] === server()->worker_id) {
|
||||
self::$store[$key] = $value;
|
||||
return true;
|
||||
} else {
|
||||
$action = ["action" => $async ? "asyncSetWorkerCache" : "setWorkerCache", "key" => $key, "value" => $value, "cid" => zm_cid()];
|
||||
return self::processRemote($action, $async, $config);
|
||||
}
|
||||
}
|
||||
|
||||
public static function hasKey($key, $subkey) {
|
||||
$config = self::$config ?? ZMConfig::get("global", "worker_cache") ?? ["worker" => 0];
|
||||
if ($config["worker"] === server()->worker_id) {
|
||||
return isset(self::$store[$key][$subkey]);
|
||||
} else {
|
||||
$action = ["hasKeyWorkerCache", "key" => $key, "subkey" => $subkey, "cid" => zm_cid()];
|
||||
return self::processRemote($action, false, $config);
|
||||
}
|
||||
}
|
||||
|
||||
private static function processRemote($action, $async, $config) {
|
||||
$ss = server()->sendMessage(json_encode($action, JSON_UNESCAPED_UNICODE), $config["worker"]);
|
||||
if (!$ss) return false;
|
||||
if ($async) return true;
|
||||
zm_yield();
|
||||
$p = self::$transfer[zm_cid()] ?? null;
|
||||
unset(self::$transfer[zm_cid()]);
|
||||
return $p;
|
||||
}
|
||||
|
||||
public static function unset($key, $async = false) {
|
||||
$config = self::$config ?? ZMConfig::get("global", "worker_cache") ?? ["worker" => 0];
|
||||
if ($config["worker"] === server()->worker_id) {
|
||||
unset(self::$store[$key]);
|
||||
return true;
|
||||
} else {
|
||||
$action = ["action" => $async ? "asyncUnsetWorkerCache" : "unsetWorkerCache", "key" => $key, "cid" => zm_cid()];
|
||||
return self::processRemote($action, $async, $config);
|
||||
}
|
||||
}
|
||||
|
||||
public static function add($key, int $value, $async = false) {
|
||||
$config = self::$config ?? ZMConfig::get("global", "worker_cache") ?? ["worker" => 0];
|
||||
if ($config["worker"] === server()->worker_id) {
|
||||
if (!isset(self::$store[$key])) self::$store[$key] = 0;
|
||||
self::$store[$key] += $value;
|
||||
return true;
|
||||
} else {
|
||||
$action = ["action" => $async ? "asyncAddWorkerCache" : "addWorkerCache", "key" => $key, "value" => $value, "cid" => zm_cid()];
|
||||
return self::processRemote($action, $async, $config);
|
||||
}
|
||||
}
|
||||
|
||||
public static function sub($key, int $value, $async = false) {
|
||||
$config = self::$config ?? ZMConfig::get("global", "worker_cache") ?? ["worker" => 0];
|
||||
if ($config["worker"] === server()->worker_id) {
|
||||
if (!isset(self::$store[$key])) self::$store[$key] = 0;
|
||||
self::$store[$key] -= $value;
|
||||
return true;
|
||||
} else {
|
||||
$action = ["action" => $async ? "asyncSubWorkerCache" : "subWorkerCache", "key" => $key, "value" => $value, "cid" => zm_cid()];
|
||||
return self::processRemote($action, $async, $config);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ class CoMessage
|
||||
* @param array $hang
|
||||
* @param array $compare
|
||||
* @param int $timeout
|
||||
* @return bool
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function yieldByWS(array $hang, array $compare, $timeout = 600) {
|
||||
@@ -55,20 +55,21 @@ class CoMessage
|
||||
SpinLock::lock("wait_api");
|
||||
$all = LightCacheInside::get("wait_api", "wait_api") ?? [];
|
||||
foreach ($all as $k => $v) {
|
||||
if(!isset($v["compare"])) continue;
|
||||
if (!isset($v["compare"])) continue;
|
||||
foreach ($v["compare"] as $vs) {
|
||||
if ($v[$vs] != ($dat[$vs] ?? null)) {
|
||||
if (!isset($v[$vs], $dat[$vs])) continue 2;
|
||||
if ($v[$vs] != $dat[$vs]) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
$last = $k;
|
||||
}
|
||||
if($last !== null) {
|
||||
if ($last !== null) {
|
||||
$all[$last]["result"] = $dat;
|
||||
LightCacheInside::set("wait_api", "wait_api", $all);
|
||||
SpinLock::unlock("wait_api");
|
||||
if ($all[$last]["worker_id"] != server()->worker_id) {
|
||||
ZMUtil::sendActionToWorker($all[$k]["worker_id"], "resume_ws_message", $all[$last]);
|
||||
ZMUtil::sendActionToWorker($all[$last]["worker_id"], "resume_ws_message", $all[$last]);
|
||||
} else {
|
||||
Co::resume($all[$last]["coroutine"]);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace ZM\Utils;
|
||||
|
||||
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
|
||||
class DataProvider
|
||||
{
|
||||
@@ -15,7 +16,7 @@ class DataProvider
|
||||
}
|
||||
|
||||
public static function getWorkingDir() {
|
||||
if(LOAD_MODE == 0) return WORKING_DIR;
|
||||
if (LOAD_MODE == 0) return WORKING_DIR;
|
||||
elseif (LOAD_MODE == 1) return LOAD_MODE_COMPOSER_PATH;
|
||||
elseif (LOAD_MODE == 2) return realpath('.');
|
||||
return null;
|
||||
@@ -28,4 +29,29 @@ class DataProvider
|
||||
public static function getDataFolder() {
|
||||
return ZM_DATA;
|
||||
}
|
||||
|
||||
public static function saveToJson($filename, $file_array) {
|
||||
$path = ZMConfig::get("global", "config_dir");
|
||||
$r = explode("/", $filename);
|
||||
if(count($r) == 2) {
|
||||
$path = $path . $r[0]."/";
|
||||
if(!is_dir($path)) mkdir($path);
|
||||
$name = $r[1];
|
||||
} elseif (count($r) != 1) {
|
||||
Console::warning("存储失败,文件名只能有一级目录");
|
||||
return false;
|
||||
} else {
|
||||
$name = $r[0];
|
||||
}
|
||||
return file_put_contents($path.$name.".json", json_encode($file_array, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
public static function loadFromJson($filename) {
|
||||
$path = ZMConfig::get("global", "config_dir");
|
||||
if(file_exists($path.$filename.".json")) {
|
||||
return json_decode(file_get_contents($path.$filename.".json"), true);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,57 +5,47 @@ namespace ZM\Utils;
|
||||
|
||||
|
||||
use Co;
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\Matcher\UrlMatcher;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\EventManager;
|
||||
use ZM\Http\Response;
|
||||
use ZM\Http\RouteManager;
|
||||
|
||||
class HttpUtil
|
||||
{
|
||||
public static function parseUri($request, $response, $uri, &$node, &$params) {
|
||||
$uri = explode("/", $uri);
|
||||
$uri = array_diff($uri, ["..", "", "."]);
|
||||
$node = EventManager::$req_mapping;
|
||||
$params = [];
|
||||
while (true) {
|
||||
$r = array_shift($uri);
|
||||
if ($r === null) break;
|
||||
if (($cnt = count($node["son"] ?? [])) == 1) {
|
||||
if (isset($node["param_route"])) {
|
||||
foreach ($node["son"] as $k => $v) {
|
||||
if ($v["id"] == $node["param_route"]) {
|
||||
$node = $v;
|
||||
$params[mb_substr($v["name"], 1, -1)] = $r;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
} elseif ($node["son"][0]["name"] == $r) {
|
||||
$node = $node["son"][0];
|
||||
continue;
|
||||
}
|
||||
} elseif ($cnt >= 1) {
|
||||
if (isset($node["param_route"])) {
|
||||
foreach ($node["son"] as $k => $v) {
|
||||
if ($v["id"] == $node["param_route"]) {
|
||||
$node = $v;
|
||||
$params[mb_substr($v["name"], 1, -1)] = $r;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($node["son"] as $k => $v) {
|
||||
if ($v["name"] == $r) {
|
||||
$node = $v;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
$context = new RequestContext();
|
||||
$context->setMethod($request->server['request_method']);
|
||||
|
||||
try {
|
||||
$matcher = new UrlMatcher(RouteManager::$routes ?? new RouteCollection(), $context);
|
||||
$matched = $matcher->match($uri);
|
||||
} catch (ResourceNotFoundException $e) {
|
||||
if (ZMConfig::get("global", "static_file_server")["status"]) {
|
||||
HttpUtil::handleStaticPage($request->server["request_uri"], $response);
|
||||
return null;
|
||||
}
|
||||
$matched = null;
|
||||
} catch (MethodNotAllowedException $e) {
|
||||
$matched = null;
|
||||
}
|
||||
if ($matched !== null) {
|
||||
$node = [
|
||||
"route" => RouteManager::$routes->get($matched["_route"])->getPath(),
|
||||
"class" => $matched["_class"],
|
||||
"method" => $matched["_method"],
|
||||
"request_method" => $request->server['request_method']
|
||||
];
|
||||
unset($matched["_class"], $matched["_method"]);
|
||||
$params = $matched;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return !isset($node["route"]) ? false : true;
|
||||
}
|
||||
|
||||
public static function getHttpCodePage(int $http_code) {
|
||||
@@ -83,9 +73,9 @@ class HttpUtil
|
||||
return true;
|
||||
}
|
||||
if (is_dir($path)) {
|
||||
if(mb_substr($uri, -1, 1) != "/") {
|
||||
if (mb_substr($uri, -1, 1) != "/") {
|
||||
Console::info("[302] " . $uri);
|
||||
$response->redirect($uri."/", 302);
|
||||
$response->redirect($uri . "/", 302);
|
||||
return true;
|
||||
}
|
||||
foreach ($base_index as $vp) {
|
||||
|
||||
17
src/ZM/Utils/ProcessManager.php
Normal file
17
src/ZM/Utils/ProcessManager.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
class ProcessManager
|
||||
{
|
||||
public static function runOnTask($param, $timeout = 0.5, $dst_worker_id = -1) {
|
||||
return server()->taskwait([
|
||||
"action" => "runMethod",
|
||||
"class" => $param["class"],
|
||||
"method" => $param["method"],
|
||||
"params" => $param["params"]
|
||||
], $timeout, $dst_worker_id);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ trait SingletonTrait
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
protected static $cached = [];
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
<?php #plain
|
||||
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Utils\DataProvider;
|
||||
|
||||
define("ZM_START_TIME", microtime(true));
|
||||
define("ZM_DATA", ZMConfig::get("global", "zm_data"));
|
||||
define("ZM_VERSION", json_decode(file_get_contents(__DIR__ . "/../../composer.json"), true)["version"] ?? "unknown");
|
||||
define("APP_VERSION", json_decode(file_get_contents(DataProvider::getWorkingDir() . "/composer.json"), true)["version"] ?? "unknown");
|
||||
define("CRASH_DIR", ZMConfig::get("global", "crash_dir"));
|
||||
@mkdir(ZM_DATA);
|
||||
@mkdir(CRASH_DIR);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php #plain
|
||||
|
||||
use Swoole\Coroutine;
|
||||
use ZM\API\ZMRobot;
|
||||
@@ -24,6 +24,7 @@ function phar_classloader($p) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
require_once $filepath;
|
||||
} catch (Exception $e) {
|
||||
echo "Error when finding class: " . $p . PHP_EOL;
|
||||
@@ -75,7 +76,7 @@ function unicode_decode($str) {
|
||||
/**
|
||||
* 获取模块文件夹下的每个类文件的类名称
|
||||
* @param $dir
|
||||
* @param string $indoor_name
|
||||
* @param $indoor_name
|
||||
* @return array
|
||||
*/
|
||||
function getAllClasses($dir, $indoor_name) {
|
||||
@@ -88,6 +89,14 @@ function getAllClasses($dir, $indoor_name) {
|
||||
//echo "At " . $indoor_name . PHP_EOL;
|
||||
if (is_dir($dir . $v)) $classes = array_merge($classes, getAllClasses($dir . $v . "/", $indoor_name . "\\" . $v));
|
||||
elseif (mb_substr($v, -4) == ".php") {
|
||||
if (substr(file_get_contents($dir . $v), 6, 6) == "#plain") continue;
|
||||
$composer = json_decode(file_get_contents(DataProvider::getWorkingDir() . "/composer.json"), true);
|
||||
foreach ($composer["autoload"]["files"] as $fi) {
|
||||
if (realpath(DataProvider::getWorkingDir() . "/" . $fi) == realpath($dir . $v)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
if ($v == "global_function.php") continue;
|
||||
$class_name = $indoor_name . "\\" . mb_substr($v, 0, -4);
|
||||
$classes [] = $class_name;
|
||||
}
|
||||
@@ -258,10 +267,14 @@ function zm_timer_after($ms, callable $callable) {
|
||||
}
|
||||
|
||||
function zm_timer_tick($ms, callable $callable) {
|
||||
go(function () use ($ms, $callable) {
|
||||
Console::debug("Adding extra timer tick of " . $ms . " ms");
|
||||
Swoole\Timer::tick($ms, $callable);
|
||||
});
|
||||
if (zm_cid() === -1) {
|
||||
return go(function () use ($ms, $callable) {
|
||||
Console::debug("Adding extra timer tick of " . $ms . " ms");
|
||||
Swoole\Timer::tick($ms, $callable);
|
||||
});
|
||||
} else {
|
||||
return Swoole\Timer::tick($ms, $callable);
|
||||
}
|
||||
}
|
||||
|
||||
function zm_data_hash($v) {
|
||||
@@ -290,8 +303,9 @@ function bot() {
|
||||
|
||||
/**
|
||||
* 获取同类型所有连接的文件描述符 ID
|
||||
* @author 854854321
|
||||
* @param string $type
|
||||
* @return array
|
||||
* @author 854854321
|
||||
*/
|
||||
function getAllFdByConnectType(string $type = 'default'): array {
|
||||
$fds = [];
|
||||
@@ -300,3 +314,19 @@ function getAllFdByConnectType(string $type = 'default'): array {
|
||||
}
|
||||
return $fds;
|
||||
}
|
||||
|
||||
function zm_atomic($name) {
|
||||
return \ZM\Store\ZMAtomic::get($name);
|
||||
}
|
||||
|
||||
function uuidgen($uppercase = false) {
|
||||
try {
|
||||
$data = random_bytes(16);
|
||||
} catch (Exception $e) {
|
||||
return "";
|
||||
}
|
||||
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
|
||||
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
|
||||
return $uppercase ? strtoupper(vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4))) :
|
||||
vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||
}
|
||||
Reference in New Issue
Block a user