mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-21 23:55:35 +08:00
update to 2.5.0-b1 (build 408)
This commit is contained in:
@@ -20,7 +20,7 @@ class CQ
|
||||
if (is_numeric($qq) || $qq === "all") {
|
||||
return "[CQ:at,qq=" . $qq . "]";
|
||||
}
|
||||
Console::warning("传入的QQ号码($qq)错误!");
|
||||
Console::warning(zm_internal_errcode("E00035") . "传入的QQ号码($qq)错误!");
|
||||
return " ";
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class CQ
|
||||
if (is_numeric($id)) {
|
||||
return "[CQ:face,id=" . $id . "]";
|
||||
}
|
||||
Console::warning("传入的face id($id)错误!");
|
||||
Console::warning(zm_internal_errcode("E00035") . "传入的face id($id)错误!");
|
||||
return " ";
|
||||
}
|
||||
|
||||
@@ -46,13 +46,13 @@ class CQ
|
||||
* @param int $timeout
|
||||
* @return string
|
||||
*/
|
||||
public static function image($file, $cache = true, $flash = false, $proxy = true, $timeout = -1) {
|
||||
public static function image($file, bool $cache = true, bool $flash = false, bool $proxy = true, int $timeout = -1) {
|
||||
return
|
||||
"[CQ:image,file=" . self::encode($file, true) .
|
||||
(!$cache ? ",cache=0" : "") .
|
||||
($flash ? ",type=flash" : "") .
|
||||
(!$proxy ? ",proxy=false" : "") .
|
||||
($timeout != -1 ? (",timeout=" . intval($timeout)) : "") .
|
||||
($timeout != -1 ? (",timeout=" . $timeout) : "") .
|
||||
"]";
|
||||
}
|
||||
|
||||
@@ -65,13 +65,13 @@ class CQ
|
||||
* @param int $timeout
|
||||
* @return string
|
||||
*/
|
||||
public static function record($file, $magic = false, $cache = true, $proxy = true, $timeout = -1) {
|
||||
public static function record($file, bool $magic = false, bool $cache = true, bool $proxy = true, int $timeout = -1) {
|
||||
return
|
||||
"[CQ:record,file=" . self::encode($file, true) .
|
||||
(!$cache ? ",cache=0" : "") .
|
||||
($magic ? ",magic=1" : "") .
|
||||
(!$proxy ? ",proxy=false" : "") .
|
||||
($timeout != -1 ? (",timeout=" . intval($timeout)) : "") .
|
||||
($timeout != -1 ? (",timeout=" . $timeout) : "") .
|
||||
"]";
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ class CQ
|
||||
* @param int $timeout
|
||||
* @return string
|
||||
*/
|
||||
public static function video($file, $cache = true, $proxy = true, $timeout = -1) {
|
||||
public static function video($file, bool $cache = true, bool $proxy = true, int $timeout = -1) {
|
||||
return
|
||||
"[CQ:video,file=" . self::encode($file, true) .
|
||||
(!$cache ? ",cache=0" : "") .
|
||||
@@ -123,8 +123,8 @@ class CQ
|
||||
* @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)) : "") . "]";
|
||||
public static function poke($type, $id, string $name = "") {
|
||||
return "[CQ:poke,type=$type,id=$id" . ($name != "" ? (",name=" . self::encode($name, true)) : "") . "]";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +132,7 @@ class CQ
|
||||
* @param int $ignore
|
||||
* @return string
|
||||
*/
|
||||
public static function anonymous($ignore = 1) {
|
||||
public static function anonymous(int $ignore = 1) {
|
||||
return "[CQ:anonymous" . ($ignore != 1 ? ",ignore=0" : "") . "]";
|
||||
}
|
||||
|
||||
@@ -170,12 +170,12 @@ class CQ
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
public static function location($lat, $lon, $title = "", $content = "") {
|
||||
public static function location($lat, $lon, string $title = "", string $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)) : "") .
|
||||
",lat=" . self::encode($lat, true) .
|
||||
",lon=" . self::encode($lon, true) .
|
||||
($title != "" ? (",title=" . self::encode($title, true)) : "") .
|
||||
($content != "" ? (",content=" . self::encode($content, true)) : "") .
|
||||
"]";
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ class CQ
|
||||
return "[CQ:music,type=$type,id=$id_or_url]";
|
||||
case "custom":
|
||||
if ($title === null || $audio === null) {
|
||||
Console::warning("传入CQ码实例的标题和音频链接不能为空!");
|
||||
Console::warning(zm_internal_errcode("E00035") . "传入CQ码实例的标题和音频链接不能为空!");
|
||||
return " ";
|
||||
}
|
||||
if ($content === null) $c = "";
|
||||
@@ -217,17 +217,17 @@ class CQ
|
||||
",audio=" . self::encode($audio, true) . ",title=" . self::encode($title, true) . $c . $i .
|
||||
"]";
|
||||
default:
|
||||
Console::warning("传入的music type($type)错误!");
|
||||
Console::warning(zm_internal_errcode("E00035") . "传入的music type($type)错误!");
|
||||
return " ";
|
||||
}
|
||||
}
|
||||
|
||||
public static function forward($id) {
|
||||
return "[CQ:forward,id=$id]";
|
||||
return "[CQ:forward,id=".self::encode($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) . "]";
|
||||
return "[CQ:node,user_id=$user_id,nickname=" . self::encode($nickname, true) . ",content=" . self::encode($content, true) . "]";
|
||||
}
|
||||
|
||||
public static function xml($data) {
|
||||
@@ -297,7 +297,7 @@ class CQ
|
||||
public static function removeCQ($msg) {
|
||||
$final = "";
|
||||
$last_end = 0;
|
||||
foreach(self::getAllCQ($msg) as $k => $v) {
|
||||
foreach (self::getAllCQ($msg) as $v) {
|
||||
$final .= mb_substr($msg, $last_end, $v["start"] - $last_end);
|
||||
$last_end = $v["end"] + 1;
|
||||
}
|
||||
@@ -319,7 +319,7 @@ class CQ
|
||||
$content = mb_substr($msg, $head + 4, $close + $head - mb_strlen($msg));
|
||||
$exp = explode(",", $content);
|
||||
$cq["type"] = array_shift($exp);
|
||||
foreach ($exp as $k => $v) {
|
||||
foreach ($exp as $v) {
|
||||
$ss = explode("=", $v);
|
||||
$sk = array_shift($ss);
|
||||
$cq["params"][$sk] = self::decode(implode("=", $ss), true);
|
||||
@@ -349,7 +349,7 @@ class CQ
|
||||
$exp = explode(",", $content);
|
||||
$cq = [];
|
||||
$cq["type"] = array_shift($exp);
|
||||
foreach ($exp as $k => $v) {
|
||||
foreach ($exp as $v) {
|
||||
$ss = explode("=", $v);
|
||||
$sk = array_shift($ss);
|
||||
$cq["params"][$sk] = self::decode(implode("=", $ss), true);
|
||||
|
||||
@@ -22,8 +22,6 @@ trait CQAPI
|
||||
return $this->processWebsocketAPI($connection, $reply, $function);
|
||||
else
|
||||
return $this->processHttpAPI($connection, $reply, $function);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function processWebsocketAPI($connection, $reply, $function = false) {
|
||||
@@ -41,7 +39,7 @@ trait CQAPI
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
Console::warning("CQAPI send failed, websocket push error.");
|
||||
Console::warning(zm_internal_errcode("E00036") . "CQAPI send failed, websocket push error.");
|
||||
$response = [
|
||||
"status" => "failed",
|
||||
"retcode" => -1000,
|
||||
@@ -69,7 +67,6 @@ trait CQAPI
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @noinspection PhpMissingReturnTypeInspection */
|
||||
public function __call($name, $arguments) {
|
||||
return false;
|
||||
}
|
||||
|
||||
689
src/ZM/API/OneBotV11.php
Normal file
689
src/ZM/API/OneBotV11.php
Normal file
@@ -0,0 +1,689 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\API;
|
||||
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\ConnectionManager\ManagerGM;
|
||||
use ZM\Exception\RobotNotFoundException;
|
||||
|
||||
/**
|
||||
* OneBot V11 的 API 实现类
|
||||
* Class OneBotV11
|
||||
* @package ZM\API
|
||||
* @since 2.5
|
||||
*/
|
||||
class OneBotV11
|
||||
{
|
||||
use CQAPI;
|
||||
|
||||
const API_ASYNC = 1;
|
||||
const API_NORMAL = 0;
|
||||
const API_RATE_LIMITED = 2;
|
||||
|
||||
/** @var ConnectionObject|null */
|
||||
private $connection;
|
||||
|
||||
private $callback = true;
|
||||
private $prefix = 0;
|
||||
|
||||
/**
|
||||
* @param $robot_id
|
||||
* @return ZMRobot
|
||||
* @throws RobotNotFoundException
|
||||
*/
|
||||
public static function get($robot_id) {
|
||||
$r = ManagerGM::getAllByName('qq');
|
||||
foreach ($r as $v) {
|
||||
if ($v->getOption('connect_id') == $robot_id) return new ZMRobot($v);
|
||||
}
|
||||
throw new RobotNotFoundException("机器人 " . $robot_id . " 未连接到框架!");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ZMRobot
|
||||
* @throws RobotNotFoundException
|
||||
*/
|
||||
public static function getRandom() {
|
||||
$r = ManagerGM::getAllByName('qq');
|
||||
if ($r == []) throw new RobotNotFoundException("没有任何机器人连接到框架!");
|
||||
return new ZMRobot($r[array_rand($r)]);
|
||||
}
|
||||
|
||||
public static function getFirst() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ZMRobot[]
|
||||
*/
|
||||
public static function getAllRobot() {
|
||||
$r = ManagerGM::getAllByName('qq');
|
||||
$obj = [];
|
||||
foreach ($r as $v) {
|
||||
$obj[] = new ZMRobot($v);
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function __construct(ConnectionObject $connection) {
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function setCallback($callback = true) {
|
||||
$this->callback = $callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPrefix($prefix = self::API_NORMAL) {
|
||||
$this->prefix = $prefix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSelfId() {
|
||||
return $this->connection->getOption('connect_id');
|
||||
}
|
||||
|
||||
/* 下面是 OneBot 标准的 V11 公开 API */
|
||||
|
||||
/**
|
||||
* 发送私聊消息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_private_msg-%E5%8F%91%E9%80%81%E7%A7%81%E8%81%8A%E6%B6%88%E6%81%AF
|
||||
* @param $user_id
|
||||
* @param $message
|
||||
* @param bool $auto_escape
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendPrivateMsg($user_id, $message, $auto_escape = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'user_id' => $user_id,
|
||||
'message' => $message,
|
||||
'auto_escape' => $auto_escape
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送群消息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_group_msg-%E5%8F%91%E9%80%81%E7%BE%A4%E6%B6%88%E6%81%AF
|
||||
* @param $group_id
|
||||
* @param $message
|
||||
* @param bool $auto_escape
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendGroupMsg($group_id, $message, $auto_escape = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'message' => $message,
|
||||
'auto_escape' => $auto_escape
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_msg-%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF
|
||||
* @param $message_type
|
||||
* @param $target_id
|
||||
* @param $message
|
||||
* @param bool $auto_escape
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendMsg($message_type, $target_id, $message, $auto_escape = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'message_type' => $message_type,
|
||||
($message_type == 'private' ? 'user' : $message_type) . '_id' => $target_id,
|
||||
'message' => $message,
|
||||
'auto_escape' => $auto_escape
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回消息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#delete_msg-%E6%92%A4%E5%9B%9E%E6%B6%88%E6%81%AF
|
||||
* @param $message_id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function deleteMsg($message_id) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'message_id' => $message_id
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_msg-%E8%8E%B7%E5%8F%96%E6%B6%88%E6%81%AF
|
||||
* @param $message_id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getMsg($message_id) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'message_id' => $message_id
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取合并转发消息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_forward_msg-%E8%8E%B7%E5%8F%96%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E6%B6%88%E6%81%AF
|
||||
* @param $id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getForwardMsg($id) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'id' => $id
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送好友赞
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_like-%E5%8F%91%E9%80%81%E5%A5%BD%E5%8F%8B%E8%B5%9E
|
||||
* @param $user_id
|
||||
* @param int $times
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendLike($user_id, $times = 1) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'user_id' => $user_id,
|
||||
'times' => $times
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组踢人
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_kick-%E7%BE%A4%E7%BB%84%E8%B8%A2%E4%BA%BA
|
||||
* @param $group_id
|
||||
* @param $user_id
|
||||
* @param bool $reject_add_request
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupKick($group_id, $user_id, $reject_add_request = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
'reject_add_request' => $reject_add_request
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组单人禁言
|
||||
* @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 $group_id
|
||||
* @param $user_id
|
||||
* @param $duration
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupBan($group_id, $user_id, $duration = 1800) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
'duration' => $duration
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组匿名用户禁言
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_anonymous_ban-%E7%BE%A4%E7%BB%84%E5%8C%BF%E5%90%8D%E7%94%A8%E6%88%B7%E7%A6%81%E8%A8%80
|
||||
* @param $group_id
|
||||
* @param $anonymous_or_flag
|
||||
* @param int $duration
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAnonymousBan($group_id, $anonymous_or_flag, $duration = 1800) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
(is_string($anonymous_or_flag) ? 'flag' : 'anonymous') => $anonymous_or_flag,
|
||||
'duration' => $duration
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组全员禁言
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_whole_ban-%E7%BE%A4%E7%BB%84%E5%85%A8%E5%91%98%E7%A6%81%E8%A8%80
|
||||
* @param $group_id
|
||||
* @param bool $enable
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupWholeBan($group_id, $enable = true) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'enable' => $enable
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组设置管理员
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_admin-%E7%BE%A4%E7%BB%84%E8%AE%BE%E7%BD%AE%E7%AE%A1%E7%90%86%E5%91%98
|
||||
* @param $group_id
|
||||
* @param $user_id
|
||||
* @param bool $enable
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAdmin($group_id, $user_id, $enable = true) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
'enable' => $enable
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组匿名
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_anonymous-%E7%BE%A4%E7%BB%84%E5%8C%BF%E5%90%8D
|
||||
* @param $group_id
|
||||
* @param bool $enable
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAnonymous($group_id, $enable = true) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'enable' => $enable
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置群名片(群备注)
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_card-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D%E7%89%87%E7%BE%A4%E5%A4%87%E6%B3%A8
|
||||
* @param $group_id
|
||||
* @param $user_id
|
||||
* @param string $card
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupCard($group_id, $user_id, $card = "") {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
'card' => $card
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置群名
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_name-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D
|
||||
* @param $group_id
|
||||
* @param $group_name
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupName($group_id, $group_name) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'group_name' => $group_name
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出群组
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_leave-%E9%80%80%E5%87%BA%E7%BE%A4%E7%BB%84
|
||||
* @param $group_id
|
||||
* @param bool $is_dismiss
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupLeave($group_id, $is_dismiss = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'is_dismiss' => $is_dismiss
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置群组专属头衔
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_special_title-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E7%BB%84%E4%B8%93%E5%B1%9E%E5%A4%B4%E8%A1%94
|
||||
* @param $group_id
|
||||
* @param $user_id
|
||||
* @param string $special_title
|
||||
* @param int $duration
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupSpecialTitle($group_id, $user_id, $special_title = "", $duration = -1) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
'special_title' => $special_title,
|
||||
'duration' => $duration
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理加好友请求
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_friend_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E5%A5%BD%E5%8F%8B%E8%AF%B7%E6%B1%82
|
||||
* @param $flag
|
||||
* @param bool $approve
|
||||
* @param string $remark
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setFriendAddRequest($flag, $approve = true, $remark = "") {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'flag' => $flag,
|
||||
'approve' => $approve,
|
||||
'remark' => $remark
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理加群请求/邀请
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E7%BE%A4%E8%AF%B7%E6%B1%82%E9%82%80%E8%AF%B7
|
||||
* @param $flag
|
||||
* @param $sub_type
|
||||
* @param bool $approve
|
||||
* @param string $reason
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAddRequest($flag, $sub_type, $approve = true, $reason = "") {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'flag' => $flag,
|
||||
'sub_type' => $sub_type,
|
||||
'approve' => $approve,
|
||||
'reason' => $reason
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录号信息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_login_info-%E8%8E%B7%E5%8F%96%E7%99%BB%E5%BD%95%E5%8F%B7%E4%BF%A1%E6%81%AF
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getLoginInfo() {
|
||||
return $this->processAPI($this->connection, ['action' => $this->getActionName(__FUNCTION__)], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取陌生人信息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_stranger_info-%E8%8E%B7%E5%8F%96%E9%99%8C%E7%94%9F%E4%BA%BA%E4%BF%A1%E6%81%AF
|
||||
* @param $user_id
|
||||
* @param bool $no_cache
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getStrangerInfo($user_id, $no_cache = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'user_id' => $user_id,
|
||||
'no_cache' => $no_cache
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取好友列表
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_friend_list-%E8%8E%B7%E5%8F%96%E5%A5%BD%E5%8F%8B%E5%88%97%E8%A1%A8
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getFriendList() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群信息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E4%BF%A1%E6%81%AF
|
||||
* @param $group_id
|
||||
* @param bool $no_cache
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupInfo($group_id, $no_cache = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'no_cache' => $no_cache
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群列表
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E5%88%97%E8%A1%A8
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupList() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群成员信息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_member_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E4%BF%A1%E6%81%AF
|
||||
* @param $group_id
|
||||
* @param $user_id
|
||||
* @param bool $no_cache
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupMemberInfo($group_id, $user_id, $no_cache = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
'no_cache' => $no_cache
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群成员列表
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_member_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E5%88%97%E8%A1%A8
|
||||
* @param $group_id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupMemberList($group_id) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群荣誉信息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_honor_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E8%8D%A3%E8%AA%89%E4%BF%A1%E6%81%AF
|
||||
* @param $group_id
|
||||
* @param $type
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupHonorInfo($group_id, $type) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'type' => $type
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 CSRF Token
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_csrf_token-%E8%8E%B7%E5%8F%96-csrf-token
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getCsrfToken() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 QQ 相关接口凭证
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_credentials-%E8%8E%B7%E5%8F%96-qq-%E7%9B%B8%E5%85%B3%E6%8E%A5%E5%8F%A3%E5%87%AD%E8%AF%81
|
||||
* @param string $domain
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getCredentials($domain = "") {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'domain' => $domain
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语音
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_record-%E8%8E%B7%E5%8F%96%E8%AF%AD%E9%9F%B3
|
||||
* @param $file
|
||||
* @param $out_format
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getRecord($file, $out_format) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'file' => $file,
|
||||
'out_format' => $out_format
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_image-%E8%8E%B7%E5%8F%96%E5%9B%BE%E7%89%87
|
||||
* @param $file
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getImage($file) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'file' => $file
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否可以发送图片
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#can_send_image-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E5%9B%BE%E7%89%87
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function canSendImage() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否可以发送语音
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#can_send_record-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E8%AF%AD%E9%9F%B3
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function canSendRecord() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取运行状态
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_status-%E8%8E%B7%E5%8F%96%E8%BF%90%E8%A1%8C%E7%8A%B6%E6%80%81
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getStatus() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本信息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_version_info-%E8%8E%B7%E5%8F%96%E7%89%88%E6%9C%AC%E4%BF%A1%E6%81%AF
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getVersionInfo() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重启 OneBot 实现
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_restart-%E9%87%8D%E5%90%AF-onebot-%E5%AE%9E%E7%8E%B0
|
||||
* @param int $delay
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setRestart($delay = 0) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'delay' => $delay
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理缓存
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#clean_cache-%E6%B8%85%E7%90%86%E7%BC%93%E5%AD%98
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function cleanCache() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
public function callExtendedAPI($action, $params = []) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $action,
|
||||
'params' => $params
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
private function getActionName(string $method) {
|
||||
$prefix = ($this->prefix == self::API_ASYNC ? '_async' : ($this->prefix == self::API_RATE_LIMITED ? '_rate_limited' : ''));
|
||||
$func_name = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $method));
|
||||
return $func_name . $prefix;
|
||||
}
|
||||
}
|
||||
@@ -54,14 +54,14 @@ class TuringAPI
|
||||
if ($api_return["intent"]["code"] == 4003) {
|
||||
return "哎呀,我刚才有点走神了,可能忘记你说什么了,可以重说一遍吗";
|
||||
}
|
||||
Console::error("图灵机器人发送错误!\n错误原始内容:" . $origin . "\n来自:" . $user_id . "\n错误信息:" . $status);
|
||||
Console::error(zm_internal_errcode("E00038") . "图灵机器人发送错误!\n错误原始内容:" . $origin . "\n来自:" . $user_id . "\n错误信息:" . $status);
|
||||
//echo json_encode($r, 128|256);
|
||||
return "哎呀,我刚才有点走神了,要不一会儿换一种问题试试?";
|
||||
}
|
||||
$result = $api_return["results"];
|
||||
//Console::info(Console::setColor(json_encode($result, 128 | 256), "green"));
|
||||
$final = "";
|
||||
foreach ($result as $k => $v) {
|
||||
foreach ($result as $v) {
|
||||
switch ($v["resultType"]) {
|
||||
case "url":
|
||||
$final .= "\n" . $v["values"]["url"];
|
||||
|
||||
@@ -5,687 +5,13 @@
|
||||
|
||||
namespace ZM\API;
|
||||
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\ConnectionManager\ManagerGM;
|
||||
use ZM\Exception\RobotNotFoundException;
|
||||
|
||||
/**
|
||||
* Class ZMRobot
|
||||
* @package ZM\Utils
|
||||
* @since 1.2
|
||||
* @version V11
|
||||
*/
|
||||
class ZMRobot
|
||||
class ZMRobot extends OneBotV11
|
||||
{
|
||||
use CQAPI;
|
||||
|
||||
const API_ASYNC = 1;
|
||||
const API_NORMAL = 0;
|
||||
const API_RATE_LIMITED = 2;
|
||||
|
||||
/** @var ConnectionObject|null */
|
||||
private $connection;
|
||||
|
||||
private $callback = true;
|
||||
private $prefix = 0;
|
||||
|
||||
/**
|
||||
* @param $robot_id
|
||||
* @return ZMRobot
|
||||
* @throws RobotNotFoundException
|
||||
*/
|
||||
public static function get($robot_id) {
|
||||
$r = ManagerGM::getAllByName('qq');
|
||||
foreach ($r as $v) {
|
||||
if ($v->getOption('connect_id') == $robot_id) return new ZMRobot($v);
|
||||
}
|
||||
throw new RobotNotFoundException("机器人 " . $robot_id . " 未连接到框架!");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ZMRobot
|
||||
* @throws RobotNotFoundException
|
||||
*/
|
||||
public static function getRandom() {
|
||||
$r = ManagerGM::getAllByName('qq');
|
||||
if ($r == []) throw new RobotNotFoundException("没有任何机器人连接到框架!");
|
||||
return new ZMRobot($r[array_rand($r)]);
|
||||
}
|
||||
|
||||
public static function getFirst() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ZMRobot[]
|
||||
*/
|
||||
public static function getAllRobot() {
|
||||
$r = ManagerGM::getAllByName('qq');
|
||||
$obj = [];
|
||||
foreach ($r as $v) {
|
||||
$obj[] = new ZMRobot($v);
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function __construct(ConnectionObject $connection) {
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function setCallback($callback = true) {
|
||||
$this->callback = $callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPrefix($prefix = self::API_NORMAL) {
|
||||
$this->prefix = $prefix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSelfId() {
|
||||
return $this->connection->getOption('connect_id');
|
||||
}
|
||||
|
||||
/* 下面是 OneBot 标准的 V11 公开 API */
|
||||
|
||||
/**
|
||||
* 发送私聊消息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_private_msg-%E5%8F%91%E9%80%81%E7%A7%81%E8%81%8A%E6%B6%88%E6%81%AF
|
||||
* @param $user_id
|
||||
* @param $message
|
||||
* @param bool $auto_escape
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendPrivateMsg($user_id, $message, $auto_escape = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'user_id' => $user_id,
|
||||
'message' => $message,
|
||||
'auto_escape' => $auto_escape
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送群消息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_group_msg-%E5%8F%91%E9%80%81%E7%BE%A4%E6%B6%88%E6%81%AF
|
||||
* @param $group_id
|
||||
* @param $message
|
||||
* @param bool $auto_escape
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendGroupMsg($group_id, $message, $auto_escape = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'message' => $message,
|
||||
'auto_escape' => $auto_escape
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_msg-%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF
|
||||
* @param $message_type
|
||||
* @param $target_id
|
||||
* @param $message
|
||||
* @param bool $auto_escape
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendMsg($message_type, $target_id, $message, $auto_escape = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'message_type' => $message_type,
|
||||
($message_type == 'private' ? 'user' : $message_type) . '_id' => $target_id,
|
||||
'message' => $message,
|
||||
'auto_escape' => $auto_escape
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回消息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#delete_msg-%E6%92%A4%E5%9B%9E%E6%B6%88%E6%81%AF
|
||||
* @param $message_id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function deleteMsg($message_id) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'message_id' => $message_id
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_msg-%E8%8E%B7%E5%8F%96%E6%B6%88%E6%81%AF
|
||||
* @param $message_id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getMsg($message_id) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'message_id' => $message_id
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取合并转发消息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_forward_msg-%E8%8E%B7%E5%8F%96%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E6%B6%88%E6%81%AF
|
||||
* @param $id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getForwardMsg($id) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'id' => $id
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送好友赞
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_like-%E5%8F%91%E9%80%81%E5%A5%BD%E5%8F%8B%E8%B5%9E
|
||||
* @param $user_id
|
||||
* @param int $times
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function sendLike($user_id, $times = 1) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'user_id' => $user_id,
|
||||
'times' => $times
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组踢人
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_kick-%E7%BE%A4%E7%BB%84%E8%B8%A2%E4%BA%BA
|
||||
* @param $group_id
|
||||
* @param $user_id
|
||||
* @param bool $reject_add_request
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupKick($group_id, $user_id, $reject_add_request = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
'reject_add_request' => $reject_add_request
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组单人禁言
|
||||
* @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 $group_id
|
||||
* @param $user_id
|
||||
* @param $duration
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupBan($group_id, $user_id, $duration = 1800) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
'duration' => $duration
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组匿名用户禁言
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_anonymous_ban-%E7%BE%A4%E7%BB%84%E5%8C%BF%E5%90%8D%E7%94%A8%E6%88%B7%E7%A6%81%E8%A8%80
|
||||
* @param $group_id
|
||||
* @param $anonymous_or_flag
|
||||
* @param int $duration
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAnonymousBan($group_id, $anonymous_or_flag, $duration = 1800) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
(is_string($anonymous_or_flag) ? 'flag' : 'anonymous') => $anonymous_or_flag,
|
||||
'duration' => $duration
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组全员禁言
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_whole_ban-%E7%BE%A4%E7%BB%84%E5%85%A8%E5%91%98%E7%A6%81%E8%A8%80
|
||||
* @param $group_id
|
||||
* @param bool $enable
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupWholeBan($group_id, $enable = true) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'enable' => $enable
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组设置管理员
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_admin-%E7%BE%A4%E7%BB%84%E8%AE%BE%E7%BD%AE%E7%AE%A1%E7%90%86%E5%91%98
|
||||
* @param $group_id
|
||||
* @param $user_id
|
||||
* @param bool $enable
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAdmin($group_id, $user_id, $enable = true) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
'enable' => $enable
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组匿名
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_anonymous-%E7%BE%A4%E7%BB%84%E5%8C%BF%E5%90%8D
|
||||
* @param $group_id
|
||||
* @param bool $enable
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAnonymous($group_id, $enable = true) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'enable' => $enable
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置群名片(群备注)
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_card-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D%E7%89%87%E7%BE%A4%E5%A4%87%E6%B3%A8
|
||||
* @param $group_id
|
||||
* @param $user_id
|
||||
* @param string $card
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupCard($group_id, $user_id, $card = "") {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
'card' => $card
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置群名
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_name-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D
|
||||
* @param $group_id
|
||||
* @param $group_name
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupName($group_id, $group_name) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'group_name' => $group_name
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出群组
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_leave-%E9%80%80%E5%87%BA%E7%BE%A4%E7%BB%84
|
||||
* @param $group_id
|
||||
* @param bool $is_dismiss
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupLeave($group_id, $is_dismiss = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'is_dismiss' => $is_dismiss
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置群组专属头衔
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_special_title-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E7%BB%84%E4%B8%93%E5%B1%9E%E5%A4%B4%E8%A1%94
|
||||
* @param $group_id
|
||||
* @param $user_id
|
||||
* @param string $special_title
|
||||
* @param int $duration
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupSpecialTitle($group_id, $user_id, $special_title = "", $duration = -1) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
'special_title' => $special_title,
|
||||
'duration' => $duration
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理加好友请求
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_friend_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E5%A5%BD%E5%8F%8B%E8%AF%B7%E6%B1%82
|
||||
* @param $flag
|
||||
* @param bool $approve
|
||||
* @param string $remark
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setFriendAddRequest($flag, $approve = true, $remark = "") {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'flag' => $flag,
|
||||
'approve' => $approve,
|
||||
'remark' => $remark
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理加群请求/邀请
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E7%BE%A4%E8%AF%B7%E6%B1%82%E9%82%80%E8%AF%B7
|
||||
* @param $flag
|
||||
* @param $sub_type
|
||||
* @param bool $approve
|
||||
* @param string $reason
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setGroupAddRequest($flag, $sub_type, $approve = true, $reason = "") {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'flag' => $flag,
|
||||
'sub_type' => $sub_type,
|
||||
'approve' => $approve,
|
||||
'reason' => $reason
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录号信息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_login_info-%E8%8E%B7%E5%8F%96%E7%99%BB%E5%BD%95%E5%8F%B7%E4%BF%A1%E6%81%AF
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getLoginInfo() {
|
||||
return $this->processAPI($this->connection, ['action' => $this->getActionName(__FUNCTION__)], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取陌生人信息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_stranger_info-%E8%8E%B7%E5%8F%96%E9%99%8C%E7%94%9F%E4%BA%BA%E4%BF%A1%E6%81%AF
|
||||
* @param $user_id
|
||||
* @param bool $no_cache
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getStrangerInfo($user_id, $no_cache = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'user_id' => $user_id,
|
||||
'no_cache' => $no_cache
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取好友列表
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_friend_list-%E8%8E%B7%E5%8F%96%E5%A5%BD%E5%8F%8B%E5%88%97%E8%A1%A8
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getFriendList() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群信息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E4%BF%A1%E6%81%AF
|
||||
* @param $group_id
|
||||
* @param bool $no_cache
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupInfo($group_id, $no_cache = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'no_cache' => $no_cache
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群列表
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E5%88%97%E8%A1%A8
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupList() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群成员信息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_member_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E4%BF%A1%E6%81%AF
|
||||
* @param $group_id
|
||||
* @param $user_id
|
||||
* @param bool $no_cache
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupMemberInfo($group_id, $user_id, $no_cache = false) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'user_id' => $user_id,
|
||||
'no_cache' => $no_cache
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群成员列表
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_member_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E5%88%97%E8%A1%A8
|
||||
* @param $group_id
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupMemberList($group_id) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群荣誉信息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_honor_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E8%8D%A3%E8%AA%89%E4%BF%A1%E6%81%AF
|
||||
* @param $group_id
|
||||
* @param $type
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getGroupHonorInfo($group_id, $type) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'group_id' => $group_id,
|
||||
'type' => $type
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 CSRF Token
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_csrf_token-%E8%8E%B7%E5%8F%96-csrf-token
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getCsrfToken() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 QQ 相关接口凭证
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_credentials-%E8%8E%B7%E5%8F%96-qq-%E7%9B%B8%E5%85%B3%E6%8E%A5%E5%8F%A3%E5%87%AD%E8%AF%81
|
||||
* @param string $domain
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getCredentials($domain = "") {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'domain' => $domain
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语音
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_record-%E8%8E%B7%E5%8F%96%E8%AF%AD%E9%9F%B3
|
||||
* @param $file
|
||||
* @param $out_format
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getRecord($file, $out_format) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'file' => $file,
|
||||
'out_format' => $out_format
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_image-%E8%8E%B7%E5%8F%96%E5%9B%BE%E7%89%87
|
||||
* @param $file
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getImage($file) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'file' => $file
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否可以发送图片
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#can_send_image-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E5%9B%BE%E7%89%87
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function canSendImage() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否可以发送语音
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#can_send_record-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E8%AF%AD%E9%9F%B3
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function canSendRecord() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取运行状态
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_status-%E8%8E%B7%E5%8F%96%E8%BF%90%E8%A1%8C%E7%8A%B6%E6%80%81
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getStatus() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本信息
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_version_info-%E8%8E%B7%E5%8F%96%E7%89%88%E6%9C%AC%E4%BF%A1%E6%81%AF
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function getVersionInfo() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重启 OneBot 实现
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_restart-%E9%87%8D%E5%90%AF-onebot-%E5%AE%9E%E7%8E%B0
|
||||
* @param int $delay
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function setRestart($delay = 0) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__),
|
||||
'params' => [
|
||||
'delay' => $delay
|
||||
]
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理缓存
|
||||
* @link https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#clean_cache-%E6%B8%85%E7%90%86%E7%BC%93%E5%AD%98
|
||||
* @return array|bool|null
|
||||
*/
|
||||
public function cleanCache() {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $this->getActionName(__FUNCTION__)
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
public function callExtendedAPI($action, $params = []) {
|
||||
return $this->processAPI($this->connection, [
|
||||
'action' => $action,
|
||||
'params' => $params
|
||||
], $this->callback);
|
||||
}
|
||||
|
||||
private function getActionName(string $method) {
|
||||
$prefix = ($this->prefix == self::API_ASYNC ? '_async' : ($this->prefix == self::API_RATE_LIMITED ? '_rate_limited' : ''));
|
||||
$func_name = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $method));
|
||||
return $func_name . $prefix;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ use ZM\Annotation\Http\MiddlewareClass;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
use ZM\Annotation\Module\Closed;
|
||||
use ZM\Http\RouteManager;
|
||||
use ZM\Utils\Manager\RouteManager;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
class AnnotationParser
|
||||
{
|
||||
@@ -53,7 +54,7 @@ class AnnotationParser
|
||||
public function registerMods() {
|
||||
foreach ($this->path_list as $path) {
|
||||
Console::debug("parsing annotation in " . $path[0]);
|
||||
$all_class = getAllClasses($path[0], $path[1]);
|
||||
$all_class = ZMUtil::getClassesPsr4($path[0], $path[1]);
|
||||
$this->reader = new AnnotationReader();
|
||||
foreach ($all_class as $v) {
|
||||
Console::debug("正在检索 " . $v);
|
||||
@@ -88,7 +89,7 @@ class AnnotationParser
|
||||
}
|
||||
|
||||
|
||||
foreach ($this->annotation_map[$v]["class_annotations"] as $ks => $vs) {
|
||||
foreach ($this->annotation_map[$v]["class_annotations"] as $vs) {
|
||||
$vs->class = $v;
|
||||
|
||||
//预处理1:将适用于每一个函数的注解到类注解重新注解到每个函数下面
|
||||
@@ -132,12 +133,12 @@ class AnnotationParser
|
||||
|
||||
public function generateAnnotationEvents() {
|
||||
$o = [];
|
||||
foreach ($this->annotation_map as $module => $obj) {
|
||||
foreach ($this->annotation_map as $obj) {
|
||||
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 $methods_annotations) {
|
||||
foreach ($methods_annotations as $annotation) {
|
||||
$o[get_class($annotation)][] = $annotation;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Console\TermColor;
|
||||
use ZM\Utils\DataProvider;
|
||||
|
||||
class BuildCommand extends Command
|
||||
{
|
||||
@@ -21,28 +22,28 @@ class BuildCommand extends Command
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("Build an \".phar\" file | 将项目构建一个phar包");
|
||||
$this->setHelp("此功能将会把炸毛框架的模块打包为\".phar\",供发布和执行。");
|
||||
$this->setHelp("此功能将会把整个项目打包为phar");
|
||||
$this->addOption("target", "D", InputOption::VALUE_REQUIRED, "Output Directory | 指定输出目录");
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$this->output = $output;
|
||||
$target_dir = $input->getOption("target") ?? (__DIR__ . '/../../../resources/');
|
||||
$target_dir = $input->getOption("target") ?? (WORKING_DIR);
|
||||
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) . zm_internal_errcode("E00039") . "Error: No such file or directory (" . $target_dir . ")" . TermColor::RESET);
|
||||
return 1;
|
||||
}
|
||||
$output->writeln("Target: " . $target_dir . " , Version: " . ($version = json_decode(file_get_contents(__DIR__ . "/../../../composer.json"), true)["version"]));
|
||||
$output->writeln("Target: " . $target_dir);
|
||||
if (mb_substr($target_dir, -1, 1) !== '/') $target_dir .= "/";
|
||||
if (ini_get('phar.readonly') == 1) {
|
||||
$output->writeln(TermColor::color8(31) . "You need to set \"phar.readonly\" to \"Off\"!");
|
||||
$output->writeln(TermColor::color8(31) . zm_internal_errcode("E00040") . "You need to set \"phar.readonly\" to \"Off\"!");
|
||||
$output->writeln(TermColor::color8(31) . "See: https://stackoverflow.com/questions/34667606/cant-enable-phar-writing");
|
||||
return 1;
|
||||
}
|
||||
if (!is_dir($target_dir)) {
|
||||
$output->writeln(TermColor::color8(31) . "Error: No such file or directory ($target_dir)" . TermColor::RESET);
|
||||
$output->writeln(TermColor::color8(31) . zm_internal_errcode("E00039") . "Error: No such file or directory ($target_dir)" . TermColor::RESET);
|
||||
return 1;
|
||||
}
|
||||
$filename = "server.phar";
|
||||
@@ -55,19 +56,43 @@ class BuildCommand extends Command
|
||||
@unlink($target_dir . $filename);
|
||||
$phar = new Phar($target_dir . $filename);
|
||||
$phar->startBuffering();
|
||||
$src = realpath(__DIR__ . '/../../zhamao-framework/');
|
||||
$hello = file_get_contents($src . '/src/Module/Example/Hello.php');
|
||||
$middleware = file_get_contents($src . '/src/Module/Middleware/TimerMiddleware.php');
|
||||
unlink($src . '/src/Module/Example/Hello.php');
|
||||
unlink($src . '/src/Module/Middleware/TimerMiddleware.php');
|
||||
$phar->buildFromDirectory($src);
|
||||
$phar->addFromString('tmp/Hello.php.bak', $hello);
|
||||
$phar->addFromString('tmp/TimerMiddleware.php.bak', $middleware);
|
||||
//$phar->compressFiles(Phar::GZ);
|
||||
$phar->setStub($phar->createDefaultStub('phar-starter.php'));
|
||||
|
||||
$allow_dir = ["bin", "config", "resources", "src", "vendor", "composer.json", "README.md", "zhamao"];
|
||||
|
||||
$archive_dir = DataProvider::getSourceRootDir();
|
||||
$scan = scandir($archive_dir);
|
||||
if ($scan[0] == ".") {
|
||||
unset($scan[0], $scan[1]);
|
||||
}
|
||||
foreach ($scan as $v) {
|
||||
if (in_array($v, $allow_dir)) {
|
||||
if (is_dir($archive_dir . "/" . $v)) {
|
||||
$this->addDirectory($phar, $archive_dir . "/" . $v, $v);
|
||||
} elseif (is_file($archive_dir . "/" . $v)) {
|
||||
$phar->addFile($archive_dir . "/" . $v, $v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$phar->setStub(
|
||||
"#!/usr/bin/env php\n" .
|
||||
$phar->createDefaultStub(LOAD_MODE == 0 ? "src/entry.php" : "vendor/zhamao/framework/src/entry.php")
|
||||
);
|
||||
$phar->stopBuffering();
|
||||
file_put_contents($src . '/src/Module/Example/Hello.php', $hello);
|
||||
file_put_contents($src . '/src/Module/Middleware/TimerMiddleware.php', $middleware);
|
||||
$this->output->writeln("Successfully built. Location: " . $target_dir . "$filename");
|
||||
}
|
||||
|
||||
private function addDirectory(Phar $phar, $dir, $local_dir) {
|
||||
$o = scandir($dir);
|
||||
if ($o[0] == ".") {
|
||||
unset($o[0], $o[1]);
|
||||
}
|
||||
foreach ($o as $v) {
|
||||
if (is_dir($dir . "/" . $v)) {
|
||||
$this->addDirectory($phar, $dir . "/" . $v, $local_dir . "/" . $v);
|
||||
} elseif (is_file($dir . "/" . $v)) {
|
||||
$phar->addFile($dir . "/" . $v, $local_dir . "/" . $v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command;
|
||||
namespace ZM\Command\Daemon;
|
||||
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
@@ -16,12 +16,12 @@ abstract class DaemonCommand extends Command
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$pid_path = DataProvider::getWorkingDir() . "/.daemon_pid";
|
||||
if (!file_exists($pid_path)) {
|
||||
$output->writeln("<comment>没有检测到正在运行的守护进程!</comment>");
|
||||
$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>");
|
||||
$output->writeln("<comment>未检测到正在运行的守护进程或框架进程!</comment>");
|
||||
unlink($pid_path);
|
||||
die();
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command;
|
||||
namespace ZM\Command\Daemon;
|
||||
|
||||
use Swoole\Process;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command;
|
||||
namespace ZM\Command\Daemon;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command;
|
||||
namespace ZM\Command\Daemon;
|
||||
|
||||
use Swoole\Process;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
@@ -45,13 +45,13 @@ class InitCommand extends Command
|
||||
$info = pathinfo($file);
|
||||
@mkdir($base_path . $info["dirname"], 0777, true);
|
||||
echo "Copying " . $file . PHP_EOL;
|
||||
$package_name = ($version = json_decode(file_get_contents(__DIR__ . "/../../../composer.json"), true)["name"]);
|
||||
$package_name = (json_decode(file_get_contents(__DIR__ . "/../../../composer.json"), true)["name"]);
|
||||
copy($base_path . "/vendor/" . $package_name . $file, $base_path . $file);
|
||||
} else {
|
||||
echo "Skipping " . $file . " , file exists." . PHP_EOL;
|
||||
}
|
||||
}
|
||||
chmod($base_path."/zhamao", 0755);
|
||||
chmod($base_path . "/zhamao", 0755);
|
||||
$autoload = [
|
||||
"psr-4" => [
|
||||
"Module\\" => "src/Module",
|
||||
@@ -69,7 +69,7 @@ class InitCommand extends Command
|
||||
foreach ($autoload["psr-4"] as $k => $v) {
|
||||
if (!isset($composer["autoload"]["psr-4"][$k])) $composer["autoload"]["psr-4"][$k] = $v;
|
||||
}
|
||||
foreach ($autoload["files"] as $k => $v) {
|
||||
foreach ($autoload["files"] as $v) {
|
||||
if (!in_array($v, $composer["autoload"]["files"])) $composer["autoload"]["files"][] = $v;
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class InitCommand extends Command
|
||||
exec("composer dump-autoload");
|
||||
echo PHP_EOL;
|
||||
} else {
|
||||
echo("Error occurred. Please check your updates.\n");
|
||||
echo(zm_internal_errcode("E00041") . "Error occurred. Please check your updates.\n");
|
||||
return 1;
|
||||
}
|
||||
$output->writeln("<info>Done!</info>");
|
||||
@@ -99,7 +99,7 @@ class InitCommand extends Command
|
||||
}
|
||||
}
|
||||
}
|
||||
$output->writeln("initialization must be started with composer-project mode!");
|
||||
$output->writeln(zm_internal_errcode("E00042") . "initialization must be started with composer-project mode!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
75
src/ZM/Command/Module/ModuleListCommand.php
Normal file
75
src/ZM/Command/Module/ModuleListCommand.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command\Module;
|
||||
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\Manager\ModuleManager;
|
||||
|
||||
class ModuleListCommand extends Command
|
||||
{
|
||||
// the name of the command (the part after "bin/console")
|
||||
protected static $defaultName = 'module:list';
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("Build an \".phar\" file | 将项目构建一个phar包");
|
||||
$this->setHelp("此功能将会把炸毛框架的模块打包为\".phar\",供发布和执行。");
|
||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
||||
ZMConfig::setEnv($args["env"] ?? "");
|
||||
if (ZMConfig::get("global") === false) {
|
||||
die ("Global config load failed: " . ZMConfig::$last_error . "\nPlease init first!\n");
|
||||
}
|
||||
|
||||
//定义常量
|
||||
include_once DataProvider::getFrameworkRootDir() . "/src/ZM/global_defines.php";
|
||||
|
||||
Console::init(
|
||||
ZMConfig::get("global", "info_level") ?? 2,
|
||||
null,
|
||||
$args["log-theme"] ?? "default",
|
||||
($o = ZMConfig::get("console_color")) === false ? [] : $o
|
||||
);
|
||||
|
||||
$timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai";
|
||||
date_default_timezone_set($timezone);
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$list = ModuleManager::getConfiguredModules();
|
||||
|
||||
foreach ($list as $v) {
|
||||
echo "[" . Console::setColor($v["name"], "green") . "]" . PHP_EOL;
|
||||
$out_list = ["类型" => "source"];
|
||||
if (isset($v["version"])) $out_list["版本"] = $v["version"];
|
||||
if (isset($v["description"])) $out_list["描述"] = $v["description"];
|
||||
$out_list["目录"] = str_replace(DataProvider::getSourceRootDir() . "/", "", $v["module-path"]);
|
||||
$this->printList($out_list);
|
||||
}
|
||||
$list = ModuleManager::getPackedModules();
|
||||
foreach ($list as $v) {
|
||||
echo "[" . Console::setColor($v["name"], "gold") . "]" . PHP_EOL;
|
||||
$out_list = ["类型" => "archive(phar)"];
|
||||
if (isset($v["module-config"]["version"])) $out_list["版本"] = $v["module-config"]["version"];
|
||||
if (isset($v["module-config"]["description"])) $out_list["描述"] = $v["module-config"]["description"];
|
||||
$out_list["位置"] = str_replace(DataProvider::getSourceRootDir() . "/", "", $v["phar-path"]);
|
||||
$this->printList($out_list);
|
||||
}
|
||||
if ($list === []) {
|
||||
echo Console::setColor("没有发现已编写打包配置文件(zm.json)的模块和已打包且装载的模块!", "yellow") . PHP_EOL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function printList($list) {
|
||||
foreach ($list as $k => $v) {
|
||||
echo "\t" . $k . ": " . Console::setColor($v, "yellow") . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
src/ZM/Command/Module/ModulePackCommand.php
Normal file
59
src/ZM/Command/Module/ModulePackCommand.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command\Module;
|
||||
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\Manager\ModuleManager;
|
||||
|
||||
class ModulePackCommand extends Command
|
||||
{
|
||||
// the name of the command (the part after "bin/console")
|
||||
protected static $defaultName = 'module:pack';
|
||||
|
||||
protected function configure() {
|
||||
$this->addArgument("module-name", InputArgument::REQUIRED);
|
||||
$this->setDescription("Build an \".phar\" file | 将项目构建一个phar包");
|
||||
$this->setHelp("此功能将会把炸毛框架的模块打包为\".phar\",供发布和执行。");
|
||||
$this->addOption("target", "D", InputOption::VALUE_REQUIRED, "Output Directory | 指定输出目录");
|
||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
||||
ZMConfig::setEnv($args["env"] ?? "");
|
||||
if (ZMConfig::get("global") === false) {
|
||||
die ("Global config load failed: " . ZMConfig::$last_error . "\nPlease init first!\n");
|
||||
}
|
||||
|
||||
//定义常量
|
||||
include_once DataProvider::getFrameworkRootDir()."/src/ZM/global_defines.php";
|
||||
|
||||
Console::init(
|
||||
ZMConfig::get("global", "info_level") ?? 2,
|
||||
null,
|
||||
$args["log-theme"] ?? "default",
|
||||
($o = ZMConfig::get("console_color")) === false ? [] : $o
|
||||
);
|
||||
|
||||
$timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai";
|
||||
date_default_timezone_set($timezone);
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$list = ModuleManager::getConfiguredModules();
|
||||
if (!isset($list[$input->getArgument("module-name")])) {
|
||||
$output->writeln("<error>不存在模块 ".$input->getArgument("module-name")." !</error>");
|
||||
return 1;
|
||||
}
|
||||
$result = ModuleManager::packModule($list[$input->getArgument("module-name")]);
|
||||
if ($result) Console::success("打包完成!");
|
||||
else Console::error("打包失败!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
57
src/ZM/Command/Module/ModuleUnpackCommand.php
Normal file
57
src/ZM/Command/Module/ModuleUnpackCommand.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command\Module;
|
||||
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\Manager\ModuleManager;
|
||||
|
||||
class ModuleUnpackCommand extends Command
|
||||
{
|
||||
// the name of the command (the part after "bin/console")
|
||||
protected static $defaultName = 'module:unpack';
|
||||
|
||||
protected function configure() {
|
||||
$this->addArgument("module-name", InputArgument::REQUIRED);
|
||||
$this->setDescription("Unpack a phar module into src directory");
|
||||
$this->setHelp("此功能将phar格式的模块包解包到src目录下。");
|
||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
||||
ZMConfig::setEnv($args["env"] ?? "");
|
||||
if (ZMConfig::get("global") === false) {
|
||||
die ("Global config load failed: " . ZMConfig::$last_error . "\nPlease init first!\n");
|
||||
}
|
||||
|
||||
//定义常量
|
||||
include_once DataProvider::getFrameworkRootDir()."/src/ZM/global_defines.php";
|
||||
|
||||
Console::init(
|
||||
ZMConfig::get("global", "info_level") ?? 2,
|
||||
null,
|
||||
$args["log-theme"] ?? "default",
|
||||
($o = ZMConfig::get("console_color")) === false ? [] : $o
|
||||
);
|
||||
|
||||
$timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai";
|
||||
date_default_timezone_set($timezone);
|
||||
// ...
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$list = ModuleManager::getPackedModules();
|
||||
if (!isset($list[$input->getArgument("module-name")])) {
|
||||
$output->writeln("<error>不存在打包的模块 ".$input->getArgument("module-name")." !</error>");
|
||||
return 1;
|
||||
}
|
||||
$result = ModuleManager::unpackModule($list[$input->getArgument("module-name")]);
|
||||
if ($result) Console::success("解压完成!");
|
||||
else Console::error("解压失败!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ class PureHttpCommand extends Command
|
||||
$output->writeln("<error>Directory error(" . ($input->getArgument('dir') ?? '.') . "): no such file or directory.</error>");
|
||||
return self::FAILURE;
|
||||
}
|
||||
ZMConfig::setDirectory(DataProvider::getWorkingDir() . '/config');
|
||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
||||
$global = ZMConfig::get("global");
|
||||
$host = $input->getOption("host") ?? $global["host"];
|
||||
$port = $input->getOption("port") ?? $global["port"];
|
||||
|
||||
@@ -23,6 +23,7 @@ class RunServerCommand extends Command
|
||||
new InputOption("log-error", null, null, "调整消息等级到error (log-level=0)"),
|
||||
new InputOption("log-theme", null, InputOption::VALUE_REQUIRED, "改变终端的主题配色"),
|
||||
new InputOption("disable-console-input", null, null, "禁止终端输入内容 (废弃)"),
|
||||
new InputOption("interact", null, null, "打开终端输入"),
|
||||
new InputOption("remote-terminal", null, null, "启用远程终端,配置使用global.php中的"),
|
||||
new InputOption("disable-coroutine", null, null, "关闭协程Hook"),
|
||||
new InputOption("daemon", null, null, "以守护进程的方式运行框架"),
|
||||
@@ -32,7 +33,8 @@ class RunServerCommand extends Command
|
||||
new InputOption("show-php-ver", null, null, "启动时显示PHP和Swoole版本"),
|
||||
new InputOption("env", null, InputOption::VALUE_REQUIRED, "设置环境类型 (production, development, staging)"),
|
||||
new InputOption("disable-safe-exit", null, null, "关闭安全退出(关闭后按CtrlC时直接杀死进程)"),
|
||||
new InputOption("preview", null, null, "只显示参数,不启动服务器")
|
||||
new InputOption("preview", null, null, "只显示参数,不启动服务器"),
|
||||
new InputOption("force-load-module", null, InputOption::VALUE_OPTIONAL, "强制打包状态下加载模块(使用英文逗号分割多个)")
|
||||
]);
|
||||
$this->setDescription("Run zhamao-framework | 启动框架");
|
||||
$this->setHelp("直接运行可以启动");
|
||||
|
||||
@@ -29,11 +29,8 @@ class SystemdCommand extends Command
|
||||
$s .= "\nUser=" . exec("whoami");
|
||||
$s .= "\nGroup=" . exec("groups | awk '{print $1}'");
|
||||
$s .= "\nWorkingDirectory=" . getcwd();
|
||||
if (LOAD_MODE == 1) {
|
||||
$s .= "\nExecStart=" . getcwd() . "/vendor/bin/start server";
|
||||
} else {
|
||||
$s .= "\nExecStart=" . getcwd() . "/bin/start server";
|
||||
}
|
||||
global $argv;
|
||||
$s .= "\nExecStart=".PHP_BINARY." {$argv[0]} server";
|
||||
$s .= "\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n";
|
||||
@mkdir(getcwd() . "/resources/");
|
||||
file_put_contents(getcwd() . "/resources/zhamao.service", $s);
|
||||
|
||||
@@ -5,22 +5,28 @@ namespace ZM;
|
||||
|
||||
|
||||
use Exception;
|
||||
use Phar;
|
||||
use ZM\Command\BuildCommand;
|
||||
use ZM\Command\CheckConfigCommand;
|
||||
use ZM\Command\DaemonReloadCommand;
|
||||
use ZM\Command\DaemonStatusCommand;
|
||||
use ZM\Command\DaemonStopCommand;
|
||||
use ZM\Command\Daemon\DaemonReloadCommand;
|
||||
use ZM\Command\Daemon\DaemonStatusCommand;
|
||||
use ZM\Command\Daemon\DaemonStopCommand;
|
||||
use ZM\Command\InitCommand;
|
||||
use ZM\Command\Module\ModuleListCommand;
|
||||
use ZM\Command\Module\ModulePackCommand;
|
||||
use ZM\Command\Module\ModuleUnpackCommand;
|
||||
use ZM\Command\PureHttpCommand;
|
||||
use ZM\Command\RunServerCommand;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Command\SystemdCommand;
|
||||
use ZM\Console\Console;
|
||||
|
||||
class ConsoleApplication extends Application
|
||||
{
|
||||
const VERSION_ID = 407;
|
||||
const VERSION = "2.5.0";
|
||||
const VERSION_ID = 408;
|
||||
const VERSION = "2.5.0-b1";
|
||||
|
||||
public function __construct(string $name = 'UNKNOWN') {
|
||||
define("ZM_VERSION_ID", self::VERSION_ID);
|
||||
@@ -32,10 +38,18 @@ class ConsoleApplication extends Application
|
||||
$this->selfCheck();
|
||||
|
||||
define("WORKING_DIR", getcwd());
|
||||
define("LOAD_MODE", is_dir(WORKING_DIR . "/src/ZM") ? 0 : 1);
|
||||
define("FRAMEWORK_ROOT_DIR", realpath(__DIR__ . "/../../"));
|
||||
if (Phar::running() !== "") {
|
||||
echo "* Running in phar mode.\n";
|
||||
define("SOURCE_ROOT_DIR", Phar::running());
|
||||
define("LOAD_MODE", is_dir(SOURCE_ROOT_DIR . "/src/ZM") ? 0 : 1);
|
||||
define("FRAMEWORK_ROOT_DIR", LOAD_MODE == 1 ? (SOURCE_ROOT_DIR . "/vendor/zhamao/framework") : SOURCE_ROOT_DIR);
|
||||
} else {
|
||||
define("SOURCE_ROOT_DIR", WORKING_DIR);
|
||||
define("LOAD_MODE", is_dir(SOURCE_ROOT_DIR . "/src/ZM") ? 0 : 1);
|
||||
define("FRAMEWORK_ROOT_DIR", realpath(__DIR__ . "/../../"));
|
||||
}
|
||||
if (LOAD_MODE == 0) {
|
||||
$composer = json_decode(file_get_contents(WORKING_DIR . "/composer.json"), true);
|
||||
$composer = json_decode(file_get_contents(SOURCE_ROOT_DIR . "/composer.json"), true);
|
||||
if (!isset($composer["autoload"]["psr-4"]["Module\\"])) {
|
||||
echo "框架源码模式需要在autoload文件中添加Module目录为自动加载,是否添加?[Y/n] ";
|
||||
$r = strtolower(trim(fgets(STDIN)));
|
||||
@@ -47,7 +61,7 @@ class ConsoleApplication extends Application
|
||||
echo "成功添加!请运行 composer dump-autoload !\n";
|
||||
exit(0);
|
||||
} else {
|
||||
echo "添加失败!请按任意键继续!";
|
||||
echo zm_internal_errcode("E00006") . "添加失败!请按任意键继续!";
|
||||
fgets(STDIN);
|
||||
exit(1);
|
||||
}
|
||||
@@ -62,13 +76,19 @@ class ConsoleApplication extends Application
|
||||
new DaemonReloadCommand(),
|
||||
new DaemonStopCommand(),
|
||||
new RunServerCommand(), //运行主服务的指令控制器
|
||||
new InitCommand(), //初始化用的,用于项目初始化和phar初始化
|
||||
new PureHttpCommand(), //纯HTTP服务器指令
|
||||
new SystemdCommand()
|
||||
]);
|
||||
if (LOAD_MODE === 1) {
|
||||
$this->add(new CheckConfigCommand());
|
||||
}
|
||||
if (Phar::running() === "") {
|
||||
$this->add(new BuildCommand());
|
||||
$this->add(new InitCommand());
|
||||
$this->add(new ModulePackCommand());
|
||||
$this->add(new ModuleListCommand());
|
||||
$this->add(new ModuleUnpackCommand());
|
||||
}
|
||||
if (!empty($with_default_cmd)) {
|
||||
$this->setDefaultCommand($with_default_cmd);
|
||||
}
|
||||
@@ -84,14 +104,20 @@ class ConsoleApplication extends Application
|
||||
try {
|
||||
return parent::run($input, $output);
|
||||
} catch (Exception $e) {
|
||||
die("{$e->getMessage()} at {$e->getFile()}({$e->getLine()})");
|
||||
die(zm_internal_errcode("E00005") . "{$e->getMessage()} at {$e->getFile()}({$e->getLine()})");
|
||||
}
|
||||
}
|
||||
|
||||
private function selfCheck(): bool {
|
||||
if (!extension_loaded("swoole")) die("Can not find swoole extension.\nSee: https://github.com/zhamao-robot/zhamao-framework/issues/19\n");
|
||||
if (version_compare(SWOOLE_VERSION, "4.5.0") == -1) die("You must install swoole version >= 4.5.0 !");
|
||||
if (version_compare(PHP_VERSION, "7.2") == -1) die("PHP >= 7.2 required.");
|
||||
return true;
|
||||
/**
|
||||
* 启动炸毛前要做的环境检查项目
|
||||
*/
|
||||
private function selfCheck(): void {
|
||||
if (!extension_loaded("swoole")) die(zm_internal_errcode("E00001") . "Can not find swoole extension.\n");
|
||||
if (version_compare(SWOOLE_VERSION, "4.5.0") == -1) die(zm_internal_errcode("E00002") . "You must install swoole version >= 4.5.0 !");
|
||||
if (version_compare(PHP_VERSION, "7.2") == -1) die(zm_internal_errcode("E00003") . "PHP >= 7.2 required.");
|
||||
if (version_compare(SWOOLE_VERSION, "4.6.7") < 0 && !extension_loaded("pcntl")) {
|
||||
Console::error(zm_internal_errcode("E00004") . "Swoole 版本必须不低于 4.6.7 或 PHP 安装加载了 pcntl 扩展!");
|
||||
die();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
namespace ZM\Context;
|
||||
|
||||
|
||||
use Co;
|
||||
use Exception;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Http\Request;
|
||||
use Swoole\WebSocket\Frame;
|
||||
use Swoole\WebSocket\Server;
|
||||
@@ -104,8 +104,7 @@ class Context implements ContextInterface
|
||||
* only can used by cq->message event function
|
||||
* @param $msg
|
||||
* @param bool $yield
|
||||
* @return mixed
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
* @return array|bool
|
||||
*/
|
||||
public function reply($msg, $yield = false) {
|
||||
$data = $this->getData();
|
||||
@@ -136,7 +135,7 @@ class Context implements ContextInterface
|
||||
/**
|
||||
* @param $msg
|
||||
* @param false $yield
|
||||
* @return mixed|void
|
||||
* @return void
|
||||
* @throws InterruptException
|
||||
*/
|
||||
public function finalReply($msg, $yield = false) {
|
||||
@@ -170,42 +169,6 @@ class Context implements ContextInterface
|
||||
throw new WaitTimeoutException($this, $timeout_prompt);
|
||||
}
|
||||
return $r["message"];
|
||||
/*
|
||||
$cid = Co::getuid();
|
||||
$api_id = ZMAtomic::get("wait_msg_id")->add(1);
|
||||
$hang = [
|
||||
"coroutine" => $cid,
|
||||
"user_id" => $this->getData()["user_id"],
|
||||
"message" => $this->getData()["message"],
|
||||
"self_id" => $this->getData()["self_id"],
|
||||
"message_type" => $this->getData()["message_type"],
|
||||
"result" => null
|
||||
];
|
||||
if ($hang["message_type"] == "group" || $hang["message_type"] == "discuss") {
|
||||
$hang[$hang["message_type"] . "_id"] = $this->getData()[$this->getData()["message_type"] . "_id"];
|
||||
}
|
||||
SpinLock::lock("wait_api");
|
||||
$hw = LightCacheInside::get("wait_api", "wait_api") ?? [];
|
||||
$hw[$api_id] = $hang;
|
||||
LightCacheInside::set("wait_api", "wait_api", $hw);
|
||||
SpinLock::unlock("wait_api");
|
||||
$id = swoole_timer_after($timeout * 1000, function () use ($api_id, $timeout_prompt) {
|
||||
$r = LightCacheInside::get("wait_api", "wait_api")[$api_id] ?? null;
|
||||
if (is_array($r)) {
|
||||
Co::resume($r["coroutine"]);
|
||||
}
|
||||
});
|
||||
|
||||
Co::suspend();
|
||||
SpinLock::lock("wait_api");
|
||||
$hw = LightCacheInside::get("wait_api", "wait_api") ?? [];
|
||||
$sess = $hw[$api_id];
|
||||
unset($hw[$api_id]);
|
||||
LightCacheInside::set("wait_api", "wait_api", $hw);
|
||||
$result = $sess["result"];
|
||||
if (isset($id)) swoole_timer_clear($id);
|
||||
if ($result === null) throw new WaitTimeoutException($this, $timeout_prompt);
|
||||
return $result;*/
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,7 +232,7 @@ class Context implements ContextInterface
|
||||
|
||||
/** @noinspection PhpMissingReturnTypeInspection */
|
||||
public function cloneFromParent() {
|
||||
set_coroutine_params(self::$context[Co::getPcid()] ?? self::$context[$this->cid]);
|
||||
set_coroutine_params(self::$context[Coroutine::getPcid()] ?? self::$context[$this->cid]);
|
||||
return context();
|
||||
}
|
||||
|
||||
|
||||
@@ -54,9 +54,9 @@ class EventManager
|
||||
try {
|
||||
$dispatcher->dispatchEvent($vss, null);
|
||||
} catch (Exception $e) {
|
||||
Console::error("Uncaught error from TimerTick: " . $e->getMessage() . " at " . $e->getFile() . "({$e->getLine()})");
|
||||
Console::error(zm_internal_errcode("E00034") . "Uncaught error from TimerTick: " . $e->getMessage() . " at " . $e->getFile() . "({$e->getLine()})");
|
||||
} catch (Error $e) {
|
||||
Console::error("Uncaught fatal error from TimerTick: " . $e->getMessage());
|
||||
Console::error(zm_internal_errcode("E00034") . "Uncaught fatal error from TimerTick: " . $e->getMessage());
|
||||
echo Console::setColor($e->getTraceAsString(), "gray");
|
||||
Console::error("Please check your code!");
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
namespace ZM\Event\SwooleEvent;
|
||||
|
||||
|
||||
use Co;
|
||||
use Error;
|
||||
use Exception;
|
||||
use Swoole\Coroutine;
|
||||
use ZM\Annotation\Swoole\OnCloseEvent;
|
||||
use ZM\Annotation\Swoole\OnSwooleEvent;
|
||||
use ZM\Annotation\Swoole\SwooleHandler;
|
||||
@@ -27,7 +27,7 @@ class OnClose implements SwooleEvent
|
||||
{
|
||||
/** @noinspection PhpUnreachableStatementInspection */
|
||||
public function onCall($server, $fd) {
|
||||
unset(Context::$context[Co::getCid()]);
|
||||
unset(Context::$context[Coroutine::getCid()]);
|
||||
Console::debug("Calling Swoole \"close\" event from fd=" . $fd);
|
||||
$conn = ManagerGM::get($fd);
|
||||
if ($conn === null) return;
|
||||
@@ -61,11 +61,11 @@ class OnClose implements SwooleEvent
|
||||
$dispatcher->dispatchEvents($conn);
|
||||
} catch (Exception $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught exception " . get_class($e) . " when calling \"close\": " . $error_msg);
|
||||
Console::error(zm_internal_errcode("E00016") . "Uncaught exception " . get_class($e) . " when calling \"close\": " . $error_msg);
|
||||
Console::trace();
|
||||
} catch (Error $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught " . get_class($e) . " when calling \"close\": " . $error_msg);
|
||||
Console::error(zm_internal_errcode("E00016") . "Uncaught " . get_class($e) . " when calling \"close\": " . $error_msg);
|
||||
Console::trace();
|
||||
}
|
||||
ManagerGM::popConnect($fd);
|
||||
|
||||
@@ -11,6 +11,7 @@ use ZM\Annotation\Swoole\SwooleHandler;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\SwooleEvent;
|
||||
use ZM\Framework;
|
||||
use ZM\Utils\SignalListener;
|
||||
|
||||
/**
|
||||
* Class OnManagerStart
|
||||
@@ -21,9 +22,7 @@ class OnManagerStart implements SwooleEvent
|
||||
{
|
||||
public function onCall(Server $server) {
|
||||
if (!Framework::$argv["disable-safe-exit"]) {
|
||||
pcntl_signal(SIGINT, function () {
|
||||
Console::verbose("Interrupted in manager!");
|
||||
});
|
||||
SignalListener::signalManager();
|
||||
}
|
||||
Console::verbose("进程 Manager 已启动");
|
||||
}
|
||||
|
||||
@@ -56,11 +56,11 @@ class OnMessage implements SwooleEvent
|
||||
//Console::success("Used ".round((microtime(true) - $starttime) * 1000, 3)." ms!");
|
||||
} catch (Exception $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught exception " . get_class($e) . " when calling \"message\": " . $error_msg);
|
||||
Console::error(zm_internal_errcode("E00017") . "Uncaught exception " . get_class($e) . " when calling \"message\": " . $error_msg);
|
||||
Console::trace();
|
||||
} catch (Error $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught " . get_class($e) . " when calling \"message\": " . $error_msg);
|
||||
Console::error(zm_internal_errcode("E00017") . "Uncaught " . get_class($e) . " when calling \"message\": " . $error_msg);
|
||||
Console::trace();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ namespace ZM\Event\SwooleEvent;
|
||||
|
||||
|
||||
use Closure;
|
||||
use Co;
|
||||
use Error;
|
||||
use Exception;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Http\Request;
|
||||
use ZM\Annotation\Swoole\OnOpenEvent;
|
||||
use ZM\Annotation\Swoole\OnSwooleEvent;
|
||||
@@ -30,20 +30,20 @@ class OnOpen implements SwooleEvent
|
||||
/** @noinspection PhpUnreachableStatementInspection */
|
||||
public function onCall($server, Request $request) {
|
||||
Console::debug("Calling Swoole \"open\" event from fd=" . $request->fd);
|
||||
unset(Context::$context[Co::getCid()]);
|
||||
unset(Context::$context[Coroutine::getCid()]);
|
||||
$type = strtolower($request->header["x-client-role"] ?? $request->get["type"] ?? "");
|
||||
$access_token = explode(" ", $request->header["authorization"] ?? "")[1] ?? $request->get["token"] ?? "";
|
||||
$token = ZMConfig::get("global", "access_token");
|
||||
if ($token instanceof Closure) {
|
||||
if (!$token($access_token)) {
|
||||
$server->close($request->fd);
|
||||
Console::warning("Unauthorized access_token: " . $access_token);
|
||||
Console::warning(zm_internal_errcode("E00018") . "Unauthorized access_token: " . $access_token);
|
||||
return;
|
||||
}
|
||||
} elseif (is_string($token)) {
|
||||
if ($access_token !== $token && $token !== "") {
|
||||
$server->close($request->fd);
|
||||
Console::warning("Unauthorized access_token: " . $access_token);
|
||||
Console::warning(zm_internal_errcode("E00019") . "Unauthorized access_token: " . $access_token);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -81,11 +81,11 @@ class OnOpen implements SwooleEvent
|
||||
$dispatcher->dispatchEvents($conn);
|
||||
} catch (Exception $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught exception " . get_class($e) . " when calling \"open\": " . $error_msg);
|
||||
Console::error(zm_internal_errcode("E00020") . "Uncaught exception " . get_class($e) . " when calling \"open\": " . $error_msg);
|
||||
Console::trace();
|
||||
} catch (Error $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught " . get_class($e) . " when calling \"open\": " . $error_msg);
|
||||
Console::error(zm_internal_errcode("E00020") . "Uncaught " . get_class($e) . " when calling \"open\": " . $error_msg);
|
||||
Console::trace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
namespace ZM\Event\SwooleEvent;
|
||||
|
||||
|
||||
use Error;
|
||||
use Exception;
|
||||
use Swoole\Server;
|
||||
use ZM\Annotation\Swoole\SwooleHandler;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\SwooleEvent;
|
||||
use ZM\Utils\ProcessManager;
|
||||
use ZM\Utils\Manager\ProcessManager;
|
||||
|
||||
/**
|
||||
* Class OnPipeMessage
|
||||
@@ -18,6 +21,16 @@ class OnPipeMessage implements SwooleEvent
|
||||
{
|
||||
public function onCall(Server $server, $src_worker_id, $data) {
|
||||
$data = json_decode($data, true);
|
||||
ProcessManager::workerAction($src_worker_id, $data);
|
||||
try {
|
||||
ProcessManager::workerAction($src_worker_id, $data);
|
||||
} catch (Exception $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error(zm_internal_errcode("E00021") . "Uncaught exception " . get_class($e) . " when calling \"pipeMessage\": " . $error_msg);
|
||||
Console::trace();
|
||||
} catch (Error $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error(zm_internal_errcode("E00021") . "Uncaught " . get_class($e) . " when calling \"pipeMessage\": " . $error_msg);
|
||||
Console::trace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
namespace ZM\Event\SwooleEvent;
|
||||
|
||||
|
||||
use Co;
|
||||
use Error;
|
||||
use Exception;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Http\Request;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Annotation\Swoole\OnRequestEvent;
|
||||
@@ -33,7 +33,7 @@ class OnRequest implements SwooleEvent
|
||||
foreach (ZMConfig::get("global")["http_header"] as $k => $v) {
|
||||
$response->setHeader($k, $v);
|
||||
}
|
||||
unset(Context::$context[Co::getCid()]);
|
||||
unset(Context::$context[Coroutine::getCid()]);
|
||||
Console::debug("Calling Swoole \"request\" event from fd=" . $request->fd);
|
||||
set_coroutine_params(["request" => $request, "response" => $response]);
|
||||
|
||||
@@ -86,11 +86,11 @@ class OnRequest implements SwooleEvent
|
||||
);
|
||||
if (!$response->isEnd()) {
|
||||
if (ZMConfig::get("global", "debug_mode"))
|
||||
$response->end("Internal server exception: " . $e->getMessage());
|
||||
$response->end(zm_internal_errcode("E00023") . "Internal server exception: " . $e->getMessage());
|
||||
else
|
||||
$response->end("Internal server error.");
|
||||
$response->end(zm_internal_errcode("E00023") . "Internal server error.");
|
||||
}
|
||||
Console::error("Internal server exception (500), caused by " . get_class($e) . ": " . $e->getMessage());
|
||||
Console::error(zm_internal_errcode("E00023") . "Internal server exception (500), caused by " . get_class($e) . ": " . $e->getMessage());
|
||||
Console::log($e->getTraceAsString(), "gray");
|
||||
} catch (Error $e) {
|
||||
$response->status(500);
|
||||
@@ -100,11 +100,11 @@ class OnRequest implements SwooleEvent
|
||||
if (!$response->isEnd()) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
if (ZMConfig::get("global", "debug_mode"))
|
||||
$response->end("Internal server error: " . $error_msg);
|
||||
$response->end(zm_internal_errcode("E00023") . "Internal server error: " . $error_msg);
|
||||
else
|
||||
$response->end("Internal server error.");
|
||||
$response->end(zm_internal_errcode("E00023") . "Internal server error.");
|
||||
}
|
||||
Console::error("Internal server error (500), caused by " . get_class($e) . ": " . $e->getMessage());
|
||||
Console::error(zm_internal_errcode("E00023") . "Internal server error (500), caused by " . get_class($e) . ": " . $e->getMessage());
|
||||
Console::log($e->getTraceAsString(), "gray");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,18 @@
|
||||
namespace ZM\Event\SwooleEvent;
|
||||
|
||||
|
||||
use Error;
|
||||
use Exception;
|
||||
use Swoole\Event;
|
||||
use Swoole\Process;
|
||||
use Swoole\Server;
|
||||
use ZM\Annotation\Swoole\SwooleHandler;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\SwooleEvent;
|
||||
use ZM\Framework;
|
||||
use ZM\Store\ZMBuf;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\SignalListener;
|
||||
use ZM\Utils\Terminal;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
/**
|
||||
@@ -23,48 +26,49 @@ use ZM\Utils\ZMUtil;
|
||||
class OnStart implements SwooleEvent
|
||||
{
|
||||
public function onCall(Server $server) {
|
||||
$r = null;
|
||||
if (!Framework::$argv["disable-safe-exit"]) {
|
||||
Process::signal(SIGINT, function () use ($r, $server) {
|
||||
if (zm_atomic("_int_is_reload")->get() === 1) {
|
||||
zm_atomic("_int_is_reload")->set(0);
|
||||
$server->reload();
|
||||
} else {
|
||||
echo "\r";
|
||||
Console::warning("Server interrupted(SIGINT) on Master.");
|
||||
if ((Framework::$server->inotify ?? null) !== null)
|
||||
/** @noinspection PhpUndefinedFieldInspection */ Event::del(Framework::$server->inotify);
|
||||
Process::kill($server->master_pid, SIGTERM);
|
||||
}
|
||||
});
|
||||
}
|
||||
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);
|
||||
SignalListener::signalMaster($server);
|
||||
}
|
||||
if (Framework::$argv["watch"]) {
|
||||
if (extension_loaded('inotify')) {
|
||||
Console::info("Enabled File watcher, framework will reload automatically.");
|
||||
/** @noinspection PhpUndefinedFieldInspection */
|
||||
Framework::$server->inotify = $fd = inotify_init();
|
||||
$this->addWatcher(DataProvider::getWorkingDir() . "/src", $fd);
|
||||
$this->addWatcher(DataProvider::getSourceRootDir() . "/src", $fd);
|
||||
Event::add($fd, function () use ($fd) {
|
||||
$r = inotify_read($fd);
|
||||
Console::verbose("File updated: ".$r[0]["name"]);
|
||||
Console::verbose("File updated: " . $r[0]["name"]);
|
||||
ZMUtil::reload();
|
||||
});
|
||||
} else {
|
||||
Console::warning("You have not loaded \"inotify\" extension, please install first.");
|
||||
Console::warning(zm_internal_errcode("E00024") . "You have not loaded \"inotify\" extension, please install first.");
|
||||
}
|
||||
}
|
||||
if (Framework::$argv["interact"]) {
|
||||
ZMBuf::$terminal = $r = STDIN;
|
||||
Event::add($r, function () use ($r) {
|
||||
$fget = fgets($r);
|
||||
if ($fget === false) {
|
||||
Event::del($r);
|
||||
return;
|
||||
}
|
||||
$var = trim($fget);
|
||||
try {
|
||||
Terminal::executeCommand($var);
|
||||
} catch (Exception $e) {
|
||||
Console::error(zm_internal_errcode("E00025") . "Uncaught exception " . get_class($e) . ": " . $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")");
|
||||
} catch (Error $e) {
|
||||
Console::error(zm_internal_errcode("E00025") . "Uncaught error " . get_class($e) . ": " . $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private function addWatcher($maindir, $fd) {
|
||||
$dir = scandir($maindir);
|
||||
unset($dir[0], $dir[1]);
|
||||
if ($dir[0] == ".") {
|
||||
unset($dir[0], $dir[1]);
|
||||
}
|
||||
foreach ($dir as $subdir) {
|
||||
if (is_dir($maindir . "/" . $subdir)) {
|
||||
Console::debug("添加监听目录:" . $maindir . "/" . $subdir);
|
||||
|
||||
@@ -65,11 +65,11 @@ class OnTask implements SwooleEvent
|
||||
$dispatcher->dispatchEvents();
|
||||
} catch (Exception $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught exception " . get_class($e) . " when calling \"task\": " . $error_msg);
|
||||
Console::error(zm_internal_errcode("E00026") . "Uncaught exception " . get_class($e) . " when calling \"task\": " . $error_msg);
|
||||
Console::trace();
|
||||
} catch (Error $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught " . get_class($e) . " when calling \"task\": " . $error_msg);
|
||||
Console::error(zm_internal_errcode("E00026") . "Uncaught " . get_class($e) . " when calling \"task\": " . $error_msg);
|
||||
Console::trace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ use Swoole\Database\PDOConfig;
|
||||
use Swoole\Database\PDOPool;
|
||||
use Swoole\Process;
|
||||
use Swoole\Server;
|
||||
use Swoole\Timer;
|
||||
use ZM\Annotation\AnnotationParser;
|
||||
use ZM\Annotation\Swoole\OnStart;
|
||||
use ZM\Annotation\Swoole\OnSwooleEvent;
|
||||
@@ -26,13 +25,14 @@ use ZM\Event\EventDispatcher;
|
||||
use ZM\Event\EventManager;
|
||||
use ZM\Event\SwooleEvent;
|
||||
use ZM\Exception\DbException;
|
||||
use ZM\Exception\ZMException;
|
||||
use ZM\Framework;
|
||||
use ZM\Module\QQBot;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\MySQL\SqlPoolStorage;
|
||||
use ZM\Store\Redis\ZMRedisPool;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\ProcessManager;
|
||||
use ZM\Utils\SignalListener;
|
||||
|
||||
/**
|
||||
* Class OnWorkerStart
|
||||
@@ -43,18 +43,10 @@ class OnWorkerStart implements SwooleEvent
|
||||
{
|
||||
public function onCall(Server $server, $worker_id) {
|
||||
if (!Framework::$argv["disable-safe-exit"]) {
|
||||
Process::signal(SIGINT, function () use ($worker_id, $server) {
|
||||
|
||||
});
|
||||
SignalListener::signalWorker($server, $worker_id);
|
||||
}
|
||||
unset(Context::$context[Coroutine::getCid()]);
|
||||
if ($server->taskworker === false) {
|
||||
if (!Framework::$argv["disable-safe-exit"]) {
|
||||
Process::signal(SIGUSR1, function () use ($worker_id) {
|
||||
Timer::clearAll();
|
||||
ProcessManager::resumeAllWorkerCoroutines();
|
||||
});
|
||||
}
|
||||
zm_atomic("_#worker_" . $worker_id)->set($server->worker_pid);
|
||||
if (LightCacheInside::get("wait_api", "wait_api") !== null) {
|
||||
LightCacheInside::unset("wait_api", "wait_api");
|
||||
@@ -63,7 +55,8 @@ class OnWorkerStart implements SwooleEvent
|
||||
register_shutdown_function(function () use ($server) {
|
||||
$error = error_get_last();
|
||||
if (($error["type"] ?? -1) != 0) {
|
||||
Console::error("Internal fatal error: " . $error["message"] . " at " . $error["file"] . "({$error["line"]})");
|
||||
Console::error(zm_internal_errcode("E00027") . "Internal fatal error: " . $error["message"] . " at " . $error["file"] . "({$error["line"]})");
|
||||
zm_dump($error);
|
||||
}
|
||||
//DataProvider::saveBuffer();
|
||||
/** @var Server $server */
|
||||
@@ -71,7 +64,7 @@ class OnWorkerStart implements SwooleEvent
|
||||
else server()->shutdown();
|
||||
});
|
||||
|
||||
Console::verbose("Worker #{$server->worker_id} 启动中");
|
||||
Console::verbose("Worker #{$server->worker_id} starting");
|
||||
Framework::$server = $server;
|
||||
//ZMBuf::resetCache(); //清空变量缓存
|
||||
//ZMBuf::set("wait_start", []); //添加队列,在workerStart运行完成前先让其他协程等待执行
|
||||
@@ -90,12 +83,12 @@ class OnWorkerStart implements SwooleEvent
|
||||
phpinfo(); //这个phpinfo是有用的,不能删除
|
||||
$str = ob_get_clean();
|
||||
$str = explode("\n", $str);
|
||||
foreach ($str as $k => $v) {
|
||||
foreach ($str as $v) {
|
||||
$v = trim($v);
|
||||
if ($v == "") continue;
|
||||
if (mb_strpos($v, "API Extensions") === false) continue;
|
||||
if (mb_strpos($v, "pdo_mysql") === false) {
|
||||
throw new DbException("未安装 mysqlnd php-mysql扩展。");
|
||||
throw new DbException(zm_internal_errcode("E00028") . "未安装 mysqlnd php-mysql扩展。");
|
||||
}
|
||||
}
|
||||
$sql = ZMConfig::get("global", "sql_config");
|
||||
@@ -115,7 +108,7 @@ class OnWorkerStart implements SwooleEvent
|
||||
// 开箱即用的Redis
|
||||
$redis = ZMConfig::get("global", "redis_config");
|
||||
if ($redis !== null && $redis["host"] != "") {
|
||||
if (!extension_loaded("redis")) Console::error("Can not find redis extension.\n");
|
||||
if (!extension_loaded("redis")) Console::error(zm_internal_errcode("E00029") . "Can not find redis extension.\n");
|
||||
else ZMRedisPool::init($redis);
|
||||
}
|
||||
|
||||
@@ -136,11 +129,11 @@ class OnWorkerStart implements SwooleEvent
|
||||
Console::success("Worker #" . $worker_id . " started");
|
||||
} catch (Exception $e) {
|
||||
Console::error("Worker加载出错!停止服务!");
|
||||
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
|
||||
Console::error(zm_internal_errcode("E00030") . $e->getMessage() . "\n" . $e->getTraceAsString());
|
||||
Process::kill($server->master_pid, SIGTERM);
|
||||
return;
|
||||
} catch (Error $e) {
|
||||
Console::error("PHP Error: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
|
||||
Console::error(zm_internal_errcode("E00030") . "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');
|
||||
Process::kill($server->master_pid, SIGTERM);
|
||||
@@ -153,11 +146,11 @@ class OnWorkerStart implements SwooleEvent
|
||||
Console::success("TaskWorker #" . $server->worker_id . " started");
|
||||
} catch (Exception $e) {
|
||||
Console::error("Worker加载出错!停止服务!");
|
||||
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
|
||||
Console::error(zm_internal_errcode("E00030") . $e->getMessage() . "\n" . $e->getTraceAsString());
|
||||
Process::kill($server->master_pid, SIGTERM);
|
||||
return;
|
||||
} catch (Error $e) {
|
||||
Console::error("PHP Error: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
|
||||
Console::error(zm_internal_errcode("E00030") . "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');
|
||||
Process::kill($server->master_pid, SIGTERM);
|
||||
@@ -189,15 +182,13 @@ class OnWorkerStart implements SwooleEvent
|
||||
//加载各个模块的注解类,以及反射
|
||||
Console::debug("检索Module中");
|
||||
$parser = new AnnotationParser();
|
||||
$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"] ?? [])) {
|
||||
if (\server()->worker_id == 0)
|
||||
Console::verbose("Add " . $v . " to register path");
|
||||
$parser->addRegisterPath(DataProvider::getWorkingDir() . "/src/" . $v . "/", $v);
|
||||
$composer = json_decode(file_get_contents(DataProvider::getSourceRootDir() . "/composer.json"), true);
|
||||
foreach ($composer["autoload"]["psr-4"] as $k => $v) {
|
||||
if (is_dir(DataProvider::getSourceRootDir() . "/" . $v)) {
|
||||
if (in_array(trim($k, "\\") . "\\", $composer["extra"]["exclude_annotate"] ?? [])) continue;
|
||||
if (trim($k, "\\") == "ZM") continue;
|
||||
if (\server()->worker_id == 0) Console::verbose("Add " . $v . ":$k to register path");
|
||||
$parser->addRegisterPath(DataProvider::getSourceRootDir() . "/" . $v . "/", trim($k, "\\"));
|
||||
}
|
||||
}
|
||||
$parser->registerMods();
|
||||
@@ -207,7 +198,7 @@ class OnWorkerStart implements SwooleEvent
|
||||
Console::debug("加载自定义上下文中...");
|
||||
$context_class = ZMConfig::get("global", "context_class");
|
||||
if (!is_a($context_class, ContextInterface::class, true)) {
|
||||
throw new Exception("Context class must implemented from ContextInterface!");
|
||||
throw new ZMException(zm_internal_errcode("E00032") ."Context class must implemented from ContextInterface!");
|
||||
}
|
||||
|
||||
//加载插件
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
namespace ZM\Exception;
|
||||
|
||||
|
||||
use Throwable;
|
||||
|
||||
class DbException extends ZMException
|
||||
{
|
||||
|
||||
public function __construct($message = "", $code = 0, Throwable $previous = null) {
|
||||
parent::__construct(zm_internal_errcode("E00043") . $message, $code, $previous);
|
||||
}
|
||||
}
|
||||
|
||||
15
src/ZM/Exception/ExceptionHandler.php
Normal file
15
src/ZM/Exception/ExceptionHandler.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Exception;
|
||||
|
||||
/**
|
||||
* 这里放所有框架内部的异常处理方法
|
||||
* Class ExceptionHandler
|
||||
* @package ZM\Exception
|
||||
* @since 2.5
|
||||
*/
|
||||
class ExceptionHandler
|
||||
{
|
||||
|
||||
}
|
||||
14
src/ZM/Exception/ModulePackException.php
Normal file
14
src/ZM/Exception/ModulePackException.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Exception;
|
||||
|
||||
|
||||
use Throwable;
|
||||
|
||||
class ModulePackException extends ZMException
|
||||
{
|
||||
public function __construct($message = "", $code = 0, Throwable $previous = null) {
|
||||
parent::__construct(zm_internal_errcode("E00044") . $message, $code, $previous);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,11 @@
|
||||
namespace ZM\Exception;
|
||||
|
||||
|
||||
class NotInitializedException extends ZMException
|
||||
{
|
||||
use Throwable;
|
||||
|
||||
class NotInitializedException extends RedisException
|
||||
{
|
||||
public function __construct($message = "", $code = 0, Throwable $previous = null) {
|
||||
parent::__construct(zm_internal_errcode("E00046") . $message, $code, $previous);
|
||||
}
|
||||
}
|
||||
|
||||
9
src/ZM/Exception/RedisException.php
Normal file
9
src/ZM/Exception/RedisException.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Exception;
|
||||
|
||||
|
||||
class RedisException extends ZMException
|
||||
{
|
||||
}
|
||||
@@ -14,6 +14,6 @@ use Throwable;
|
||||
class RobotNotFoundException extends ZMException
|
||||
{
|
||||
public function __construct($message = "", $code = 0, Throwable $previous = null) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
parent::__construct(zm_internal_errcode("E00037") . $message, $code, $previous);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,11 @@
|
||||
|
||||
namespace ZM;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Error;
|
||||
use Exception;
|
||||
use Swoole\Event;
|
||||
use Swoole\Process;
|
||||
use Swoole\Server\Port;
|
||||
use ZM\Annotation\Swoole\OnSetup;
|
||||
use Throwable;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\ConnectionManager\ManagerGM;
|
||||
use ZM\Console\TermColor;
|
||||
@@ -21,7 +18,6 @@ use ZM\Store\ZMAtomic;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionMethod;
|
||||
use Swoole\Runtime;
|
||||
use Swoole\WebSocket\Server;
|
||||
use ZM\Annotation\Swoole\SwooleHandler;
|
||||
@@ -47,17 +43,19 @@ class Framework
|
||||
* @var array|bool|mixed|null
|
||||
*/
|
||||
private $server_set;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $setup_events = [];
|
||||
|
||||
/** @noinspection PhpUnusedParameterInspection */
|
||||
public function __construct($args = []) {
|
||||
$tty_width = $this->getTtyWidth();
|
||||
|
||||
self::$argv = $args;
|
||||
|
||||
ZMConfig::setDirectory(DataProvider::getWorkingDir() . '/config');
|
||||
ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config');
|
||||
ZMConfig::setEnv($args["env"] ?? "");
|
||||
if (ZMConfig::get("global") === false) {
|
||||
die ("Global config load failed: " . ZMConfig::$last_error . "\nPlease init first!\n");
|
||||
die (zm_internal_errcode("E00007") . "Global config load failed: " . ZMConfig::$last_error . "\nPlease init first!\nSee: https://github.com/zhamao-robot/zhamao-framework/issues/37\n");
|
||||
}
|
||||
|
||||
//定义常量
|
||||
@@ -80,9 +78,10 @@ class Framework
|
||||
]
|
||||
]);
|
||||
} catch (ConnectionManager\TableException $e) {
|
||||
die($e->getMessage());
|
||||
die(zm_internal_errcode("E00008") . $e->getMessage());
|
||||
}
|
||||
try {
|
||||
|
||||
Console::init(
|
||||
ZMConfig::get("global", "info_level") ?? 2,
|
||||
self::$server,
|
||||
@@ -97,13 +96,18 @@ class Framework
|
||||
$this->server_set["log_level"] = SWOOLE_LOG_DEBUG;
|
||||
$add_port = ZMConfig::get("global", "remote_terminal")["status"] ?? false;
|
||||
|
||||
$this->loadServerEvents();
|
||||
|
||||
$this->parseCliArgs(self::$argv, $add_port);
|
||||
|
||||
if (!isset($this->server_set["max_wait_time"])) {
|
||||
$this->server_set["max_wait_time"] = 5;
|
||||
}
|
||||
$worker = $this->server_set["worker_num"] ?? swoole_cpu_num();
|
||||
define("ZM_WORKER_NUM", $worker);
|
||||
|
||||
ZMAtomic::init();
|
||||
|
||||
// 打印初始信息
|
||||
$out["listen"] = ZMConfig::get("global", "host") . ":" . ZMConfig::get("global", "port");
|
||||
if (!isset($this->server_set["worker_num"])) $out["worker"] = swoole_cpu_num() . " (auto)";
|
||||
@@ -116,6 +120,9 @@ class Framework
|
||||
if (isset($this->server_set["task_worker_num"])) {
|
||||
$out["task_worker"] = $this->server_set["task_worker_num"];
|
||||
}
|
||||
if (!isset($this->server_set["pid_file"])) {
|
||||
$this->server_set["pid_file"] = ZMConfig::get("crash_dir") . ".zm.pid";
|
||||
}
|
||||
if (ZMConfig::get("global", "sql_config")["sql_host"] !== "") {
|
||||
$conf = ZMConfig::get("global", "sql_config");
|
||||
$out["mysql_pool"] = $conf["sql_database"] . "@" . $conf["sql_host"] . ":" . $conf["sql_port"];
|
||||
@@ -141,6 +148,8 @@ class Framework
|
||||
if ($args["preview"]) {
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
self::$server = new Server(ZMConfig::get("global", "host"), ZMConfig::get("global", "port"));
|
||||
|
||||
if ($add_port) {
|
||||
@@ -196,11 +205,11 @@ class Framework
|
||||
Terminal::executeCommand(trim($data));
|
||||
} catch (Exception $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught exception " . get_class($e) . " when calling \"open\": " . $error_msg);
|
||||
Console::error(zm_internal_errcode("E00009") . "Uncaught exception " . get_class($e) . " when calling \"open\": " . $error_msg);
|
||||
Console::trace();
|
||||
} catch (Error $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught " . get_class($e) . " when calling \"open\": " . $error_msg);
|
||||
Console::error(zm_internal_errcode("E00009") . "Uncaught " . get_class($e) . " when calling \"open\": " . $error_msg);
|
||||
Console::trace();
|
||||
}
|
||||
|
||||
@@ -224,11 +233,11 @@ class Framework
|
||||
// 注册 Swoole Server 的事件
|
||||
$this->registerServerEvents();
|
||||
$r = ZMConfig::get("global", "light_cache") ?? [
|
||||
"size" => 1024,
|
||||
"max_strlen" => 8192,
|
||||
"hash_conflict_proportion" => 0.6,
|
||||
"persistence_path" => realpath(DataProvider::getDataFolder() . "_cache.json"),
|
||||
"auto_save_interval" => 900
|
||||
'size' => 512, //最多允许储存的条数(需要2的倍数)
|
||||
'max_strlen' => 32768, //单行字符串最大长度(需要2的倍数)
|
||||
'hash_conflict_proportion' => 0.6, //Hash冲突率(越大越好,但是需要的内存更多)
|
||||
'persistence_path' => DataProvider::getDataFolder() . '_cache.json',
|
||||
'auto_save_interval' => 900
|
||||
];
|
||||
LightCache::init($r);
|
||||
LightCacheInside::init();
|
||||
@@ -269,15 +278,15 @@ class Framework
|
||||
}, E_ALL | E_STRICT);
|
||||
} catch (Exception $e) {
|
||||
Console::error("Framework初始化出现错误,请检查!");
|
||||
Console::error($e->getMessage());
|
||||
Console::error(zm_internal_errcode("E00010") . $e->getMessage());
|
||||
Console::debug($e);
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
private static function printMotd($tty_width) {
|
||||
if (file_exists(DataProvider::getWorkingDir() . "/config/motd.txt")) {
|
||||
$motd = file_get_contents(DataProvider::getWorkingDir() . "/config/motd.txt");
|
||||
if (file_exists(DataProvider::getSourceRootDir() . "/config/motd.txt")) {
|
||||
$motd = file_get_contents(DataProvider::getSourceRootDir() . "/config/motd.txt");
|
||||
} else {
|
||||
$motd = file_get_contents(__DIR__ . "/../../config/motd.txt");
|
||||
}
|
||||
@@ -290,10 +299,27 @@ class Framework
|
||||
}
|
||||
|
||||
public function start() {
|
||||
self::$loaded_files = get_included_files();
|
||||
self::$server->start();
|
||||
zm_atomic("server_is_stopped")->set(1);
|
||||
Console::log("zhamao-framework is stopped.");
|
||||
try {
|
||||
self::$loaded_files = get_included_files();
|
||||
self::$server->start();
|
||||
zm_atomic("server_is_stopped")->set(1);
|
||||
Console::log("zhamao-framework is stopped.");
|
||||
} catch (Throwable $e) {
|
||||
die(zm_internal_errcode("E00011") . "Framework has an uncaught " . get_class($e) . ": " . $e->getMessage() . PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
||||
private function loadServerEvents() {
|
||||
$r = exec(PHP_BINARY . " " . DataProvider::getFrameworkRootDir() . "/src/ZM/script_setup_loader.php", $output, $result_code);
|
||||
if ($result_code !== 0) {
|
||||
Console::error("Parsing code error!");
|
||||
exit(1);
|
||||
}
|
||||
$json = json_decode($r, true);
|
||||
if (!is_array($json)) {
|
||||
Console::warning(zm_internal_errcode("E00012") . "Parsing @SwooleHandler and @OnSetup error!");
|
||||
}
|
||||
$this->setup_events = $json;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,18 +329,13 @@ class Framework
|
||||
*/
|
||||
private function registerServerEvents() {
|
||||
$reader = new AnnotationReader();
|
||||
global $master_events;
|
||||
$master_events = [
|
||||
"setup" => [],
|
||||
"event" => []
|
||||
];
|
||||
$all = getAllClasses(FRAMEWORK_ROOT_DIR . "/src/ZM/Event/SwooleEvent/", "ZM\\Event\\SwooleEvent");
|
||||
$all = ZMUtil::getClassesPsr4(FRAMEWORK_ROOT_DIR . "/src/ZM/Event/SwooleEvent/", "ZM\\Event\\SwooleEvent");
|
||||
foreach ($all as $v) {
|
||||
$class = new $v();
|
||||
$reflection_class = new ReflectionClass($class);
|
||||
$anno_class = $reader->getClassAnnotation($reflection_class, SwooleHandler::class);
|
||||
if ($anno_class !== null) { // 类名形式的注解
|
||||
$master_events["event"][]=[
|
||||
$this->setup_events["event"][] = [
|
||||
"class" => $v,
|
||||
"method" => "onCall",
|
||||
"event" => $anno_class->event
|
||||
@@ -322,66 +343,14 @@ class Framework
|
||||
}
|
||||
}
|
||||
|
||||
$base_path = DataProvider::getWorkingDir();
|
||||
$scan_paths = [];
|
||||
$composer = json_decode(file_get_contents(DataProvider::getWorkingDir() . "/composer.json"), true);
|
||||
foreach (($composer["autoload"]["psr-4"] ?? []) as $k => $v) {
|
||||
if (is_dir($base_path."/".$v) && !in_array($v, $composer["extra"]["exclude_annotate"] ?? [])) {
|
||||
$scan_paths[trim($k, "\\")]=realpath($base_path."/".$v);
|
||||
}
|
||||
}
|
||||
$all_event_class = [];
|
||||
foreach($scan_paths as $namespace => $autoload_path) {
|
||||
$all_event_class = array_merge($all_event_class, getAllClasses($autoload_path."/", $namespace));
|
||||
}
|
||||
$process = new Process(function(Process $process) use ($all_event_class) {
|
||||
$reader = new AnnotationReader();
|
||||
$event_list = [];
|
||||
$setup_list = [];
|
||||
foreach ($all_event_class as $v) {
|
||||
$reflection_class = new ReflectionClass($v);
|
||||
$methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||
foreach ($methods as $vs) {
|
||||
$method_annotations = $reader->getMethodAnnotations($vs);
|
||||
if ($method_annotations != []) {
|
||||
$annotation = $method_annotations[0];
|
||||
if ($annotation instanceof SwooleHandler) {
|
||||
$event_list[]=[
|
||||
"class" => $v,
|
||||
"method" => $vs->getName(),
|
||||
"event" => $annotation->event
|
||||
];
|
||||
} elseif ($annotation instanceof OnSetup) {
|
||||
$setup_list[]=[
|
||||
"class" => $v,
|
||||
"method" => $vs->getName()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$sock = $process->exportSocket();
|
||||
$sock->send(json_encode(["setup" => $setup_list, "event" => $event_list]));
|
||||
});
|
||||
$process->start();
|
||||
go(function() use ($process) {
|
||||
$socket = $process->exportSocket();
|
||||
global $master_events;
|
||||
$obj = json_decode($socket->recv(), true);
|
||||
if ($obj["setup"] != []) $master_events["setup"] = array_merge($master_events["setup"], $obj["setup"]);
|
||||
if ($obj["event"] != []) $master_events["event"] = array_merge($master_events["event"], $obj["event"]);
|
||||
});
|
||||
Process::wait(true);
|
||||
Event::wait();
|
||||
|
||||
foreach($master_events["setup"] as $k => $v) {
|
||||
foreach (($this->setup_events["setup"] ?? []) as $v) {
|
||||
$c = ZMUtil::getModInstance($v["class"]);
|
||||
$method = $v["method"];
|
||||
$c->$method();
|
||||
}
|
||||
|
||||
foreach ($master_events["event"] as $k => $v) {
|
||||
self::$server->on($v["event"], function (...$param) use ($k, $v) {
|
||||
foreach ($this->setup_events["event"] as $v) {
|
||||
self::$server->on($v["event"], function (...$param) use ($v) {
|
||||
ZMUtil::getModInstance($v["class"])->{$v["method"]}(...$param);
|
||||
});
|
||||
}
|
||||
@@ -403,7 +372,7 @@ class Framework
|
||||
if (intval($y) >= 1 && intval($y) <= 1024) {
|
||||
$this->server_set["worker_num"] = intval($y);
|
||||
} else {
|
||||
Console::warning("Invalid worker num! Turn to default value (" . ($this->server_set["worker_num"] ?? swoole_cpu_num()) . ")");
|
||||
Console::warning(zm_internal_errcode("E00013") . "Invalid worker num! Turn to default value (" . ($this->server_set["worker_num"] ?? swoole_cpu_num()) . ")");
|
||||
}
|
||||
break;
|
||||
case 'task-worker-num':
|
||||
@@ -411,7 +380,7 @@ class Framework
|
||||
$this->server_set["task_worker_num"] = intval($y);
|
||||
$this->server_set["task_enable_coroutine"] = true;
|
||||
} else {
|
||||
Console::warning("Invalid worker num! Turn to default value (0)");
|
||||
Console::warning(zm_internal_errcode("E00013") . "Invalid worker num! Turn to default value (0)");
|
||||
}
|
||||
break;
|
||||
case 'disable-coroutine':
|
||||
@@ -421,7 +390,7 @@ class Framework
|
||||
$coroutine_mode = false;
|
||||
$terminal_id = null;
|
||||
self::$argv["watch"] = true;
|
||||
echo ("* You are in debug mode, do not use in production!\n");
|
||||
echo("* You are in debug mode, do not use in production!\n");
|
||||
break;
|
||||
case 'daemon':
|
||||
$this->server_set["daemonize"] = 1;
|
||||
@@ -463,7 +432,8 @@ class Framework
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($coroutine_mode) Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL);
|
||||
$global_hook = ZMConfig::get("global", 'runtime')['swoole_coroutine_hook_flags'] ?? SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL);
|
||||
if ($coroutine_mode && $global_hook === false) Runtime::enableCoroutine(true, $global_hook);
|
||||
else Runtime::enableCoroutine(false, SWOOLE_HOOK_ALL);
|
||||
}
|
||||
|
||||
@@ -554,7 +524,9 @@ class Framework
|
||||
}
|
||||
|
||||
public static function getTtyWidth(): string {
|
||||
return explode(" ", trim(exec("stty size")))[1];
|
||||
$size = exec("stty size");
|
||||
if (empty($size)) return 65;
|
||||
return explode(" ", trim($size))[1];
|
||||
}
|
||||
|
||||
public static function loadFrameworkState() {
|
||||
|
||||
@@ -3,58 +3,12 @@
|
||||
|
||||
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;
|
||||
use ZM\Store\LightCacheInside;
|
||||
|
||||
class RouteManager
|
||||
/**
|
||||
* 为了兼容,豁出去了(2.5),此兼容类
|
||||
* Class RouteManager
|
||||
* @package ZM\Http
|
||||
* @deprecated
|
||||
*/
|
||||
class RouteManager extends \ZM\Utils\Manager\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);
|
||||
}
|
||||
|
||||
public static function addStaticFileRoute($route, $path) {
|
||||
$tail = trim($route, "/");
|
||||
$route_name = ($tail === "" ? "" : "/") . $tail . "/{filename}";
|
||||
Console::debug("添加静态文件路由:" . $route_name);
|
||||
$route = new Route($route_name, ['_class' => RouteManager::class, '_method' => "onStaticRoute"]);
|
||||
//echo $path.PHP_EOL;
|
||||
LightCacheInside::set("static_route", $route->getPath(), $path);
|
||||
|
||||
self::$routes->add(md5($route_name), $route);
|
||||
}
|
||||
|
||||
public function onStaticRoute($params) {
|
||||
$route_path = self::$routes->get($params["_route"])->getPath();
|
||||
if(($path = LightCacheInside::get("static_route", $route_path)) === null) {
|
||||
ctx()->getResponse()->endWithStatus(404);
|
||||
return false;
|
||||
}
|
||||
unset($params["_route"]);
|
||||
$obj = array_shift($params);
|
||||
return new StaticFileHandler($obj, $path);
|
||||
}
|
||||
}
|
||||
206
src/ZM/Module/ModulePacker.php
Normal file
206
src/ZM/Module/ModulePacker.php
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Module;
|
||||
|
||||
use Exception;
|
||||
use Phar;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Exception\ModulePackException;
|
||||
use ZM\Exception\ZMException;
|
||||
use ZM\Store\LightCache;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
/**
|
||||
* 模块构造器
|
||||
* Class ModulePacker
|
||||
* @package ZM\Module
|
||||
* @since 2.5
|
||||
*/
|
||||
class ModulePacker
|
||||
{
|
||||
const ZM_MODULE_PACKER_VERSION = '1.0';
|
||||
/** @var array $module */
|
||||
private $module = [];
|
||||
/** @var bool $override */
|
||||
private $override = false;
|
||||
/** @var string $output_path */
|
||||
private $output_path = '';
|
||||
/** @var string $filename */
|
||||
private $filename = '';
|
||||
/** @var Phar $phar */
|
||||
private $phar;
|
||||
/** @var null|array $module_config */
|
||||
private $module_config = null;
|
||||
|
||||
/**
|
||||
* @throws ModulePackException
|
||||
*/
|
||||
public function __construct(array $module) {
|
||||
if (ini_get('phar.readonly') == '1') {
|
||||
throw new ModulePackException('请先在 php.ini 中设置 `phar.readonly = Off` 后再打包模块!');
|
||||
}
|
||||
if (!isset($module['name'], $module['module-path'], $module['namespace'])) {
|
||||
throw new ModulePackException('模块打包需要至少传入name、module-path、namespace三个参数!');
|
||||
}
|
||||
$this->module = $module;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输出文件夹
|
||||
* @param $path
|
||||
*/
|
||||
public function setOutputPath($path) {
|
||||
$this->output_path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否覆盖
|
||||
* @param bool $override
|
||||
*/
|
||||
public function setOverride(bool $override = true) {
|
||||
$this->override = $override;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块名字
|
||||
* @return mixed
|
||||
*/
|
||||
public function getName() {
|
||||
return $this->module['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取打包的文件名绝对路径
|
||||
* @return string
|
||||
*/
|
||||
public function getFileName(): string {
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打包模块
|
||||
* @throws ZMException
|
||||
*/
|
||||
public function pack() {
|
||||
$this->filename = $this->output_path . '/' . $this->module['name'];
|
||||
if (isset($this->module['version'])) {
|
||||
$this->filename .= '_' . $this->module['version'];
|
||||
}
|
||||
$this->filename .= '.phar';
|
||||
if ($this->override) {
|
||||
if (file_exists($this->filename)) {
|
||||
Console::info('Overwriting ' . $this->filename);
|
||||
unlink($this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
$this->phar = new Phar($this->filename);
|
||||
$this->phar->startBuffering();
|
||||
Console::info('模块输出文件:' . $this->filename);
|
||||
|
||||
$this->addFiles(); //添加文件
|
||||
$this->addLightCacheStore(); //保存light-cache-store指定的项
|
||||
$this->addModuleConfig(); //生成module-config.json
|
||||
$this->addEntry(); //生成模块的入口文件module_entry.php
|
||||
|
||||
$this->phar->stopBuffering();
|
||||
}
|
||||
|
||||
private function addFiles() {
|
||||
$file_list = DataProvider::scanDirFiles($this->module['module-path'], true, false);
|
||||
foreach ($file_list as $v) {
|
||||
$this->phar->addFile($v, $this->getRelativePath($v));
|
||||
}
|
||||
}
|
||||
|
||||
private function getRelativePath($path) {
|
||||
return str_replace(realpath(DataProvider::getSourceRootDir()) . '/', '', realpath($path));
|
||||
}
|
||||
|
||||
private function generatePharAutoload(): array {
|
||||
return ZMUtil::getClassesPsr4($this->module['module-path'], $this->module['namespace'], null, true);
|
||||
}
|
||||
|
||||
private function getComposerAutoloadItems(): array {
|
||||
$composer = json_decode(file_get_contents(DataProvider::getSourceRootDir() . '/composer.json'), true);
|
||||
$path = self::getRelativePath($this->module['module-path']);
|
||||
$item = [];
|
||||
foreach (($composer['autoload']['psr-4'] ?? []) as $k => $v) {
|
||||
if (strpos($path, $v) === 0) {
|
||||
$item['psr-4'][$k] = $v;
|
||||
}
|
||||
}
|
||||
foreach (($composer['autoload']['files'] ?? []) as $v) {
|
||||
if (strcmp($path, $v) === 0) {
|
||||
$item['files'][] = $v;
|
||||
}
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ZMException
|
||||
* @throws Exception
|
||||
*/
|
||||
private function addLightCacheStore() {
|
||||
if (isset($this->module['light-cache-store'])) {
|
||||
$store = [];
|
||||
$r = ZMConfig::get('global', 'light_cache') ?? [
|
||||
'size' => 512, //最多允许储存的条数(需要2的倍数)
|
||||
'max_strlen' => 32768, //单行字符串最大长度(需要2的倍数)
|
||||
'hash_conflict_proportion' => 0.6, //Hash冲突率(越大越好,但是需要的内存更多)
|
||||
'persistence_path' => DataProvider::getDataFolder() . '_cache.json',
|
||||
'auto_save_interval' => 900
|
||||
];
|
||||
LightCache::init($r);
|
||||
foreach ($this->module['light-cache-store'] as $v) {
|
||||
$r = LightCache::get($v);
|
||||
if ($r === null) {
|
||||
Console::warning(zm_internal_errcode("E00045") . 'LightCache 项:`$v` 不存在或值为null,无法为其保存。');
|
||||
} else {
|
||||
$store[$v] = $r;
|
||||
Console::info('打包LightCache持久化项:' . $v);
|
||||
}
|
||||
}
|
||||
$this->phar->addFromString('light_cache_store.json', json_encode($store, 128 | 256));
|
||||
}
|
||||
}
|
||||
|
||||
private function addModuleConfig() {
|
||||
$stub_values = [
|
||||
'zm-module' => true,
|
||||
'generated-id' => sha1(strval(microtime(true))),
|
||||
'module-packer-version' => self::ZM_MODULE_PACKER_VERSION,
|
||||
'module-root-path' => $this->getRelativePath($this->module['module-path']),
|
||||
'namespace' => $this->module['namespace'],
|
||||
'autoload-psr-4' => $this->generatePharAutoload(),
|
||||
'unpack' => [
|
||||
'composer-autoload-items' => $this->getComposerAutoloadItems(),
|
||||
'global-config-override' => !empty($this->module['global-config-override'] ?? []) ? $this->module['global-config-override'] : false
|
||||
],
|
||||
'allow-hotload' => empty($this->module['global-config-override'] ?? []) && !isset($this->module['depends'])
|
||||
];
|
||||
$this->phar->addFromString('zmplugin.json', json_encode($stub_values, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
$this->module_config = $stub_values;
|
||||
}
|
||||
|
||||
private function addEntry() {
|
||||
$stub_replace = [
|
||||
'generated_id' => $this->module_config['generated-id']
|
||||
];
|
||||
|
||||
$stub_template = str_replace(
|
||||
array_map(function ($x) {
|
||||
return '__' . $x . '__';
|
||||
}, array_keys($stub_replace)),
|
||||
array_values($stub_replace),
|
||||
file_get_contents(DataProvider::getFrameworkRootDir() . '/src/ZM/script_phar_stub.php')
|
||||
);
|
||||
$this->phar->addFromString('module_entry.php', $stub_template);
|
||||
|
||||
$this->phar->setStub($this->phar->createDefaultStub('module_entry.php'));
|
||||
}
|
||||
}
|
||||
23
src/ZM/Module/ModuleUnpacker.php
Normal file
23
src/ZM/Module/ModuleUnpacker.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Module;
|
||||
|
||||
|
||||
class ModuleUnpacker
|
||||
{
|
||||
private $module;
|
||||
|
||||
public function __construct(array $module) {
|
||||
$this->module = $module;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解包模块
|
||||
* @return array
|
||||
*/
|
||||
public function unpack(): array {
|
||||
// TODO: 解包模块到src
|
||||
return $this->module;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ use ZM\Console\Console;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Exception\ZMException;
|
||||
use ZM\Framework;
|
||||
use ZM\Utils\ProcessManager;
|
||||
use ZM\Utils\Manager\ProcessManager;
|
||||
|
||||
class LightCache
|
||||
{
|
||||
@@ -26,7 +26,6 @@ class LightCache
|
||||
* @param $config
|
||||
* @return bool|mixed
|
||||
* @throws Exception
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
*/
|
||||
public static function init($config) {
|
||||
self::$config = $config;
|
||||
@@ -44,17 +43,17 @@ class LightCache
|
||||
$write = self::set($k, $v);
|
||||
Console::verbose("Writing LightCache: " . $k);
|
||||
if ($write === false) {
|
||||
self::$last_error = '可能是由于 Hash 冲突过多导致动态空间无法分配内存';
|
||||
self::$last_error = zm_internal_errcode("E00051") . '可能是由于 Hash 冲突过多导致动态空间无法分配内存';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($result === false) {
|
||||
self::$last_error = '系统内存不足,申请失败';
|
||||
self::$last_error = zm_internal_errcode("E00050") . '系统内存不足,申请失败';
|
||||
} else {
|
||||
$obj = Framework::loadFrameworkState();
|
||||
foreach(($obj["expiring_light_cache"] ?? []) as $k => $v) {
|
||||
foreach (($obj["expiring_light_cache"] ?? []) as $k => $v) {
|
||||
$value = $v["value"];
|
||||
if (is_array($value)) {
|
||||
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
@@ -87,7 +86,7 @@ class LightCache
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function get(string $key) {
|
||||
if (self::$kv_table === null) throw new ZMException("not initialized LightCache");
|
||||
if (self::$kv_table === null) throw new ZMException(zm_internal_errcode("E00048") . "not initialized LightCache");
|
||||
self::checkExpire($key);
|
||||
$r = self::$kv_table->get($key);
|
||||
return $r === false ? null : self::parseGet($r);
|
||||
@@ -99,7 +98,7 @@ class LightCache
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function getExpire(string $key) {
|
||||
if (self::$kv_table === null) throw new ZMException("not initialized LightCache");
|
||||
if (self::$kv_table === null) throw new ZMException(zm_internal_errcode("E00048") . "not initialized LightCache");
|
||||
self::checkExpire($key);
|
||||
$r = self::$kv_table->get($key, "expire");
|
||||
return $r === false ? null : $r - time();
|
||||
@@ -112,7 +111,7 @@ class LightCache
|
||||
* @since 2.4.3
|
||||
*/
|
||||
public static function getExpireTS(string $key) {
|
||||
if (self::$kv_table === null) throw new ZMException("not initialized LightCache");
|
||||
if (self::$kv_table === null) throw new ZMException(zm_internal_errcode("E00048") . "not initialized LightCache");
|
||||
self::checkExpire($key);
|
||||
$r = self::$kv_table->get($key, "expire");
|
||||
return $r === false ? null : $r;
|
||||
@@ -124,10 +123,9 @@ class LightCache
|
||||
* @param int $expire
|
||||
* @return mixed
|
||||
* @throws ZMException
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
*/
|
||||
public static function set(string $key, $value, int $expire = -1) {
|
||||
if (self::$kv_table === null) throw new ZMException("not initialized LightCache");
|
||||
if (self::$kv_table === null) throw new ZMException(zm_internal_errcode("E00048") . "not initialized LightCache");
|
||||
if (is_array($value)) {
|
||||
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
if (strlen($value) >= self::$config["max_strlen"]) return false;
|
||||
@@ -140,7 +138,7 @@ class LightCache
|
||||
$data_type = "bool";
|
||||
$value = json_encode($value);
|
||||
} else {
|
||||
throw new ZMException("Only can set string, array and int");
|
||||
throw new ZMException(zm_internal_errcode("E00049") . "Only can set string, array and int");
|
||||
}
|
||||
try {
|
||||
return self::$kv_table->set($key, [
|
||||
@@ -158,10 +156,9 @@ class LightCache
|
||||
* @param $value
|
||||
* @return bool|mixed
|
||||
* @throws ZMException
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
*/
|
||||
public static function update(string $key, $value) {
|
||||
if (self::$kv_table === null) throw new ZMException("not initialized LightCache.");
|
||||
if (self::$kv_table === null) throw new ZMException(zm_internal_errcode("E00048") . "not initialized LightCache.");
|
||||
if (is_array($value)) {
|
||||
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
if (strlen($value) >= self::$config["max_strlen"]) return false;
|
||||
@@ -171,7 +168,7 @@ class LightCache
|
||||
} elseif (is_int($value)) {
|
||||
$data_type = "int";
|
||||
} else {
|
||||
throw new ZMException("Only can set string, array and int");
|
||||
throw new ZMException(zm_internal_errcode("E00048") . "Only can set string, array and int");
|
||||
}
|
||||
try {
|
||||
if (self::$kv_table->get($key) === false) return false;
|
||||
|
||||
@@ -67,6 +67,6 @@ class LightCacheInside
|
||||
self::$kv_table[$name] = new Table($size, $conflict_proportion);
|
||||
self::$kv_table[$name]->column("value", Table::TYPE_STRING, $str_size);
|
||||
$r = self::$kv_table[$name]->create();
|
||||
if ($r === false) throw new ZMException("内存不足,创建静态表失败!");
|
||||
if ($r === false) throw new ZMException(zm_internal_errcode("E00050") . "内存不足,创建静态表失败!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class ZMRedisPool
|
||||
var_dump($r);
|
||||
}
|
||||
} catch (RedisException $e) {
|
||||
Console::error("Redis init failed! " . $e->getMessage());
|
||||
Console::error(zm_internal_errcode("E00047") . "Redis init failed! " . $e->getMessage());
|
||||
self::$pool = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ class ZMAtomic
|
||||
for ($i = 0; $i < 10; ++$i) {
|
||||
self::$atomics["_tmp_" . $i] = new Atomic(0);
|
||||
}
|
||||
self::$atomics["ss"] = new Atomic(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
use Co;
|
||||
use Swoole\Coroutine;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\Lock\SpinLock;
|
||||
use ZM\Store\ZMAtomic;
|
||||
use ZM\Utils\Manager\ProcessManager;
|
||||
|
||||
class CoMessage
|
||||
{
|
||||
@@ -16,10 +17,9 @@ class CoMessage
|
||||
* @param array $compare
|
||||
* @param int $timeout
|
||||
* @return mixed
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
*/
|
||||
public static function yieldByWS(array $hang, array $compare, $timeout = 600) {
|
||||
$cid = Co::getuid();
|
||||
$cid = Coroutine::getuid();
|
||||
$api_id = ZMAtomic::get("wait_msg_id")->add(1);
|
||||
$hang["compare"] = $compare;
|
||||
$hang["coroutine"] = $cid;
|
||||
@@ -33,10 +33,10 @@ class CoMessage
|
||||
$id = swoole_timer_after($timeout * 1000, function () use ($api_id) {
|
||||
$r = LightCacheInside::get("wait_api", "wait_api")[$api_id] ?? null;
|
||||
if (is_array($r)) {
|
||||
Co::resume($r["coroutine"]);
|
||||
Coroutine::resume($r["coroutine"]);
|
||||
}
|
||||
});
|
||||
Co::suspend();
|
||||
Coroutine::suspend();
|
||||
SpinLock::lock("wait_api");
|
||||
$sess = LightCacheInside::get("wait_api", "wait_api");
|
||||
$result = $sess[$api_id]["result"] ?? null;
|
||||
@@ -70,7 +70,7 @@ class CoMessage
|
||||
if ($all[$last]["worker_id"] != server()->worker_id) {
|
||||
ProcessManager::sendActionToWorker($all[$last]["worker_id"], "resume_ws_message", $all[$last]);
|
||||
} else {
|
||||
Co::resume($all[$last]["coroutine"]);
|
||||
Coroutine::resume($all[$last]["coroutine"]);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -38,6 +38,10 @@ class CoroutinePool
|
||||
self::$sizes[$name] = $size;
|
||||
}
|
||||
|
||||
public static function getRunningCoroutineCount($name = "default") {
|
||||
return count(self::$cids[$name]);
|
||||
}
|
||||
|
||||
private static function checkCids($name) {
|
||||
if (in_array(Coroutine::getCid(), self::$cids[$name])) {
|
||||
$a = array_search(Coroutine::getCid(), self::$cids[$name]);
|
||||
|
||||
@@ -11,44 +11,139 @@ class DataProvider
|
||||
{
|
||||
public static $buffer_list = [];
|
||||
|
||||
/**
|
||||
* 返回资源目录
|
||||
* @return string
|
||||
*/
|
||||
public static function getResourceFolder(): string {
|
||||
return self::getWorkingDir() . '/resources/';
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回工作目录,不带最右边文件夹的斜杠(/)
|
||||
* @return false|string
|
||||
*/
|
||||
public static function getWorkingDir() {
|
||||
return WORKING_DIR;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取框架所在根目录
|
||||
* @return false|string
|
||||
*/
|
||||
public static function getFrameworkRootDir() {
|
||||
return FRAMEWORK_ROOT_DIR;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取源码根目录,除Phar模式外均与工作目录相同
|
||||
* @return false|string
|
||||
*/
|
||||
public static function getSourceRootDir() {
|
||||
return defined("SOURCE_ROOT_DIR") ? SOURCE_ROOT_DIR : WORKING_DIR;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取框架反代链接
|
||||
* @return array|false|mixed|null
|
||||
*/
|
||||
public static function getFrameworkLink() {
|
||||
return ZMConfig::get("global", "http_reverse_link");
|
||||
}
|
||||
|
||||
public static function getDataFolder() {
|
||||
/**
|
||||
* 获取zm_data数据目录,如果二级目录不为空,则自动创建目录并返回
|
||||
* @param string $second
|
||||
* @return array|false|mixed|string|null
|
||||
*/
|
||||
public static function getDataFolder(string $second = '') {
|
||||
if ($second !== '') {
|
||||
@mkdir(ZM_DATA . $second);
|
||||
if (!is_dir(ZM_DATA . $second)) return false;
|
||||
return realpath(ZM_DATA . $second) . "/";
|
||||
}
|
||||
return ZM_DATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将变量保存在zm_data下的数据目录,传入数组
|
||||
* @param $filename
|
||||
* @param $file_array
|
||||
* @return false|int
|
||||
*/
|
||||
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);
|
||||
if (count($r) == 2) {
|
||||
$path = $path . $r[0] . "/";
|
||||
if (!is_dir($path)) mkdir($path);
|
||||
$name = $r[1];
|
||||
} elseif (count($r) != 1) {
|
||||
Console::warning("存储失败,文件名只能有一级目录");
|
||||
Console::warning(zm_internal_errcode("E00057") . "存储失败,文件名只能有一级目录");
|
||||
return false;
|
||||
} else {
|
||||
$name = $r[0];
|
||||
}
|
||||
return file_put_contents($path.$name.".json", json_encode($file_array, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
||||
return file_put_contents($path . $name . ".json", json_encode($file_array, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从json加载变量到内存
|
||||
* @param $filename
|
||||
* @return mixed|null
|
||||
*/
|
||||
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);
|
||||
if (file_exists($path . $filename . ".json")) {
|
||||
return json_decode(file_get_contents($path . $filename . ".json"), true);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归或非递归扫描目录,可返回相对目录的文件列表或绝对目录的文件列表
|
||||
* @param $dir
|
||||
* @param bool $recursive
|
||||
* @param bool|string $relative
|
||||
* @return array|false
|
||||
* @since 2.5
|
||||
*/
|
||||
public static function scanDirFiles($dir, bool $recursive = true, $relative = false) {
|
||||
$dir = rtrim($dir, "/");
|
||||
if (!is_dir($dir)) return false;
|
||||
$r = scandir($dir);
|
||||
if ($r === false) return false;
|
||||
$list = [];
|
||||
if ($relative === true) {
|
||||
$relative = $dir;
|
||||
}
|
||||
foreach ($r as $v) {
|
||||
if ($v == "." || $v == "..") continue;
|
||||
$sub_file = $dir . "/" . $v;
|
||||
if (is_dir($sub_file) && $recursive) {
|
||||
$list = array_merge($list, self::scanDirFiles($sub_file, $recursive, $relative));
|
||||
} elseif (is_file($sub_file)) {
|
||||
if (is_string($relative) && mb_strpos($sub_file, $relative) === 0) {
|
||||
$list [] = ltrim(mb_substr($sub_file, mb_strlen($relative)), "/");
|
||||
} elseif ($relative === false) {
|
||||
$list[] = $sub_file;
|
||||
} else {
|
||||
Console::warning(zm_internal_errcode("E00058") . "Relative path is not generated: wrong base directory ($relative)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查路径是否为相对路径(根据第一个字符是否为"/"来判断)
|
||||
* @param $path
|
||||
* @return bool
|
||||
* @since 2.5
|
||||
*/
|
||||
public static function isRelativePath($path) {
|
||||
return strlen($path) > 0 && $path[0] === '/';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
use Co;
|
||||
use Swoole\Coroutine;
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\Matcher\UrlMatcher;
|
||||
@@ -13,7 +13,7 @@ use Symfony\Component\Routing\RouteCollection;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Http\Response;
|
||||
use ZM\Http\RouteManager;
|
||||
use ZM\Utils\Manager\RouteManager;
|
||||
|
||||
class HttpUtil
|
||||
{
|
||||
@@ -51,7 +51,7 @@ class HttpUtil
|
||||
|
||||
public static function getHttpCodePage(int $http_code) {
|
||||
if (isset(ZMConfig::get("global", "http_default_code_page")[$http_code])) {
|
||||
return Co::readFile(DataProvider::getResourceFolder() . "html/" . ZMConfig::get("global", "http_default_code_page")[$http_code]);
|
||||
return Coroutine::readFile(DataProvider::getResourceFolder() . "html/" . ZMConfig::get("global", "http_default_code_page")[$http_code]);
|
||||
} else return null;
|
||||
}
|
||||
|
||||
|
||||
115
src/ZM/Utils/Manager/ModuleManager.php
Normal file
115
src/ZM/Utils/Manager/ModuleManager.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Utils\Manager;
|
||||
|
||||
|
||||
use ZM\Console\Console;
|
||||
use ZM\Exception\ModulePackException;
|
||||
use ZM\Exception\ZMException;
|
||||
use ZM\Module\ModulePacker;
|
||||
use ZM\Module\ModuleUnpacker;
|
||||
use ZM\Utils\DataProvider;
|
||||
|
||||
/**
|
||||
* 模块管理器,负责打包解包模块
|
||||
* Class ModuleManager
|
||||
* @package ZM\Utils\Manager
|
||||
* @since 2.5
|
||||
*/
|
||||
class ModuleManager
|
||||
{
|
||||
|
||||
/**
|
||||
* 扫描src目录下的所有已经被标注的模块
|
||||
* @return array
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function getConfiguredModules(): array {
|
||||
$dir = DataProvider::getSourceRootDir() . "/src/";
|
||||
$ls = DataProvider::scanDirFiles($dir, true, true);
|
||||
$modules = [];
|
||||
foreach ($ls as $v) {
|
||||
$pathinfo = pathinfo($v);
|
||||
if ($pathinfo["basename"] == "zm.json") {
|
||||
$json = json_decode(file_get_contents(realpath($dir . "/" . $v)), true);
|
||||
if ($json === null) continue;
|
||||
if (!isset($json["name"])) continue;
|
||||
if ($pathinfo["dirname"] == ".") {
|
||||
throw new ZMException(zm_internal_errcode("E00052") . "在/src/目录下不可以直接标记为模块(zm.json),因为命名空间不能为根空间!");
|
||||
}
|
||||
$json["module-path"] = realpath($dir . "/" . $pathinfo["dirname"]);
|
||||
$json["namespace"] = str_replace("/", "\\", $pathinfo["dirname"]);
|
||||
if (isset($modules[$json["name"]])) {
|
||||
throw new ZMException(zm_internal_errcode("E00053") . "重名模块:" . $json["name"]);
|
||||
}
|
||||
$modules[$json["name"]] = $json;
|
||||
}
|
||||
}
|
||||
return $modules;
|
||||
}
|
||||
|
||||
public static function getPackedModules(): array {
|
||||
$dir = DataProvider::getDataFolder() . "modules";
|
||||
$ls = DataProvider::scanDirFiles($dir, true, false);
|
||||
if ($ls === false) return [];
|
||||
$modules = [];
|
||||
foreach ($ls as $v) {
|
||||
$pathinfo = pathinfo($v);
|
||||
if (($pathinfo["extension"] ?? "") != "phar") continue;
|
||||
$file = "phar://" . $v;
|
||||
if (!is_file($file . "/module_entry.php") || !is_file($file . "/zmplugin.json")) continue;
|
||||
$module_config = json_decode(file_get_contents($file . "/zmplugin.json"), true);
|
||||
if ($module_config === null) continue;
|
||||
if (!is_file($file . "/" . $module_config["module-root-path"] . "/zm.json")) {
|
||||
Console::warning(zm_internal_errcode("E00054") . "模块(插件)文件 " . $pathinfo["basename"] . " 无法找到模块配置文件(zm.json)!");
|
||||
continue;
|
||||
}
|
||||
$module_file = json_decode(file_get_contents($file . "/" . $module_config["module-root-path"] . "/zm.json"), true);
|
||||
if ($module_file === null) {
|
||||
Console::warning(zm_internal_errcode("E000555") . "模块(插件)文件 " . $pathinfo["basename"] . " 无法正常读取模块配置文件(zm.json)!");
|
||||
continue;
|
||||
}
|
||||
$module_config["phar-path"] = $v;
|
||||
$module_config["name"] = $module_file["name"] ?? null;
|
||||
if ($module_config["name"] === null) continue;
|
||||
$module_config["module-config"] = $module_file;
|
||||
$modules[$module_config["name"]] = $module_config;
|
||||
}
|
||||
return $modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打包模块
|
||||
* @param $module
|
||||
* @return bool
|
||||
* @throws ZMException
|
||||
*/
|
||||
public static function packModule($module): bool {
|
||||
try {
|
||||
$packer = new ModulePacker($module);
|
||||
$packer->setOutputPath(DataProvider::getDataFolder() . "output");
|
||||
$packer->setOverride();
|
||||
$packer->pack();
|
||||
return true;
|
||||
} catch (ModulePackException $e) {
|
||||
Console::error($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解包模块 TODO
|
||||
* @param $module
|
||||
* @return array|false
|
||||
*/
|
||||
public static function unpackModule($module) {
|
||||
try {
|
||||
$packer = new ModuleUnpacker($module);
|
||||
return $packer->unpack();
|
||||
} catch (ModulePackException $e) {
|
||||
Console::error($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
namespace ZM\Utils\Manager;
|
||||
|
||||
|
||||
use Co;
|
||||
use Swoole\Coroutine;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\Swoole\OnPipeMessageEvent;
|
||||
use ZM\Console\Console;
|
||||
@@ -38,7 +38,7 @@ class ProcessManager
|
||||
break;
|
||||
case "resume_ws_message":
|
||||
$obj = $data["data"];
|
||||
Co::resume($obj["coroutine"]);
|
||||
Coroutine::resume($obj["coroutine"]);
|
||||
break;
|
||||
case "getWorkerCache":
|
||||
$r = WorkerCache::get($data["key"]);
|
||||
@@ -99,7 +99,7 @@ class ProcessManager
|
||||
public static function sendActionToWorker($worker_id, $action, $data) {
|
||||
$obj = ["action" => $action, "data" => $data];
|
||||
if (server()->worker_id === -1 && server()->getManagerPid() != posix_getpid()) {
|
||||
Console::warning("Cannot send worker action from master or manager process!");
|
||||
Console::warning(zm_internal_errcode("E00022") . "Cannot send worker action from master or manager process!");
|
||||
return;
|
||||
}
|
||||
if (server()->worker_id == $worker_id) {
|
||||
@@ -114,9 +114,9 @@ class ProcessManager
|
||||
Console::warning("Cannot call '" . __FUNCTION__ . "' in non-worker process!");
|
||||
return;
|
||||
}
|
||||
foreach ((LightCacheInside::get("wait_api", "wait_api") ?? []) as $k => $v) {
|
||||
foreach ((LightCacheInside::get("wait_api", "wait_api") ?? []) as $v) {
|
||||
if (isset($v["coroutine"], $v["worker_id"])) {
|
||||
if (server()->worker_id == $v["worker_id"]) Co::resume($v["coroutine"]);
|
||||
if (server()->worker_id == $v["worker_id"]) Coroutine::resume($v["coroutine"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
67
src/ZM/Utils/Manager/RouteManager.php
Normal file
67
src/ZM/Utils/Manager/RouteManager.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Utils\Manager;
|
||||
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use ZM\Annotation\Http\Controller;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Http\StaticFileHandler;
|
||||
use ZM\Store\LightCacheInside;
|
||||
|
||||
/**
|
||||
* 路由管理器,2.5版本更改了命名空间
|
||||
* Class RouteManager
|
||||
* @package ZM\Utils\Manager
|
||||
* @since 2.3.0
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
public static function addStaticFileRoute($route, $path) {
|
||||
$tail = trim($route, "/");
|
||||
$route_name = ($tail === "" ? "" : "/") . $tail . "/{filename}";
|
||||
Console::debug("添加静态文件路由:" . $route_name);
|
||||
$route = new Route($route_name, ['_class' => RouteManager::class, '_method' => "onStaticRoute"]);
|
||||
//echo $path.PHP_EOL;
|
||||
LightCacheInside::set("static_route", $route->getPath(), $path);
|
||||
|
||||
self::$routes->add(md5($route_name), $route);
|
||||
}
|
||||
|
||||
public function onStaticRoute($params) {
|
||||
$route_path = self::$routes->get($params["_route"])->getPath();
|
||||
if(($path = LightCacheInside::get("static_route", $route_path)) === null) {
|
||||
ctx()->getResponse()->endWithStatus(404);
|
||||
return false;
|
||||
}
|
||||
unset($params["_route"]);
|
||||
$obj = array_shift($params);
|
||||
return new StaticFileHandler($obj, $path);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php /** @noinspection PhpUnused */
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
namespace ZM\Utils\Manager;
|
||||
|
||||
|
||||
use ZM\Console\Console;
|
||||
@@ -9,15 +9,14 @@ use ZM\Console\Console;
|
||||
class TaskManager
|
||||
{
|
||||
/**
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
* @param $task_name
|
||||
* @param int $timeout
|
||||
* @param mixed ...$params
|
||||
* @return false|mixed
|
||||
*/
|
||||
public static function runTask($task_name, $timeout = -1, ...$params) {
|
||||
public static function runTask($task_name, int $timeout = -1, ...$params) {
|
||||
if (!isset(server()->setting["task_worker_num"])) {
|
||||
Console::warning("未开启 TaskWorker 进程,请先修改 global 配置文件启用!");
|
||||
Console::warning(zm_internal_errcode("E00056") . "未开启 TaskWorker 进程,请先修改 global 配置文件启用!");
|
||||
return false;
|
||||
}
|
||||
$r = server()->taskwait(["task" => $task_name, "params" => $params], $timeout);
|
||||
@@ -11,6 +11,7 @@ use ZM\Console\Console;
|
||||
use ZM\Entity\MatchResult;
|
||||
use ZM\Event\EventManager;
|
||||
use ZM\Requests\ZMRequest;
|
||||
use ZM\Utils\Manager\ProcessManager;
|
||||
|
||||
class MessageUtil
|
||||
{
|
||||
@@ -25,7 +26,7 @@ class MessageUtil
|
||||
if (!is_dir($path)) mkdir($path);
|
||||
$path = realpath($path);
|
||||
if ($path === false) {
|
||||
Console::warning("指定的路径错误不存在!");
|
||||
Console::warning(zm_internal_errcode("E00059") . "指定的路径错误不存在!");
|
||||
return false;
|
||||
}
|
||||
$files = [];
|
||||
@@ -34,7 +35,7 @@ class MessageUtil
|
||||
if ($v->type == "image") {
|
||||
$result = ZMRequest::downloadFile($v->params["url"], $path . "/" . $v->params["file"]);
|
||||
if ($result === false) {
|
||||
Console::warning("图片 " . $v->params["url"] . " 下载失败!");
|
||||
Console::warning(zm_internal_errcode("E00060") . "图片 " . $v->params["url"] . " 下载失败!");
|
||||
return false;
|
||||
}
|
||||
$files[] = $path . "/" . $v->params["file"];
|
||||
@@ -112,7 +113,7 @@ class MessageUtil
|
||||
$ls = EventManager::$events[CQCommand::class] ?? [];
|
||||
$word = self::splitCommand($msg);
|
||||
$matched = new MatchResult();
|
||||
foreach ($ls as $k => $v) {
|
||||
foreach ($ls as $v) {
|
||||
if (array_diff([$v->match, $v->pattern, $v->regex, $v->keyword, $v->end_with, $v->start_with], [""]) == []) continue;
|
||||
elseif (($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == $obj["user_id"])) &&
|
||||
($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == ($obj["group_id"] ?? 0))) &&
|
||||
|
||||
75
src/ZM/Utils/SignalListener.php
Normal file
75
src/ZM/Utils/SignalListener.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
use Swoole\Event;
|
||||
use Swoole\Process;
|
||||
use Swoole\Server;
|
||||
use Swoole\Timer;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Framework;
|
||||
use ZM\Store\ZMBuf;
|
||||
use ZM\Utils\Manager\ProcessManager;
|
||||
|
||||
/**
|
||||
* 炸毛框架的Linux signal管理类
|
||||
* Class SignalListener
|
||||
* @package ZM\Utils
|
||||
* @since 2.5
|
||||
*/
|
||||
class SignalListener
|
||||
{
|
||||
/**
|
||||
* 监听Master进程的Ctrl+C
|
||||
* @param Server $server
|
||||
*/
|
||||
public static function signalMaster(Server $server) {
|
||||
Process::signal(SIGINT, function () use ($server) {
|
||||
if (zm_atomic("_int_is_reload")->get() === 1) {
|
||||
zm_atomic("_int_is_reload")->set(0);
|
||||
$server->reload();
|
||||
} else {
|
||||
echo "\r";
|
||||
Console::warning("Server interrupted(SIGINT) on Master.");
|
||||
if ((Framework::$server->inotify ?? null) !== null)
|
||||
/** @noinspection PhpUndefinedFieldInspection */ Event::del(Framework::$server->inotify);
|
||||
if (ZMBuf::$terminal !== null)
|
||||
Event::del(ZMBuf::$terminal);
|
||||
Process::kill($server->master_pid, SIGTERM);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听Manager进程的Ctrl+C
|
||||
*/
|
||||
public static function signalManager() {
|
||||
$func = function () {
|
||||
Console::verbose("Interrupted in manager!");
|
||||
};
|
||||
if (version_compare(SWOOLE_VERSION, "4.6.7") >= 0) {
|
||||
Process::signal(SIGINT, $func);
|
||||
} elseif (extension_loaded("pcntl")) {
|
||||
pcntl_signal(SIGINT, $func);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听Worker/TaskWorker进程的Ctrl+C
|
||||
* @param Server $server
|
||||
* @param $worker_id
|
||||
*/
|
||||
public static function signalWorker(Server $server, $worker_id) {
|
||||
Process::signal(SIGINT, function () use ($worker_id, $server) {
|
||||
// do nothing
|
||||
});
|
||||
if ($server->taskworker === false) {
|
||||
Process::signal(SIGUSR1, function () use ($worker_id) {
|
||||
Timer::clearAll();
|
||||
ProcessManager::resumeAllWorkerCoroutines();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,13 +33,13 @@ class Terminal
|
||||
$dispatcher = new EventDispatcher(TerminalCommand::class);
|
||||
$dispatcher->setRuleFunction(function ($v) use ($it) {
|
||||
/** @var TerminalCommand $v */
|
||||
return $v->command == $it[0] || $v->alias == $it[0];
|
||||
return !empty($it) && ($v->command == $it[0] || $v->alias == $it[0]);
|
||||
});
|
||||
$dispatcher->setReturnFunction(function () {
|
||||
EventDispatcher::interrupt('none');
|
||||
});
|
||||
$dispatcher->dispatchEvents($it);
|
||||
if ($dispatcher->store !== 'none') {
|
||||
if ($dispatcher->store !== 'none' && $cmd !== "") {
|
||||
Console::info("Command not found: " . $cmd);
|
||||
return true;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ class Terminal
|
||||
Console::$type($log_msg);
|
||||
$r = ob_get_clean();
|
||||
$all = ManagerGM::getAllByName("terminal");
|
||||
foreach ($all as $k => $v) {
|
||||
foreach ($all as $v) {
|
||||
server()->send($v->getFd(), "\r" . $r);
|
||||
server()->send($v->getFd(), ">>> ");
|
||||
}
|
||||
@@ -72,7 +72,7 @@ class Terminal
|
||||
$class = new Terminal();
|
||||
$reader = new AnnotationReader();
|
||||
$reflection = new ReflectionClass($class);
|
||||
foreach ($reflection->getMethods() as $k => $v) {
|
||||
foreach ($reflection->getMethods() as $v) {
|
||||
$r = $reader->getMethodAnnotation($v, TerminalCommand::class);
|
||||
if ($r !== null) {
|
||||
Console::debug("adding command " . $r->command);
|
||||
@@ -176,7 +176,7 @@ class Terminal
|
||||
* @TerminalCommand(command="stop",description="停止框架")
|
||||
*/
|
||||
public function stop() {
|
||||
Process::kill(server()->master_pid, SIGTERM);
|
||||
posix_kill(server()->master_pid, SIGTERM);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,13 +18,13 @@ class ZMUtil
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function stop() {
|
||||
if (SpinLock::tryLock("_stop_signal") === false) return;
|
||||
Console::warning(Console::setColor("Stopping server...", "red"));
|
||||
if (SpinLock::tryLock('_stop_signal') === false) return;
|
||||
Console::warning(Console::setColor('Stopping server...', 'red'));
|
||||
if (Console::getLevel() >= 4) Console::trace();
|
||||
ZMAtomic::get("stop_signal")->set(1);
|
||||
ZMAtomic::get('stop_signal')->set(1);
|
||||
for ($i = 0; $i < ZM_WORKER_NUM; ++$i) {
|
||||
if (Process::kill(zm_atomic("_#worker_" . $i)->get(), 0))
|
||||
Process::kill(zm_atomic("_#worker_" . $i)->get(), SIGUSR1);
|
||||
if (Process::kill(zm_atomic('_#worker_' . $i)->get(), 0))
|
||||
Process::kill(zm_atomic('_#worker_' . $i)->get(), SIGUSR1);
|
||||
}
|
||||
server()->shutdown();
|
||||
}
|
||||
@@ -38,7 +38,7 @@ class ZMUtil
|
||||
|
||||
public static function getModInstance($class) {
|
||||
if (!isset(ZMBuf::$instance[$class])) {
|
||||
//Console::debug("Class instance $class not exist, so I created it.");
|
||||
//Console::debug('Class instance $class not exist, so I created it.');
|
||||
return ZMBuf::$instance[$class] = new $class();
|
||||
} else {
|
||||
return ZMBuf::$instance[$class];
|
||||
@@ -52,11 +52,44 @@ class ZMUtil
|
||||
public static function getReloadableFiles() {
|
||||
return array_map(
|
||||
function ($x) {
|
||||
return str_replace(DataProvider::getWorkingDir() . "/", "", $x);
|
||||
return str_replace(DataProvider::getSourceRootDir() . '/', '', $x);
|
||||
}, array_diff(
|
||||
get_included_files(),
|
||||
Framework::$loaded_files
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Psr-4标准获取目录下的所有类
|
||||
* @param $dir
|
||||
* @param $base_namespace
|
||||
* @param null|mixed $rule
|
||||
* @param bool $return_kv
|
||||
* @return String[]
|
||||
*/
|
||||
public static function getClassesPsr4($dir, $base_namespace, $rule = null, $return_kv = false) {
|
||||
// 预先读取下composer的file列表
|
||||
$composer = json_decode(file_get_contents(DataProvider::getSourceRootDir() . '/composer.json'), true);
|
||||
$classes = [];
|
||||
// 扫描目录,使用递归模式,相对路径模式,因为下面此路径要用作转换成namespace
|
||||
$files = DataProvider::scanDirFiles($dir, true, true);
|
||||
foreach ($files as $v) {
|
||||
$pathinfo = pathinfo($v);
|
||||
if ($pathinfo['extension'] == 'php') {
|
||||
if ($rule === null) { //规则未设置回调时候,使用默认的识别过滤规则
|
||||
if (substr(file_get_contents($dir . '/' . $v), 6, 6) == '#plain') continue;
|
||||
elseif (mb_substr($v, 0, 7) == 'global_' || mb_substr($v, 0, 7) == 'script_') continue;
|
||||
foreach (($composer['autoload']['files'] ?? []) as $fi) {
|
||||
if (md5_file(DataProvider::getSourceRootDir().'/'.$fi) == md5_file($dir.'/'.$v)) continue 2;
|
||||
}
|
||||
} elseif (is_callable($rule) && !($rule($dir, $pathinfo))) continue;
|
||||
$dirname = $pathinfo['dirname'] == '.' ? '' : (str_replace('/', '\\', $pathinfo['dirname']) . '\\');
|
||||
$class_name = $base_namespace . '\\' . $dirname . $pathinfo['filename'];
|
||||
if ($return_kv) $classes[$class_name] = $v;
|
||||
else $classes[] = $class_name;
|
||||
}
|
||||
}
|
||||
return $classes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use ZM\Utils\DataProvider;
|
||||
|
||||
define("ZM_START_TIME", microtime(true));
|
||||
define("ZM_DATA", ZMConfig::get("global", "zm_data"));
|
||||
define("APP_VERSION", LOAD_MODE == 1 ? (json_decode(file_get_contents(DataProvider::getWorkingDir() . "/composer.json"), true)["version"] ?? "unknown") : "unknown");
|
||||
define("APP_VERSION", LOAD_MODE == 1 ? (json_decode(file_get_contents(DataProvider::getSourceRootDir() . "/composer.json"), true)["version"] ?? "unknown") : "unknown");
|
||||
define("CRASH_DIR", ZMConfig::get("global", "crash_dir"));
|
||||
define("MAIN_WORKER", ZMConfig::get("global", "worker_cache")["worker"] ?? 0);
|
||||
if (!is_dir(ZM_DATA)) @mkdir(ZM_DATA);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php #plain
|
||||
|
||||
use Swoole\Coroutine as Co;
|
||||
use Swoole\Atomic;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\WebSocket\Server;
|
||||
use Symfony\Component\VarDumper\VarDumper;
|
||||
use ZM\API\ZMRobot;
|
||||
use ZM\API\OneBotV11;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\ConnectionManager\ManagerGM;
|
||||
use ZM\Console\Console;
|
||||
@@ -23,7 +23,7 @@ use ZM\Context\ContextInterface;
|
||||
|
||||
function getClassPath($class_name) {
|
||||
$dir = str_replace("\\", "/", $class_name);
|
||||
$dir2 = WORKING_DIR . "/src/" . $dir . ".php";
|
||||
$dir2 = DataProvider::getSourceRootDir() . "/src/" . $dir . ".php";
|
||||
//echo "@@@".$dir2.PHP_EOL;
|
||||
$dir2 = str_replace("\\", "/", $dir2);
|
||||
if (file_exists($dir2)) return $dir2;
|
||||
@@ -44,50 +44,20 @@ function explodeMsg($msg, $ban_comma = false): array {
|
||||
}
|
||||
$msgs = explode("\n", $msg);
|
||||
$ls = [];
|
||||
foreach ($msgs as $k => $v) {
|
||||
foreach ($msgs as $v) {
|
||||
if (trim($v) == "") continue;
|
||||
$ls[] = trim($v);
|
||||
}
|
||||
return $ls;
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
function unicode_decode($str) {
|
||||
return preg_replace_callback('/\\\\u([0-9a-f]{4})/i', function ($matches) {
|
||||
return mb_convert_encoding(pack("H*", $matches[1]), "UTF-8", "UCS-2BE");
|
||||
}, $str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块文件夹下的每个类文件的类名称
|
||||
* @param $dir
|
||||
* @param $indoor_name
|
||||
* @return array
|
||||
*/
|
||||
function getAllClasses($dir, $indoor_name): array {
|
||||
if (!is_dir($dir)) return [];
|
||||
$list = scandir($dir);
|
||||
$classes = [];
|
||||
if ($list[0] == '.') unset($list[0], $list[1]);
|
||||
foreach ($list as $v) {
|
||||
//echo "Finding " . $dir . $v . PHP_EOL;
|
||||
//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;
|
||||
}
|
||||
}
|
||||
return $classes;
|
||||
}
|
||||
|
||||
function matchPattern($pattern, $context): bool {
|
||||
if (mb_substr($pattern, 0, 1) == "" && mb_substr($context, 0, 1) == "")
|
||||
return true;
|
||||
@@ -124,7 +94,7 @@ function split_explode($del, $str, $divide_en = false): array {
|
||||
$str = implode($del, $str);
|
||||
//echo $str."\n";
|
||||
$ls = [];
|
||||
foreach (explode($del, $str) as $k => $v) {
|
||||
foreach (explode($del, $str) as $v) {
|
||||
if (trim($v) == "") continue;
|
||||
$ls[] = $v;
|
||||
}
|
||||
@@ -178,8 +148,8 @@ function getAnnotations(): array {
|
||||
$s = debug_backtrace()[1];
|
||||
//echo json_encode($s, 128|256);
|
||||
$list = [];
|
||||
foreach (EventManager::$events as $event => $v) {
|
||||
foreach ($v as $ks => $vs) {
|
||||
foreach (EventManager::$events as $v) {
|
||||
foreach ($v as $vs) {
|
||||
//echo get_class($vs).": ".$vs->class." => ".$vs->method.PHP_EOL;
|
||||
if ($vs->class == $s["class"] && $vs->method == $s["function"]) {
|
||||
$list[get_class($vs)][] = $vs;
|
||||
@@ -191,7 +161,7 @@ function getAnnotations(): array {
|
||||
|
||||
function set_coroutine_params($array) {
|
||||
$cid = Co::getCid();
|
||||
if ($cid == -1) die("Cannot set coroutine params at none coroutine mode.");
|
||||
if ($cid == -1) die(zm_internal_errcode("E00061")."Cannot set coroutine params at none coroutine mode.");
|
||||
if (isset(Context::$context[$cid])) Context::$context[$cid] = array_merge(Context::$context[$cid], $array);
|
||||
else Context::$context[$cid] = $array;
|
||||
foreach (Context::$context as $c => $v) {
|
||||
@@ -261,11 +231,11 @@ function call_with_catch($callable) {
|
||||
$callable();
|
||||
} catch (Exception $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught exception " . get_class($e) . " when calling \"message\": " . $error_msg);
|
||||
Console::error(zm_internal_errcode("E00033") . "Uncaught exception " . get_class($e) . ": " . $error_msg);
|
||||
Console::trace();
|
||||
} catch (Error $e) {
|
||||
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
||||
Console::error("Uncaught " . get_class($e) . " when calling \"message\": " . $error_msg);
|
||||
Console::error(zm_internal_errcode("E00033") . "Uncaught " . get_class($e) . ": " . $error_msg);
|
||||
Console::trace();
|
||||
}
|
||||
}
|
||||
@@ -298,15 +268,15 @@ function server(): ?Server {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ZMRobot
|
||||
* @return OneBotV11
|
||||
* @throws RobotNotFoundException
|
||||
* @throws ZMException
|
||||
*/
|
||||
function bot(): ZMRobot {
|
||||
function bot() {
|
||||
if (($conn = LightCacheInside::get("connect", "conn_fd")) == -2) {
|
||||
return ZMRobot::getRandom();
|
||||
return OneBotV11::getRandom();
|
||||
} elseif ($conn != -1) {
|
||||
if (($obj = ManagerGM::get($conn)) !== null) return new ZMRobot($obj);
|
||||
if (($obj = ManagerGM::get($conn)) !== null) return new OneBotV11($obj);
|
||||
else throw new RobotNotFoundException("单机器人连接模式可能连接了多个机器人!");
|
||||
} else {
|
||||
throw new RobotNotFoundException("没有任何机器人连接到框架!");
|
||||
@@ -376,7 +346,11 @@ function zm_error($obj) { Console::error($obj); }
|
||||
function zm_config($name, $key = null) { return ZMConfig::get($name, $key); }
|
||||
|
||||
function quick_reply_closure($reply) {
|
||||
return function() use ($reply){
|
||||
return function () use ($reply) {
|
||||
return $reply;
|
||||
};
|
||||
}
|
||||
|
||||
function zm_internal_errcode($code): string {
|
||||
return "[ErrCode:$code] ";
|
||||
}
|
||||
|
||||
11
src/ZM/script_phar_stub.php
Normal file
11
src/ZM/script_phar_stub.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
function loader__generated_id__() {
|
||||
$obj = json_decode(file_get_contents(__DIR__.'/zmplugin.json'), true);
|
||||
foreach(($obj["autoload-psr-4"] ?? []) as $v) {
|
||||
require_once Phar::running().'/'.$v;
|
||||
}
|
||||
|
||||
}
|
||||
echo "OK!\n";
|
||||
return json_decode(file_get_contents(__DIR__.'/zmplugin.json'), true) ?? ['zm_module' => false];
|
||||
59
src/ZM/script_setup_loader.php
Normal file
59
src/ZM/script_setup_loader.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use ZM\Annotation\Swoole\OnSetup;
|
||||
use ZM\Annotation\Swoole\SwooleHandler;
|
||||
use ZM\ConsoleApplication;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
require_once ((!is_dir(__DIR__ . '/../../vendor')) ? getcwd() : (__DIR__ . "/../..")) . "/vendor/autoload.php";
|
||||
|
||||
try {
|
||||
(new ConsoleApplication('zhamao'))->initEnv();
|
||||
|
||||
$base_path = DataProvider::getSourceRootDir();
|
||||
$scan_paths = [];
|
||||
$composer = json_decode(file_get_contents($base_path . "/composer.json"), true);
|
||||
foreach (($composer["autoload"]["psr-4"] ?? []) as $k => $v) {
|
||||
if (is_dir($base_path . "/" . $v) && !in_array($v, $composer["extra"]["exclude_annotate"] ?? [])) {
|
||||
$scan_paths[trim($k, "\\")] = $base_path . "/" . $v;
|
||||
}
|
||||
}
|
||||
$all_event_class = [];
|
||||
foreach ($scan_paths as $namespace => $autoload_path) {
|
||||
$all_event_class = array_merge($all_event_class, ZMUtil::getClassesPsr4($autoload_path, $namespace));
|
||||
}
|
||||
|
||||
$reader = new AnnotationReader();
|
||||
$event_list = [];
|
||||
$setup_list = [];
|
||||
foreach ($all_event_class as $v) {
|
||||
$reflection_class = new ReflectionClass($v);
|
||||
$methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||
foreach ($methods as $vs) {
|
||||
$method_annotations = $reader->getMethodAnnotations($vs);
|
||||
if ($method_annotations != []) {
|
||||
$annotation = $method_annotations[0];
|
||||
if ($annotation instanceof SwooleHandler) {
|
||||
$event_list[] = [
|
||||
"class" => $v,
|
||||
"method" => $vs->getName(),
|
||||
"event" => $annotation->event
|
||||
];
|
||||
} elseif ($annotation instanceof OnSetup) {
|
||||
$setup_list[] = [
|
||||
"class" => $v,
|
||||
"method" => $vs->getName()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
echo json_encode(["setup" => $setup_list, "event" => $event_list]);
|
||||
} catch (Throwable $e) {
|
||||
$stderr = fopen("php://stderr", "w");
|
||||
fwrite($stderr, zm_internal_errcode("E00031") . $e->getMessage() . " in " . $e->getFile() . " at line " . $e->getLine() . PHP_EOL);
|
||||
fclose($stderr);
|
||||
exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user