Compare commits

...

33 Commits
1.5 ... 1.6

Author SHA1 Message Date
whale
c635891e0b fix multiple middleware registered in class annotation bug 2020-07-14 12:25:32 +08:00
whale
7b7a2d7010 update to 1.6 version 2020-07-11 15:53:30 +08:00
whale
23b1f797ad fix where body compiler 2020-07-10 21:11:00 +08:00
whale
a72e0f705c add Class-based custom annotation parsing 2020-07-06 19:06:02 +08:00
whale
504934f057 update to 1.5.8 version
add fullMatch to CQCommand.php
2020-06-26 15:53:40 +08:00
whale
e976a96d0b add auto-reconnect method to terminal_listener.php
add short name of ZM_BREAKPOINT (BP)
fix loop error of syntax happened
2020-06-20 14:51:24 +08:00
whale
dc7af105f4 deprecate ModBase, it will be removed in v1.6 2020-06-15 22:11:03 +08:00
whale
fd96543268 remove stupid access token 2020-06-15 19:51:32 +08:00
whale
cebc6fb310 update to 1.5.6 version
add CQCommand filter
add ZM_DEFAULT_FETCH_MODE
2020-06-15 19:50:07 +08:00
whale
c654cf40d5 update README.md 2020-06-13 17:45:29 +08:00
whale
68cc536af3 update to 1.5.5 version 2020-06-13 17:39:50 +08:00
whale
42412fee98 update init structure 2020-06-13 17:23:53 +08:00
whale
500ec4707e update Hello default module 2020-06-13 17:08:13 +08:00
whale
b09857e3b8 add init command of start commandline 2020-06-13 16:58:45 +08:00
whale
7d79126c8f fix stupid bug of rule parsing 2020-06-13 16:19:11 +08:00
whale
a542558503 close ws connection after calling event 2020-06-13 16:05:57 +08:00
whale
6697591a22 fix a bug 2020-06-13 14:09:25 +08:00
whale
aad28f1ec4 update to 1.5.4 version 2020-06-13 12:21:27 +08:00
whale
f1949b1bd0 remove stupid debug breakpoint 2020-06-13 11:29:09 +08:00
whale
3b8aac5d8f add CQCommand alias
switch spl_autoload to psr-4 autoload
2020-06-13 11:28:31 +08:00
whale
5fd45c2542 add sql_no_exception config 2020-06-13 10:17:14 +08:00
whale
af89c1b1f6 fix some sql bugs 2020-06-10 14:39:30 +08:00
whale
3287b96f30 update to 1.5.3 version 2020-06-10 13:24:04 +08:00
whale
00a8683658 update to 1.5.3 version
fix linux terminal input bug
2020-06-10 13:23:40 +08:00
whale
c0ea068d04 update to 1.5.2 version
add ZM_VERSION const
2020-06-08 23:52:55 +08:00
whale
9ba58ff90f remove ModBase to default Module 2020-06-05 19:54:20 +08:00
whale
1a1cf0ad30 update to 1.5.1 version 2020-06-05 19:31:43 +08:00
whale
1de93b9dc1 update to 1.5.1 version
fix a warning bug
2020-06-05 19:31:18 +08:00
Whale
99e44eea3d Merge pull request #12 from 854854321/patch-1
Update ZMRequest.php
2020-06-05 19:25:20 +08:00
Whale
e67958a8d1 Update ZMRequest.php 2020-06-05 19:24:56 +08:00
Whale
23b3dc34e2 Merge pull request #11 from 854854321/master
Update README.md
2020-06-05 18:51:52 +08:00
775672d515 Update ZMRequest.php
增加了http请求 传输 数据体
2020-06-05 18:31:55 +08:00
40e17fab62 Update README.md 2020-06-05 16:02:13 +08:00
29 changed files with 1393 additions and 232 deletions

View File

@@ -2,7 +2,7 @@
[![作者QQ](https://img.shields.io/badge/作者QQ-627577391-orange.svg)]()
[![zhamao License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/zhamao-robot/zhamao-framework/blob/master/LICENSE)
[![版本](https://img.shields.io/badge/version-1.5-green.svg)]()
[![Latest Stable Version](http://img.shields.io/packagist/v/zhamao/framework.svg)](https://packagist.org/packages/zhamao/framework)
[![stupid counter](https://img.shields.io/github/search/zhamao-robot/zhamao-framework/stupid.svg)](https://github.com/zhamao-robot/zhamao-framework/search?q=stupid)
[![TODO counter](https://img.shields.io/github/search/zhamao-robot/zhamao-framework/TODO.svg)](https://github.com/zhamao-robot/zhamao-framework/search?q=TODO)
@@ -84,6 +84,6 @@ Pages托管[https://framework.zhamao.xin/](https://framework.zhamao.xin/)
欢迎随时在 HTTP-API 插件群里提问,当然更好的话可以加作者 QQ627577391或提交 Issue 进行疑难解答。
本项目在更内容时,请及时关注 GitHub 动态,更新前请将自己的模块代码做好备份。
本项目在更内容时,请及时关注 GitHub 动态,更新前请将自己的模块代码做好备份。
项目框架采用 Apache-2.0 协议开源,在分发或重写修改等操作时需遵守协议。项目模块部分(`Module` 文件夹) 在非借鉴框架内代码时可不遵守 Apache-2.0 协议进行分发和修改(声明版权)。

View File

@@ -40,6 +40,71 @@ switch ($argv[1] ?? '') {
array_shift($argv);
require_once 'systemd';
break;
case 'init':
array_shift($argv);
if (LOAD_MODE != 1) {
echo "initialization must be started with composer-project mode!\n";
exit(1);
}
$cwd = LOAD_MODE_COMPOSER_PATH;
echo "Copying default module file ...";
@mkdir($cwd . "/config");
@mkdir($cwd . "/src");
@mkdir($cwd . "/src/Custom");
@mkdir($cwd . "/src/Module");
@mkdir($cwd . "/src/Module/Example");
@mkdir($cwd . "/src/Module/Middleware");
$ls = [
"/config/global.php",
"/.gitignore",
"/config/file_header.json",
"/config/motd.txt",
"/src/Module/Example/Hello.php",
"/src/Module/Middleware/TimerMiddleware.php",
"/src/Custom/global_function.php"
];
foreach($ls as $v) {
if(!file_exists($cwd.$v)) {
echo "Copying ".$v.PHP_EOL;
copy($cwd."/vendor/zhamao/framework".$v, $cwd.$v);
}
}
$autoload = [
"psr-4" => [
"Module\\" => "src/Module",
"Custom\\" => "src/Custom"
],
"files" => [
"src/Custom/global_function.php"
]
];
$scripts = [
"server" => "vendor/bin/start server",
"server:log-debug" => "vendor/bin/start server --log-debug",
"server:log-verbose" => "vendor/bin/start server --log-verbose",
"server:log-info" => "vendor/bin/start server --log-info",
"server:log-warning" => "vendor/bin/start server --log-warning",
"server:debug-mode" => "vendor/bin/start server --debug-mode",
"systemd" => "vendor/bin/start systemd"
];
echo PHP_EOL;
if (file_exists($cwd . "/composer.json")) {
echo "Updating composer.json ...";
$composer = json_decode(file_get_contents($cwd . "/composer.json"), true);
if (!isset($composer["autoload"])) {
$composer["autoload"] = $autoload;
}
if (!isset($composer["scripts"])) {
$composer["scripts"] = $scripts;
}
file_put_contents($cwd . "/composer.json", json_encode($composer, 64 | 128 | 256));
echo PHP_EOL;
} else {
echo("Error occurred. Please check your updates.\n");
exit(1);
}
echo "success!\n";
break;
case '':
case 'framework':
case 'server':
@@ -60,6 +125,7 @@ switch ($argv[1] ?? '') {
echo "\n\n -h, --help\t\tShow this help menu";
echo "\n framework, server\tstart main framework, this is default option";
echo "\n phar-build\t\tbuild a new phar archive";
echo "\n init\t\t\tinitialize framework structure in this directory";
echo "\n systemd\t\tgenerate a new systemd \".service\" file to use\n\n";
break;
default:

View File

@@ -1,9 +1,9 @@
{
"name": "zhamao/framework",
"description": "high-performance intelligent assistant",
"description": "High performance QQ robot and web server development framework",
"minimum-stability": "stable",
"license": "Apache-2.0",
"version": "1.5",
"version": "1.6",
"authors": [
{
"name": "whale",
@@ -23,11 +23,19 @@
"swoole/ide-helper": "@dev",
"ext-mbstring": "*",
"swlib/saber": "^1.0",
"doctrine/annotations": "<1.10.2",
"doctrine/annotations": "~1.10",
"ext-json": "*",
"ext-posix": "*",
"ext-ctype": "*",
"ext-pdo": "*",
"psy/psysh": "@stable"
},
"autoload": {
"psr-4": {
"Custom\\": "src/Custom",
"Framework\\": "src/Framework",
"ZM\\": "src/ZM",
"Module\\": "src/Module"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -27,7 +27,8 @@ $config['swoole'] = [
'log_file' => $config['crash_dir'].'swoole_error.log',
'worker_num' => 1,
'dispatch_mode' => 2,
'task_worker_num' => 0
//'task_worker_num' => 1,
//'task_enable_coroutine' => true
];
/** MySQL数据库连接信息host留空则启动时不创建sql连接池 */
@@ -38,7 +39,13 @@ $config['sql_config'] = [
'sql_database' => 'db_name',
'sql_password' => '',
'sql_enable_cache' => true,
'sql_reset_cache' => '0300'
'sql_reset_cache' => '0300',
'sql_options' => [
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => false
],
'sql_no_exception' => false,
'sql_default_fetch_mode' => PDO::FETCH_BOTH // added in 1.5.6
];
/** CQHTTP连接约定的token */
@@ -79,4 +86,9 @@ $config['static_file_server'] = [
]
];
/** 注册 Swoole Server 事件注解的类列表 */
$config['server_event_handler_class'] = [
\Framework\ServerEventHandler::class, //默认不可删除,否则会不能使用框架
];
return $config;

View File

@@ -1,7 +1,3 @@
<?php
//这里写你的全局函数
function phptest(){
echo "Nothing.\n";
}

View File

@@ -184,7 +184,8 @@ class Console
$vss->callback = function(?WSConnection $conn) use ($terminal_id){
$req = ctx()->getRequest();
if($conn->getType() != "terminal") return false;
if(($req->header["x-terminal-id"] ?? "") != $terminal_id || ($req->header["x-pid"] ?? "") != posix_getpid()) {
Console::debug("Terminal fd: ".$conn->fd);
if(($req->header["x-terminal-id"] ?? "") != $terminal_id) {
$conn->close();
return false;
}
@@ -195,6 +196,7 @@ class Console
$vss2->type = "message";
$vss2->rule = "connectType:terminal";
$vss2->callback = function(?WSConnection $conn){
if ($conn === null) return false;
if($conn->getType() != "terminal") return false;
$cmd = ctx()->getFrame()->data;
self::executeCommand($cmd);

View File

@@ -3,15 +3,13 @@
namespace Framework;
use Co;
use Doctrine\Common\Annotations\AnnotationException;
use Swoole\Http\Request;
use Doctrine\Common\Annotations\AnnotationReader;
use ReflectionClass;
use ReflectionMethod;
use Swoole\Runtime;
use Swoole\WebSocket\Frame;
use ZM\Event\EventHandler;
use ZM\Annotation\Swoole\OnEvent;
use Exception;
use Swoole\WebSocket\Server;
use ZM\Http\Response;
/**
* Class FrameworkLoader
@@ -37,15 +35,22 @@ class FrameworkLoader
private $server;
public function __construct($args = []) {
if (self::$instance !== null) die("Cannot run two FrameworkLoader in one process!");
self::$instance = $this;
$this->requireGlobalFunctions();
if (LOAD_MODE == 0) define("WORKING_DIR", getcwd());
elseif(LOAD_MODE == 1) define("WORKING_DIR", realpath(__DIR__ . "/../../"));
elseif (LOAD_MODE == 1) define("WORKING_DIR", realpath(__DIR__ . "/../../"));
elseif (LOAD_MODE == 2) echo "Phar mode: " . WORKING_DIR . PHP_EOL;
$this->registerAutoloader('classLoader');
//$this->registerAutoloader('classLoader');
require_once "DataProvider.php";
if (file_exists(DataProvider::getWorkingDir() . "/vendor/autoload.php")) {
/** @noinspection PhpIncludeInspection */
require_once DataProvider::getWorkingDir() . "/vendor/autoload.php";
}
if (LOAD_MODE == 2) {
require_once FRAMEWORK_DIR . "/vendor/autoload.php";
spl_autoload_register('phar_classloader');
}
self::$settings = new GlobalConfig();
if (self::$settings->get("debug_mode") === true) {
$args[] = "--debug-mode";
@@ -71,24 +76,33 @@ class FrameworkLoader
self::$argv[] = "--disable-console-input";
}
$this->server->set($settings);
$this->server->on("WorkerStart", [$this, "onWorkerStart"]);
$this->server->on("message", function ($server, Frame $frame) {
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd);
EventHandler::callSwooleEvent("message", $server, $frame);
});
$this->server->on("request", function ($request, $response) {
$response = new Response($response);
Console::debug("Receiving Http request event, cid=" . Co::getCid());
EventHandler::callSwooleEvent("request", $request, $response);
});
$this->server->on("open", function ($server, Request $request) {
Console::debug("Calling Swoole \"open\" event from fd=" . $request->fd);
EventHandler::callSwooleEvent("open", $server, $request);
});
$this->server->on("close", function ($server, $fd) {
Console::debug("Calling Swoole \"close\" event from fd=" . $fd);
EventHandler::callSwooleEvent("close", $server, $fd);
});
$all_event_class = self::$settings->get("server_event_handler_class");
$event_list = [];
foreach ($all_event_class as $v) {
$reader = new AnnotationReader();
$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 OnEvent) {
$annotation->class = $v;
$annotation->method = $vs->getName();
$event_list[strtolower($annotation->event)] = $annotation;
}
}
}
}
foreach ($event_list as $k => $v) {
$this->server->on($k, function (...$param) use ($v) {
$c = $v->class;
//echo $c.PHP_EOL;
$c = new $c();
call_user_func_array([$c, $v->method], $param);
});
}
ZMBuf::initAtomic();
if (in_array("--remote-shell", $args)) RemoteShell::listen($this->server, "127.0.0.1");
if (in_array("--log-error", $args)) ZMBuf::$atomics["info_level"]->set(0);
@@ -100,7 +114,7 @@ class FrameworkLoader
"host: " . self::$settings->get("host") .
", port: " . self::$settings->get("port") .
", log_level: " . ZMBuf::$atomics["info_level"]->get() .
", version: " . json_decode(file_get_contents(WORKING_DIR . "/composer.json"), true)["version"] .
", version: " . ZM_VERSION .
"\nworking_dir: " . DataProvider::getWorkingDir()
);
global $motd;
@@ -111,6 +125,15 @@ class FrameworkLoader
}
if (in_array("--debug-mode", self::$argv))
Console::warning("You are in debug mode, do not use in production!");
register_shutdown_function(function() {
$error = error_get_last();
if(isset($error["type"]) && $error["type"] == 1) {
if(mb_strpos($error["message"], "require") !== false && mb_strpos($error["message"], "callback") !== false) {
echo "\e[38;5;203mYou may need to update your \"global.php\" config!\n";
echo "Please see: https://github.com/zhamao-robot/zhamao-framework/issues/15\e[m\n";
}
}
});
$this->server->start();
} catch (Exception $e) {
Console::error("Framework初始化出现错误请检查");
@@ -123,13 +146,10 @@ class FrameworkLoader
require_once __DIR__ . '/global_functions.php';
}
private function registerAutoloader(string $string) {
if (!spl_autoload_register($string)) die("Failed to register autoloader named \"$string\" !");
}
private function defineProperties() {
define("ZM_START_TIME", microtime(true));
define("ZM_DATA", self::$settings->get("zm_data"));
define("ZM_VERSION", json_decode(file_get_contents(__DIR__ . "/../../composer.json"), true)["version"] ?? "unknown");
define("CONFIG_DIR", self::$settings->get("config_dir"));
define("CRASH_DIR", self::$settings->get("crash_dir"));
@mkdir(ZM_DATA);
@@ -140,6 +160,8 @@ class FrameworkLoader
define("ZM_MATCH_NUMBER", 2);
define("ZM_MATCH_SECOND", 3);
define("ZM_BREAKPOINT", 'if(in_array("--debug-mode", \Framework\FrameworkLoader::$argv)) extract(\Psy\debug(get_defined_vars(), isset($this) ? $this : @get_called_class()));');
define("BP", ZM_BREAKPOINT);
define("ZM_DEFAULT_FETCH_MODE", self::$settings->get("sql_config")["sql_default_fetch_mode"] ?? 4);
}
private function selfCheck() {
@@ -156,16 +178,6 @@ class FrameworkLoader
return true;
}
/**
* @param \Swoole\Server $server
* @param $worker_id
* @throws AnnotationException
*/
public function onWorkerStart(\Swoole\Server $server, $worker_id) {
self::$instance = $this;
self::$run_time = microtime(true);
EventHandler::callSwooleEvent("WorkerStart", $server, $worker_id);
}
}
global $motd;

View File

@@ -0,0 +1,84 @@
<?php
namespace Framework;
use Co;
use Doctrine\Common\Annotations\AnnotationException;
use Swoole\Http\Request;
use Swoole\Server;
use Swoole\WebSocket\Frame;
use ZM\Annotation\AnnotationParser;
use ZM\Annotation\Swoole\OnEvent;
use ZM\Connection\ConnectionManager;
use ZM\Event\EventHandler;
use ZM\Http\Response;
class ServerEventHandler
{
/**
* @OnEvent("WorkerStart")
* @param Server $server
* @param $worker_id
* @throws AnnotationException
* @throws \ReflectionException
*/
public function onWorkerStart(Server $server, $worker_id) {
if ($server->taskworker === false) {
FrameworkLoader::$run_time = microtime(true);
EventHandler::callSwooleEvent("WorkerStart", $server, $worker_id);
} else {
ob_start();
AnnotationParser::registerMods();
//加载Custom目录下的自定义的内部类
ConnectionManager::registerCustomClass();
ob_get_clean();
}
}
/**
* @OnEvent("message")
* @param $server
* @param Frame $frame
* @throws AnnotationException
*/
public function onMessage($server, Frame $frame) {
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd);
EventHandler::callSwooleEvent("message", $server, $frame);
}
/**
* @OnEvent("request")
* @param $request
* @param $response
* @throws AnnotationException
*/
public function onRequest($request, $response) {
$response = new Response($response);
Console::debug("Receiving Http request event, cid=" . Co::getCid());
EventHandler::callSwooleEvent("request", $request, $response);
}
/**
* @OnEvent("open")
* @param $server
* @param Request $request
* @throws AnnotationException
*/
public function onOpen($server, Request $request) {
Console::debug("Calling Swoole \"open\" event from fd=" . $request->fd);
EventHandler::callSwooleEvent("open", $server, $request);
}
/**
* @OnEvent("close")
* @param $server
* @param $fd
* @throws AnnotationException
*/
public function onClose($server, $fd) {
Console::debug("Calling Swoole \"close\" event from fd=" . $fd);
EventHandler::callSwooleEvent("close", $server, $fd);
}
}

View File

@@ -52,6 +52,7 @@ class ZMBuf
public static $context = [];
public static $instance = [];
public static $context_class = [];
public static $server_events = [];
static function get($name, $default = null) {
return self::$cache[$name] ?? $default;

View File

@@ -8,11 +8,12 @@ use ZM\Context\ContextInterface;
use ZM\Utils\ZMUtil;
function classLoader($p) {
function phar_classloader($p){
$filepath = getClassPath($p);
if ($filepath === null)
echo "F:Warning: get class path wrongs.$p\n";
//else echo "F:DBG: Found " . $p . "\n";
if($filepath === null) {
Console::debug("F:Warning: get class path wrongs.$p");
return;
}
try {
require_once $filepath;
} catch (Exception $e) {
@@ -222,3 +223,5 @@ function zm_timer_tick($ms, callable $callable) {
}

View File

@@ -3,6 +3,7 @@
use Swoole\Coroutine\Http\Client;
Co\run(function (){
hello:
global $terminal_id, $port;
$client = new Client("127.0.0.1", $port);
$client->set(['websocket_mask' => true]);
@@ -17,8 +18,9 @@ Co\run(function (){
break;
}
if($r === false) {
echo "Unable to connect framework terminal, connection closed.\n";
break;
echo "Unable to connect framework terminal, connection closed. Trying to reconnect after 5s.\n";
sleep(5);
goto hello;
}
} else {
break;

View File

@@ -10,7 +10,6 @@ use ZM\Annotation\Http\Middleware;
use ZM\Annotation\Http\RequestMapping;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Connection\CQConnection;
use ZM\ModBase;
use ZM\Utils\ZMUtil;
/**
@@ -18,7 +17,7 @@ use ZM\Utils\ZMUtil;
* @package Module\Example
* @since 1.0
*/
class Hello extends ModBase
class Hello
{
/**
* 在机器人连接后向终端输出信息
@@ -29,9 +28,18 @@ class Hello extends ModBase
Console::info("机器人 " . $conn->getQQ() . " 已连接!");
}
/**
* 在机器人连接后向终端输出信息
* @SwooleEventAt("close",rule="connectType:qq")
*/
public function onDisconnect() {
$conn = ctx()->getConnection();
Console::info("机器人 " . $conn->getQQ() . " 已断开连接!");
}
/**
* 向机器人发送"你好",即可回复这句话
* @CQCommand("你好")
* @CQCommand(match="你好",alias={"你好啊","你是谁"})
*/
public function hello() {
return "你好啊,我是由炸毛框架构建的机器人!";
@@ -67,7 +75,6 @@ class Hello extends ModBase
* @Middleware("timer")
*/
public function timer() {
eval(ZM_BREAKPOINT);
return "This page is used as testing TimerMiddleware! Do not use it in production.";
}

View File

@@ -56,7 +56,7 @@ class AnnotationParser
$class_prefix = '';
$methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC);
$class_annotations = $reader->getClassAnnotations($reflection_class);
$middleware_addon = null;
$middleware_addon = [];
foreach ($class_annotations as $vs) {
if ($vs instanceof Closed) {
continue 2;
@@ -98,18 +98,24 @@ class AnnotationParser
ZMBuf::$events[MiddlewareClass::class][$result["name"]] = $result;
continue 2;
} elseif ($vs instanceof Middleware) {
$middleware_addon = $vs;
$middleware_addon[] = $vs;
} elseif ($vs instanceof CustomAnnotation) {
$vs->class = $reflection_class->getName();
ZMBuf::$events[get_class($vs)][] = $vs;
}
}
foreach ($methods as $vs) {
if ($middleware_addon !== null) {
Console::debug("Added middleware " . $middleware_addon->middleware . " to $v -> " . $vs->getName());
ZMBuf::$events[MiddlewareInterface::class][$v][$vs->getName()][] = $middleware_addon->middleware;
if ($middleware_addon !== []) {
foreach($middleware_addon as $value){
Console::debug("Added middleware " . $value->middleware . " to $v -> " . $vs->getName());
ZMBuf::$events[MiddlewareInterface::class][$v][$vs->getName()][] = $value->middleware;
}
}
$method_annotations = $reader->getMethodAnnotations($vs);
foreach ($method_annotations as $vss) {
if ($vss instanceof Rule) $vss = self::registerRuleEvent($vss, $vs, $reflection_class);
else $vss = self::registerMethod($vss, $vs, $reflection_class);
Console::debug("寻找 " . $vs->getName() . " -> " . get_class($vss));
if ($vss instanceof SwooleEventAt) ZMBuf::$events[SwooleEventAt::class][] = $vss;
elseif ($vss instanceof SwooleEventAfter) ZMBuf::$events[SwooleEventAfter::class][] = $vss;
@@ -168,7 +174,7 @@ class AnnotationParser
switch ($asp_name) {
case "connectType": //websocket连接类型
$func = function (?WSConnection $connection) use ($rest) {
if($connection === null) return false;
if ($connection === null) return false;
return $connection->getType() == $rest ? true : false;
};
break;
@@ -326,6 +332,7 @@ class AnnotationParser
$class = getAllClasses(DataProvider::getWorkingDir() . "/src/Custom/Annotation/", "Custom\\Annotation");
foreach ($class as $v) {
$s = DataProvider::getWorkingDir() . '/src/' . str_replace("\\", "/", $v) . ".php";
Console::debug("Requiring custom annotation " . $s);
require_once $s;
}
}

View File

@@ -19,6 +19,18 @@ class CQCommand extends AnnotationBase implements Level
public $match = "";
/** @var string */
public $regexMatch = "";
/** @var string */
public $fullMatch = "";
/** @var string[] */
public $alias = [];
/** @var string */
public $message_type = "";
/** @var int */
public $user_id = 0;
/** @var int */
public $group_id = 0;
/** @var int */
public $discuss_id = 0;
/** @var int */
public $level = 20;
@@ -32,4 +44,4 @@ class CQCommand extends AnnotationBase implements Level
*/
public function setLevel(int $level) { $this->level = $level; }
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace ZM\Annotation\Swoole;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
/**
* Class OnEvent
* @package ZM\Annotation\Swoole
* @Annotation
* @Target("METHOD")
*/
class OnEvent extends AnnotationBase
{
/**
* @var string
* @Required()
*/
public $event;
}

View File

@@ -0,0 +1,15 @@
<?php
namespace ZM\Annotation\Swoole;
/**
* Class OnTaskWorkerStart
* @package ZM\Annotation\Swoole
* @Annotation
* @Target("METHOD")
*/
class OnTaskWorkerStart
{
}

View File

@@ -90,10 +90,11 @@ class DB
/**
* @param string $line
* @param array $params
* @param int $fetch_mode
* @return mixed
* @throws DbException
*/
public static function rawQuery(string $line, $params = []) {
public static function rawQuery(string $line, $params = [], $fetch_mode = ZM_DEFAULT_FETCH_MODE) {
if (ZMBuf::get("sql_log") === true) {
$starttime = microtime(true);
}
@@ -131,7 +132,7 @@ class DB
"] " . $line . " " . json_encode($params, JSON_UNESCAPED_UNICODE) . "\n";
Coroutine::writeFile(CRASH_DIR . "sql.log", $log, FILE_APPEND);
}
return $ps->fetchAll();
return $ps->fetchAll($fetch_mode);
}
} catch (DbException $e) {
if (ZMBuf::get("sql_log") === true) {

View File

@@ -41,10 +41,11 @@ class SelectBody
}
/**
* @param int $fetch_mode
* @return null
* @throws DbException
*/
public function fetchAll() {
public function fetchAll($fetch_mode = ZM_DEFAULT_FETCH_MODE) {
if ($this->table->isCacheEnabled()) {
$rr = md5(implode(",", $this->select_thing) . serialize($this->where_thing));
if (array_key_exists($rr, $this->table->cache)) {
@@ -52,7 +53,7 @@ class SelectBody
return $this->table->cache[$rr]->getResult();
}
}
$this->execute();
$this->execute($fetch_mode);
if ($this->table->isCacheEnabled() && !in_array($rr, $this->table->cache)) {
$this->table->cache[$rr] = $this;
}
@@ -81,11 +82,12 @@ class SelectBody
}
/**
* @param int $fetch_mode
* @throws DbException
*/
public function execute() {
public function execute($fetch_mode = ZM_DEFAULT_FETCH_MODE) {
$str = $this->queryPrepare();
$this->result = DB::rawQuery($str[0], $str[1]);
$this->result = DB::rawQuery($str[0], $str[1], $fetch_mode);
}
public function getResult() { return $this->result; }

View File

@@ -9,8 +9,8 @@ trait WhereBody
protected $where_thing = [];
public function where($column, $operation_or_value, $value = null) {
if (!in_array($operation_or_value, ['=', '!='])) $this->where_thing['='][$column] = $operation_or_value;
elseif ($value !== null) $this->where_thing[$operation_or_value][$column] = $value;
if ($value !== null) $this->where_thing[$operation_or_value][$column] = $value;
elseif (!in_array($operation_or_value, ['=', '!=', '>', '<', '>=', '<=', 'IN', 'in'])) $this->where_thing['='][$column] = $operation_or_value;
else $this->where_thing['='][$column] = $operation_or_value;
return $this;
}

View File

@@ -38,9 +38,9 @@ class MessageEvent
* @throws AnnotationException
*/
public function onBefore() {
$obj_list = ZMBuf::$events[CQBefore::class]["message"];
$obj_list = ZMBuf::$events[CQBefore::class]["message"] ?? [];
foreach ($obj_list as $v) {
if($v->level < 200) break;
if ($v->level < 200) break;
EventHandler::callWithMiddleware(
$v->class,
$v->method,
@@ -65,7 +65,7 @@ class MessageEvent
}
}
foreach (ZMBuf::$events[CQBefore::class]["message"] ?? [] as $v) {
if($v->level >= 200) continue;
if ($v->level >= 200) continue;
$c = $v->class;
if (ctx()->getCache("level") != 0) continue;
EventHandler::callWithMiddleware(
@@ -100,8 +100,12 @@ class MessageEvent
$obj = [];
foreach (ZMBuf::$events[CQCommand::class] ?? [] as $v) {
/** @var CQCommand $v */
if ($v->match == "" && $v->regexMatch == "") continue;
else {
if ($v->match == "" && $v->regexMatch == "" && $v->fullMatch == "") continue;
elseif (($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == context()->getData()["user_id"])) &&
($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == (context()->getData()["group_id"] ?? 0))) &&
($v->discuss_id == 0 || ($v->discuss_id != 0 && $v->discuss_id == (context()->getData()["discuss_id"] ?? 0))) &&
($v->message_type == '' || ($v->message_type != '' && $v->message_type == context()->getData()["message_type"]))
) {
$c = $v->class;
$class_construct = [
"data" => context()->getData(),
@@ -117,6 +121,13 @@ class MessageEvent
return true;
});
return;
} elseif (in_array($word[0], $v->alias)) {
Console::debug("Calling $c -> {$v->method}");
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, $class_construct, [$word], function ($r) {
if (is_string($r)) context()->reply($r);
return true;
});
return;
} elseif ($v->regexMatch != "" && ($args = matchArgs($v->regexMatch, context()->getMessage())) !== false) {
Console::debug("Calling $c -> {$v->method}");
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, $class_construct, [$args], function ($r) {
@@ -124,6 +135,14 @@ class MessageEvent
return true;
});
return;
} elseif ($v->fullMatch != "" && (preg_match("/".$v->fullMatch."/u", ctx()->getMessage(), $args)) != 0) {
Console::debug("Calling $c -> {$v->method}");
array_shift($args);
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, $class_construct, [$args], function ($r) {
if (is_string($r)) context()->reply($r);
return true;
});
return;
}
}
}

View File

@@ -39,14 +39,17 @@ class EventHandler
switch ($event_name) {
case "workerstart":
try {
register_shutdown_function(function () {
register_shutdown_function(function () use ($param0) {
$error = error_get_last();
if ($error["type"] != 0) {
Console::error("Internal fatal error: " . $error["message"] . " at " . $error["file"] . "({$error["line"]})");
}
DataProvider::saveBuffer();
ZMBuf::$server->shutdown();
/** @var Server $param0 */
if (ZMBuf::$server === null) $param0->shutdown();
else ZMBuf::$server->shutdown();
});
ZMBuf::$server = $param0;
$r = (new WorkerStartEvent($param0, $param1))->onActivate();
Console::log("\n=== Worker #" . $param0->worker_id . " 已启动 ===\n", "gold");
$r->onAfter();
@@ -57,7 +60,9 @@ class EventHandler
ZMUtil::stop();
return;
} catch (Error $e) {
var_export($e);
Console::error("PHP Error: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
Console::error("Maybe it caused by your own code if in your own Module directory.");
Console::log($e->getTraceAsString(), 'gray');
ZMUtil::stop();
}
break;
@@ -85,7 +90,7 @@ class EventHandler
" [" . $param1->getStatusCode() . "] " . $param0->server["request_uri"]
);
if (!$param1->isEnd()) $param1->end("Internal server error: " . $e->getMessage());
Console::error("Internal server error (500), caused by uncaught exception.");
Console::error("Internal server exception (500), caused by " . get_class($e));
Console::log($e->getTraceAsString(), "gray");
} catch (Error $e) {
/** @var Response $param1 */
@@ -134,7 +139,7 @@ class EventHandler
* @throws AnnotationException
*/
public static function callCQEvent($event_data, $conn_or_response, int $level = 0) {
ctx()->setCache("level",$level);
ctx()->setCache("level", $level);
if ($level >= 5) {
Console::warning("Recursive call reached " . $level . " times");
Console::stackTrace();
@@ -172,7 +177,7 @@ class EventHandler
* @throws AnnotationException
*/
public static function callCQResponse($req) {
Console::debug("收到来自API连接的回复".json_encode($req, 128|256));
Console::debug("收到来自API连接的回复" . json_encode($req, 128 | 256));
$status = $req["status"];
$retcode = $req["retcode"];
$data = $req["data"];

View File

@@ -42,7 +42,7 @@ class RequestEvent implements SwooleEvent
$this->response->setHeader($k, $v);
}
$uri = $this->request->server["request_uri"];
Console::verbose($this->request->server["remote_addr"]." request ".$uri);
Console::verbose($this->request->server["remote_addr"] . " request " . $uri);
$uri = explode("/", $uri);
$uri = array_diff($uri, ["..", "", "."]);
$node = ZMBuf::$req_mapping;

View File

@@ -4,6 +4,7 @@
namespace ZM\Event\Swoole;
use Closure;
use Doctrine\Common\Annotations\AnnotationException;
use Framework\ZMBuf;
use Swoole\Server;
@@ -11,8 +12,6 @@ use ZM\Annotation\Swoole\SwooleEventAfter;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Connection\ConnectionManager;
use ZM\Event\EventHandler;
use ZM\ModBase;
use ZM\ModHandleType;
use ZM\Utils\ZMUtil;
class WSCloseEvent implements SwooleEvent
@@ -32,8 +31,7 @@ class WSCloseEvent implements SwooleEvent
*/
public function onActivate() {
ZMUtil::checkWait();
ConnectionManager::close($this->fd);
set_coroutine_params(["server" => $this->server, "fd" => $this->fd]);
set_coroutine_params(["server" => $this->server, "fd" => $this->fd, "connection" => ConnectionManager::get($this->fd)]);
foreach(ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
if(strtolower($v->type) == "close" && $this->parseSwooleRule($v)) {
$c = $v->class;
@@ -41,6 +39,7 @@ class WSCloseEvent implements SwooleEvent
if(context()->getCache("block_continue") === true) break;
}
}
ConnectionManager::close($this->fd);
return $this;
}
@@ -60,6 +59,11 @@ class WSCloseEvent implements SwooleEvent
}
private function parseSwooleRule($v) {
switch (explode(":", $v->rule)[0]) {
case "connectType": //websocket连接类型
if ($v->callback instanceof Closure) return call_user_func($v->callback, ConnectionManager::get($this->fd));
break;
}
return true;
}
}

View File

@@ -102,7 +102,7 @@ class WorkerStartEvent implements SwooleEvent
->withCharset('utf8mb4')
->withUsername($sql["sql_username"])
->withPassword($sql["sql_password"])
->withOptions([PDO::ATTR_STRINGIFY_FETCHES => false])
->withOptions($sql["sql_options"] ?? [PDO::ATTR_STRINGIFY_FETCHES => false])
);
DB::initTableList();
}
@@ -176,13 +176,8 @@ class WorkerStartEvent implements SwooleEvent
}
}
}
//加载composer类
Console::info("加载composer资源中");
if (file_exists(DataProvider::getWorkingDir() . "/vendor/autoload.php")) {
require_once DataProvider::getWorkingDir() . "/vendor/autoload.php";
}
if (LOAD_MODE == 2) require_once FRAMEWORK_DIR . "/vendor/autoload.php";
//remove stupid duplicate code
//加载各个模块的注解类,以及反射
Console::info("检索Module中");

View File

@@ -15,6 +15,11 @@ use ZM\Http\Response;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;
/**
* Class ModBase
* @package ZM
* @deprecated
*/
abstract class ModBase
{
/** @var Server */

View File

@@ -27,6 +27,7 @@ class ZMRequest
Console::warning("ZMRequest: url must contains scheme such as \"http(s)\"");
return false;
}
if(!isset($parse["path"])) $parse["path"] = "/";
$port = $parse["port"] ?? (($parse["scheme"] ?? "http") == "https" ? 443 : 80);
$cli = new Client($parse["host"], $port, (($parse["scheme"] ?? "http") == "https" ? true : false));
$cli->setHeaders($headers);
@@ -60,6 +61,7 @@ class ZMRequest
Console::warning("ZMRequest: url must contains scheme such as \"http(s)://\"");
return false;
}
if(!isset($parse["path"])) $parse["path"] = "/";
$port = $parse["port"] ?? (($parse["scheme"] ?? "http") == "https" ? 443 : 80);
$cli = new Client($parse["host"], $port, (($parse["scheme"] ?? "http") == "https" ? true : false));
$cli->set($set == [] ? ['timeout' => 15.0] : $set);
@@ -94,4 +96,34 @@ class ZMRequest
public static function session($option) {
return Saber::session($option);
}
public static function request($url, $attribute = [], $return_body = true) {
$parse = parse_url($url);
if (!isset($parse["host"])) {
Console::warning("ZMRequest: url must contains scheme such as \"http(s)://\"");
return false;
}
if(!isset($parse["path"])) $parse["path"] = "/";
$port = $parse["port"] ?? (($parse["scheme"] ?? "http") == "https" ? 443 : 80);
$cli = new Client($parse["host"], $port, (($parse["scheme"] ?? "http") == "https" ? true : false));
$cli->set($attribute["set"] ?? ["timeout" => 15.0]);
$cli->setMethod($attribute["method"] ?? "GET");
$cli->setHeaders($attribute["headers"] ?? []);
if(isset($attribute["data"])) $cli->setData($attribute["data"]);
if(isset($attribute["file"])) {
foreach($attribute["file"] as $k => $v) {
$cli->addFile($v["path"], $v["name"], $v["mime_type"] ?? null, $v["filename"] ?? null, $v["offset"] ?? 0, $v["length"] ?? 0);
}
}
$cli->execute($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""));
if ($return_body) {
if ($cli->errCode != 0 || $cli->statusCode != 200) return false;
$a = $cli->body;
$cli->close();
return $a;
} else {
$cli->close();
return $cli;
}
}
}

View File

@@ -24,7 +24,7 @@ class ZMUtil
public static function stop($without_shutdown = false) {
Console::info(Console::setColor("Stopping server...", "red"));
foreach (ZMBuf::$server->connections as $v) {
foreach ((ZMBuf::$server->connections ?? []) as $v) {
ZMBuf::$server->close($v);
}
DataProvider::saveBuffer();

View File

@@ -29,6 +29,7 @@ class ZMWebSocket
Console::warning("ZMRequest: url must contains scheme such as \"ws(s)://\"");
return;
}
if (!isset($this->parse["path"])) $this->parse["path"] = "/";
$port = $this->parse["port"] ?? (($this->parse["scheme"] ?? "ws") == "wss" ? 443 : 80);
$this->client = new Client($this->parse["host"], $port, (($this->parse["scheme"] ?? "ws") == "wss" ? true : false));
$this->client->set($set);
@@ -85,18 +86,18 @@ class ZMWebSocket
}
}
if(!debug_backtrace()) {
if (!debug_backtrace()) {
go(function () {
require_once __DIR__ . "/../../Framework/Console.php";
$cli = new ZMWebSocket("ws://127.0.0.1:20001/");
if (!$cli->is_available) die("Error!\n");
$cli->onMessage(function ($frame){
$cli->onMessage(function (Frame $frame) {
var_dump($frame);
});
$cli->onClose(function($client){
$cli->onClose(function () {
echo "Connection closed.\n";
});
if($cli->upgrade()){
if ($cli->upgrade()) {
echo "成功连接!\n";
} else {
echo "连接失败!\n";