initial 2.0 commit

This commit is contained in:
jerry 2020-08-31 10:11:06 +08:00
parent b6756179f5
commit beb1f5f063
69 changed files with 1237 additions and 2090 deletions

View File

@ -1,60 +0,0 @@
#!/usr/bin/env php
<?php /** @since 1.2.1 */
global $version;
echo "version: " . ($version = json_decode(file_get_contents(__DIR__ . "/../composer.json"), true)["version"]) . PHP_EOL;
switch ($argv[1] ?? '') {
case '--normal':
case '':
build();
break;
case '--help':
case '-h':
default:
echo "\nzhamao-framework Phar builder.\n";
echo "\nUsage: " . $argv[0] . " [OPTION]";
echo "\n\n -h, --help\t\tShow this help menu";
echo "\n --with-wechat-patch\tReplace ModBase with wechat patch version and build your own phar package";
echo "\n --normal\t\tBuild your own phar package as normal options\n\n";
break;
}
function build($with_wechat_patch = false) {
if (ini_get('phar.readonly') == 1) {
die("You need to set \"phar.readonly\" to \"Off\"!\nSee: https://stackoverflow.com/questions/34667606/cant-enable-phar-writing\n");
}
$filename = "server.phar";
@unlink(__DIR__ . '/../resources/' . $filename);
$phar = new Phar(__DIR__ . '/../resources/' . $filename);
$phar->startBuffering();
$src = realpath(__DIR__ . '/../');
$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');
if ($with_wechat_patch) {
global $wechat_patch;
$wechat = base64_decode($wechat_patch);
} else {
$wechat = false;
}
if ($wechat !== false) {
echo "Using wechat patch.\n";
$modbase = file_get_contents($src . '/src/ZM/ModBase.php');
unlink($src . '/src/ZM/ModBase.php');
}
$phar->buildFromDirectory($src);
$phar->addFromString('tmp/Hello.php.bak', $hello);
$phar->addFromString('tmp/TimerMiddleware.php.bak', $middleware);
if ($wechat !== false) {
$phar->addFromString('src/ZM/ModBase.php', $wechat);
file_put_contents($src . '/src/ZM/ModBase.php', $modbase);
}
//$phar->compressFiles(Phar::GZ);
$phar->setStub($phar->createDefaultStub('phar-starter.php'));
$phar->stopBuffering();
file_put_contents($src . '/src/Module/Example/Hello.php', $hello);
file_put_contents($src . '/src/Module/Middleware/TimerMiddleware.php', $middleware);
echo "Successfully built. Location: " . $src . "/resources/$filename\n";
}

144
bin/start
View File

@ -1,135 +1,29 @@
#!/usr/bin/env php
<?php
use Framework\FrameworkLoader;
use Scheduler\Scheduler;
use ZM\ConsoleApplication;
require __DIR__ . '/../src/Framework/FrameworkLoader.php';
require __DIR__ . '/../src/Scheduler/Scheduler.php';
// 这行是用于开发者自己电脑的调试功能
Swoole\Coroutine::set([
'max_coroutine' => 30000,
]);
global $vendor_mode;
$vendor_mode = false;
if (mb_strpos(__DIR__, getcwd()) !== false && substr(str_replace(getcwd(), "", __DIR__), 0, 8) == "/vendor/") {
$symbol = sha1(is_file("/flag") ? file_get_contents("/flag") : '1') == '6252c0ec7fcbd544c3d6f5f0a162f60407d7a896' || mb_strpos(getcwd(), "/private/tmp");
// 首先得判断是直接从library模式启动的框架还是从composer引入library启动的框架
// 判断方法:判断当前目录上面有没有 /vendor 目录,如果没有 /vendor 目录说明是从 composer 引入的
// 否则就是直接从 framework 项目启动的
if (!is_dir(__DIR__ . '/../vendor') || $symbol) {
define("LOAD_MODE", 1); //composer项目模式
define("LOAD_MODE_COMPOSER_PATH", getcwd());
/** @noinspection PhpIncludeInspection */
require_once LOAD_MODE_COMPOSER_PATH . "/vendor/autoload.php";
} elseif (substr(__DIR__, 0, 7) != 'phar://') {
define("LOAD_MODE", 2); //phar模式
// 会废弃phar启动的方式在2.0
} else {
define("LOAD_MODE", 0); //正常模式
}
date_default_timezone_set("Asia/Shanghai");
switch ($argv[1] ?? '') {
case 'scheduler':
case 'timer':
go(function () {
try {
new Scheduler(Scheduler::REMOTE);
} catch (Exception $e) {
die($e->getMessage());
}
});
break;
case 'phar-build':
array_shift($argv);
require_once 'phar-build';
break;
case 'systemd':
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':
if (!is_dir(__DIR__ . '/../vendor/') && LOAD_MODE == 0) {
echo "Warning: you have not update composer!\n";
exec("composer update", $out, $var);
if ($var != 0) {
echo "You need to run \"composer update\" at root of zhamao-framework!\n";
die;
}
}
$loader = new FrameworkLoader($argv);
break;
case '--help':
case '-h':
echo "\nUsage: " . $argv[0] . " [OPTION]\n";
echo "\nzhamao-framework start script, provides several startup arguments.";
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:
echo "Unknown option \"{$argv[1]}\"!\n\"--help\" for more information\n";
break;
define("LOAD_MODE", 0);
require_once __DIR__ . "/../vendor/autoload.php";
}
// 终端的命令行功能启动!!
$application = new ConsoleApplication("zhamao-framework");
$application->initEnv();
$application->run();

View File

@ -3,7 +3,7 @@
"description": "High performance QQ robot and web server development framework",
"minimum-stability": "stable",
"license": "Apache-2.0",
"version": "1.6.2",
"version": "2.0.0-a1",
"authors": [
{
"name": "whale",
@ -28,14 +28,24 @@
"ext-posix": "*",
"ext-ctype": "*",
"ext-pdo": "*",
"psy/psysh": "@stable"
"psy/psysh": "@stable",
"symfony/console": "^5.1",
"symfony/polyfill-ctype": "^1.18",
"zhamao/connection-manager": "^1.0",
"zhamao/console": "^1.0",
"zhamao/config": "^1.0"
},
"autoload": {
"psr-4": {
"Custom\\": "src/Custom",
"Framework\\": "src/Framework",
"ZM\\": "src/ZM",
"Module\\": "src/Module"
}
},
"files": [
"src/ZM/global_functions.php"
]
},
"require-dev": {
"phpunit/phpunit": "^9.3"
}
}

20
config/console_color.json Normal file
View File

@ -0,0 +1,20 @@
{
"default": {
"success": "green",
"info": "lightblue",
"warning": "yellow",
"error": "red",
"verbose": "blue",
"debug": "gray",
"trace": "gray"
},
"white-term": {
"success": "green",
"info": "black",
"warning": "yellow",
"error": "red",
"verbose": "blue",
"debug": "gray",
"trace": "gray"
}
}

View File

@ -27,6 +27,7 @@ $config['swoole'] = [
'log_file' => $config['crash_dir'] . 'swoole_error.log',
'worker_num' => 1,
'dispatch_mode' => 2,
'max_coroutine' => 30000,
//'task_worker_num' => 1,
//'task_enable_coroutine' => true
];
@ -68,9 +69,10 @@ $config['init_atomics'] = [
'out_count' => 0, //消息发送调用send_*_msg的统计数量
'reload_time' => 0, //调用reload功能统计数量
'wait_msg_id' => 0, //协程挂起id自增
'info_level' => 2, //终端显示的log等级
];
$config["info_level"] = 2;
/** 自动保存的缓存保存时间(秒) */
$config['auto_save_interval'] = 900;
@ -88,7 +90,7 @@ $config['static_file_server'] = [
/** 注册 Swoole Server 事件注解的类列表 */
$config['server_event_handler_class'] = [
\Framework\ServerEventHandler::class,
\ZM\Event\ServerEventHandler::class,
];
return $config;

View File

@ -1,85 +0,0 @@
<?php
global $is_phar;
use Framework\FrameworkLoader;
$is_phar = true;
if (substr(__DIR__, 0, 7) != 'phar://') {
die("You can not run this script directly!\n");
}
testEnvironment();
spl_autoload_register(function ($class) {
//echo $class."\n";
$exp = str_replace("\\", '/', $class);
$exp = __DIR__ . '/src/' . $exp . '.php';
if (is_file($exp)) {
require_once $exp;
}
});
loadPhp(__DIR__ . '/src');
Swoole\Coroutine::set([
'max_coroutine' => 30000,
]);
date_default_timezone_set("Asia/Shanghai");
define('WORKING_DIR', __DIR__);
define('FRAMEWORK_DIR', __DIR__);
define('LOAD_MODE', 2);
$s = new FrameworkLoader($argv);
function loadPhp($dir) {
$dirs = scandir($dir);
foreach ($dirs as $v) {
$path = $dir . '/' . $v;
if (is_dir($path)) {
loadPhp($path);
} else {
if (pathinfo($dir . '/' . $v)['extension'] == 'php') {
if(pathinfo($dir . '/' . $v)['basename'] == 'terminal_listener.php') continue;
//echo 'loading '.$path.PHP_EOL;
require_once $path;
}
}
}
}
function testEnvironment() {
$current_dir = realpath('.');
@mkdir($current_dir . '/config/');
if (!is_file($current_dir . '/config/global.php')) {
echo "Exporting default global config...\n";
$global = file_get_contents(__DIR__ . '/config/global.php');
$global = str_replace("WORKING_DIR", 'realpath(__DIR__ . "/../")', $global);
file_put_contents($current_dir . '/config/global.php', $global);
}
if (!is_file($current_dir . '/config/file_header.json')) {
echo "Exporting default file_header config...\n";
$global = file_get_contents(__DIR__ . '/config/file_header.json');
file_put_contents($current_dir . '/config/file_header.json', $global);
}
if (!is_dir($current_dir . '/resources')) mkdir($current_dir . '/resources');
if (!is_dir($current_dir . '/src')) mkdir($current_dir . '/src');
if (!is_dir($current_dir . '/src')) mkdir($current_dir . '/src');
if (!is_dir($current_dir . '/src/Module')) {
mkdir($current_dir . '/src/Module');
mkdir($current_dir . '/src/Module/Example');
file_put_contents($current_dir . '/src/Module/Example/Hello.php', file_get_contents(__DIR__ . '/tmp/Hello.php.bak'));
mkdir($current_dir . '/src/Module/Middleware');
file_put_contents($current_dir . '/src/Module/Middleware/TimerMiddleware.php', file_get_contents(__DIR__ . '/tmp/TimerMiddleware.php.bak'));
}
if (!is_dir($current_dir . '/src/Custom')) {
mkdir($current_dir . '/src/Custom');
mkdir($current_dir . '/src/Custom/Annotation');
mkdir($current_dir . '/src/Custom/Connection');
file_put_contents($current_dir . '/src/Custom/global_function.php', "<?php\n\n//这里写你的全局方法");
}
}

View File

@ -4,6 +4,7 @@
namespace Custom\Annotation;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\Interfaces\CustomAnnotation;
/**
@ -12,8 +13,8 @@ use ZM\Annotation\Interfaces\CustomAnnotation;
* @Target("ALL")
* @package Custom\Annotation
*/
class Example implements CustomAnnotation
class Example extends AnnotationBase implements CustomAnnotation
{
/** @var string */
public $str;
}
}

View File

@ -1,14 +0,0 @@
<?php
namespace Custom\Connection;
use ZM\Connection\WSConnection;
class CustomConnection extends WSConnection
{
public function getType() {
return "custom";
}
}

View File

@ -1,277 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: jerry
* Date: 2018/2/10
* Time: 下午6:13
*/
namespace Framework;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Connection\WSConnection;
use ZM\Utils\ZMUtil;
use Exception;
class Console
{
/**
* @var false|resource
*/
public static $console_proc = null;
public static $pipes = [];
static function setColor($string, $color = "") {
switch ($color) {
case "red":
return "\x1b[38;5;203m" . $string . "\x1b[m";
case "green":
return "\x1b[38;5;83m" . $string . "\x1b[m";
case "yellow":
return "\x1b[38;5;227m" . $string . "\x1b[m";
case "blue":
return "\033[34m" . $string . "\033[0m";
case "pink": // I really don't know what stupid color it is.
case "lightpurple":
return "\x1b[38;5;207m" . $string . "\x1b[m";
case "lightblue":
return "\x1b[38;5;87m" . $string . "\x1b[m";
case "gold":
return "\x1b[38;5;214m" . $string . "\x1b[m";
case "gray":
return "\x1b[38;5;59m" . $string . "\x1b[m";
case "lightlightblue":
return "\x1b[38;5;63m" . $string . "\x1b[m";
default:
return $string;
}
}
static function error($obj, $head = null) {
if ($head === null) $head = date("[H:i:s] ") . "[E] ";
if (ZMBuf::$info_level !== null && in_array(ZMBuf::$info_level->get(), [1, 2])) {
$trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
$trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
}
if (!is_string($obj)) {
if (isset($trace)) {
var_dump($obj);
return;
} else $obj = "{Object}";
}
echo(self::setColor($head . ($trace ?? "") . $obj, "red") . "\n");
}
static function warning($obj, $head = null) {
if ($head === null) $head = date("[H:i:s]") . " [W] ";
if (ZMBuf::$info_level !== null && in_array(ZMBuf::$info_level->get(), [1, 2])) {
$trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
$trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
}
if (ZMBuf::$atomics["info_level"]->get() >= 1) {
if (!is_string($obj)) {
if (isset($trace)) {
var_dump($obj);
return;
} else $obj = "{Object}";
}
echo(self::setColor($head . ($trace ?? "") . $obj, in_array("--white-term", FrameworkLoader::$argv) ? "blue" : "yellow") . "\n");
}
}
static function info($obj, $head = null) {
if ($head === null) $head = date("[H:i:s] ") . "[I] ";
if (ZMBuf::$info_level !== null && in_array(ZMBuf::$info_level->get(), [1, 2])) {
$trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
$trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
}
if (ZMBuf::$atomics["info_level"]->get() >= 2) {
if (!is_string($obj)) {
if (isset($trace)) {
var_dump($obj);
return;
} else $obj = "{Object}";
}
echo(self::setColor($head . ($trace ?? "") . $obj, in_array("--white-term", FrameworkLoader::$argv) ? "black" : "lightblue") . "\n");
}
}
static function success($obj, $head = null) {
if ($head === null) $head = date("[H:i:s] ") . "[S] ";
if (ZMBuf::$info_level !== null && in_array(ZMBuf::$info_level->get(), [1, 2])) {
$trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
$trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
}
if (ZMBuf::$atomics["info_level"]->get() >= 2) {
if (!is_string($obj)) {
if (isset($trace)) {
var_dump($obj);
return;
} else $obj = "{Object}";
}
echo(self::setColor($head . ($trace ?? "") . $obj, "green") . "\n");
}
}
static function verbose($obj, $head = null) {
if ($head === null) $head = date("[H:i:s] ") . "[V] ";
if (ZMBuf::$atomics["info_level"]->get() >= 3) {
if (!is_string($obj)) {
if (isset($trace)) {
var_dump($obj);
return;
} else $obj = "{Object}";
}
echo(self::setColor($head . ($trace ?? "") . $obj, "blue") . "\n");
}
}
static function debug($msg) {
if (ZMBuf::$atomics["info_level"]->get() >= 4) Console::log(date("[H:i:s] ") . "[D] " . $msg, 'gray');
}
static function log($obj, $color = "") {
if (!is_string($obj)) var_dump($obj);
else echo(self::setColor($obj, $color) . "\n");
}
static function stackTrace() {
$log = "Stack trace:\n";
$trace = debug_backtrace();
//array_shift($trace);
foreach ($trace as $i => $t) {
if (!isset($t['file'])) {
$t['file'] = 'unknown';
}
if (!isset($t['line'])) {
$t['line'] = 0;
}
if (!isset($t['function'])) {
$t['function'] = 'unknown';
}
$log .= "#$i {$t['file']}({$t['line']}): ";
if (isset($t['object']) and is_object($t['object'])) {
$log .= get_class($t['object']) . '->';
}
$log .= "{$t['function']}()\n";
}
$log = Console::setColor($log, "gray");
echo $log;
}
static function listenConsole() {
if (in_array('--disable-console-input', FrameworkLoader::$argv) || in_array('--debug-mode', FrameworkLoader::$argv)) {
self::info("ConsoleCommand disabled.");
return;
}
global $terminal_id;
global $port;
$port = ZMBuf::globals("port");
$vss = new SwooleEventAt();
$vss->type = "open";
$vss->level = 256;
$vss->rule = "connectType:terminal";
$terminal_id = call_user_func(function () {
try {
$data = random_bytes(16);
} catch (Exception $e) {
return "";
}
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return strtoupper(vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)));
});
$vss->callback = function(?WSConnection $conn) use ($terminal_id){
$req = ctx()->getRequest();
if($conn->getType() != "terminal") return false;
Console::debug("Terminal fd: ".$conn->fd);
if(($req->header["x-terminal-id"] ?? "") != $terminal_id) {
$conn->close();
return false;
}
return false;
};
ZMBuf::$events[SwooleEventAt::class][] = $vss;
$vss2 = new SwooleEventAt();
$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);
return false;
};
ZMBuf::$events[SwooleEventAt::class][] = $vss2;
go(function () {
global $terminal_id, $port;
$descriptorspec = array(
0 => STDIN,
1 => STDOUT,
2 => STDERR
);
self::$console_proc = proc_open('php -r \'$terminal_id = "'.$terminal_id.'";$port = '.$port.';require "'.__DIR__.'/terminal_listener.php";\'', $descriptorspec, $pipes);
});
}
/**
* @param string $cmd
* @return bool
*/
private static function executeCommand(string $cmd) {
$it = explodeMsg($cmd);
switch ($it[0] ?? '') {
case 'logtest':
Console::log(date("[H:i:s]") . " [L] This is normal msg. (0)");
Console::error("This is error msg. (0)");
Console::warning("This is warning msg. (1)");
Console::info("This is info msg. (2)");
Console::success("This is success msg. (2)");
Console::verbose("This is verbose msg. (3)");
Console::debug("This is debug msg. (4)");
return true;
case 'call':
$class_name = $it[1];
$function_name = $it[2];
$class = new $class_name([]);
call_user_func_array([$class, $function_name], []);
return true;
case 'bc':
$code = base64_decode($it[1] ?? '', true);
try {
eval($code);
} catch (Exception $e) {
}
return true;
case 'echo':
Console::info($it[1]);
return true;
case 'color':
Console::log($it[2], $it[1]);
return true;
case 'stop':
ZMUtil::stop();
return false;
case 'reload':
case 'r':
ZMUtil::reload();
return false;
case 'save':
$origin = ZMBuf::$atomics["info_level"]->get();
//ZMBuf::$atomics["info_level"]->set(3);
DataProvider::saveBuffer();
//ZMBuf::$atomics["info_level"]->set($origin);
return true;
case '':
return true;
default:
Console::info("Command not found: " . $cmd);
return true;
}
}
public static function withSleep(string $string, int $int) {
self::info($string);
sleep($int);
}
}

View File

@ -1,186 +0,0 @@
<?php
namespace Framework;
use Doctrine\Common\Annotations\AnnotationReader;
use ReflectionClass;
use ReflectionMethod;
use Swoole\Runtime;
use ZM\Annotation\Swoole\OnEvent;
use Exception;
use Swoole\WebSocket\Server;
/**
* Class FrameworkLoader
* Everything is beginning from here
* @package Framework
*/
class FrameworkLoader
{
/** @var GlobalConfig */
public static $settings;
/** @var FrameworkLoader|null */
public static $instance = null;
/** @var float|string */
public static $run_time;
/**
* @var array
*/
public static $argv;
/** @var Server */
private $server;
public function __construct($args = []) {
$this->requireGlobalFunctions();
if (LOAD_MODE == 0) define("WORKING_DIR", getcwd());
elseif (LOAD_MODE == 1) define("WORKING_DIR", realpath(__DIR__ . "/../../"));
elseif (LOAD_MODE == 2) echo "Phar mode: " . WORKING_DIR . PHP_EOL;
//$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";
$args[] = "--disable-console-input";
}
self::$argv = $args;
if (!in_array("--debug-mode", self::$argv)) {
Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL);
}
self::$settings = new GlobalConfig();
ZMBuf::$globals = self::$settings;
if (!self::$settings->success) die("Failed to load global config. Please check config/global.php file");
$this->defineProperties();
//start swoole Framework
$this->selfCheck();
try {
$this->server = new Server(self::$settings->get("host"), self::$settings->get("port"));
$settings = self::$settings->get("swoole");
if (in_array("--daemon", $args)) {
$settings["daemonize"] = 1;
Console::log("已启用守护进程,输出重定向到 " . $settings["log_file"]);
self::$argv[] = "--disable-console-input";
}
$this->server->set($settings);
$all_event_class = self::$settings->get("server_event_handler_class") ?? [];
if (!in_array(ServerEventHandler::class, $all_event_class)) {
$all_event_class[] = ServerEventHandler::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);
if (in_array("--log-warning", $args)) ZMBuf::$atomics["info_level"]->set(1);
if (in_array("--log-info", $args)) ZMBuf::$atomics["info_level"]->set(2);
if (in_array("--log-verbose", $args)) ZMBuf::$atomics["info_level"]->set(3);
if (in_array("--log-debug", $args)) ZMBuf::$atomics["info_level"]->set(4);
Console::log(
"host: " . self::$settings->get("host") .
", port: " . self::$settings->get("port") .
", log_level: " . ZMBuf::$atomics["info_level"]->get() .
", version: " . ZM_VERSION .
"\nworking_dir: " . DataProvider::getWorkingDir()
);
global $motd;
if (!file_exists(DataProvider::getWorkingDir() . "/config/motd.txt")) {
echo $motd;
} else {
echo file_get_contents(DataProvider::getWorkingDir() . "/config/motd.txt");
}
if (in_array("--debug-mode", self::$argv))
Console::warning("You are in debug mode, do not use in production!");
$this->server->start();
} catch (Exception $e) {
Console::error("Framework初始化出现错误请检查");
Console::error($e->getMessage());
die;
}
}
private function requireGlobalFunctions() {
require_once __DIR__ . '/global_functions.php';
}
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);
@mkdir(CONFIG_DIR);
@mkdir(CRASH_DIR);
define("ZM_MATCH_ALL", 0);
define("ZM_MATCH_FIRST", 1);
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() {
if (!extension_loaded("swoole")) die("Can not find swoole extension.\n");
if (version_compare(SWOOLE_VERSION, "4.4.13") == -1) die("You must install swoole version >= 4.4.13 !");
//if (!extension_loaded("gd")) die("Can not find gd extension.\n");
if (!extension_loaded("sockets")) die("Can not find sockets extension.\n");
if (!extension_loaded("ctype")) die("Can not find ctype extension.\n");
if (!function_exists("mb_substr")) die("Can not find mbstring extension.\n");
if (substr(PHP_VERSION, 0, 1) != "7") die("PHP >=7 required.\n");
//if (!function_exists("curl_exec")) die("Can not find curl extension.\n");
//if (!class_exists("ZipArchive")) die("Can not find Zip extension.\n");
//if (!file_exists(CRASH_DIR . "last_error.log")) die("Can not find log file.\n");
return true;
}
}
global $motd;
$motd = <<<EOL
______
|__ / |__ __ _ _ __ ___ __ _ ___
/ /| '_ \ / _` | '_ ` _ \ / _` |/ _ \
/ /_| | | | (_| | | | | | | (_| | (_) |
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/
EOL;

View File

@ -1,37 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: jerry
* Date: 2019-03-16
* Time: 13:58
*/
namespace Framework;
/**
* 请不要diss此class的语法。可能写的很糟糕。
* Class GlobalConfig
*/
class GlobalConfig
{
private $config = null;
public $success = false;
public function __construct() {
/** @noinspection PhpIncludeInspection */
include_once DataProvider::getWorkingDir() . '/config/global.php';
global $config;
$this->success = true;
$this->config = $config;
}
public function get($key) {
$r = $this->config[$key] ?? null;
if ($r === null) return null;
return $r;
}
public function getAll() {
return $this->config;
}
}

View File

@ -4,12 +4,12 @@
namespace Module\Example;
use Framework\Console;
use ZM\ConnectionManager\ConnectionObject;
use ZM\Console\Console;
use ZM\Annotation\CQ\CQCommand;
use ZM\Annotation\Http\Middleware;
use ZM\Annotation\Http\RequestMapping;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Connection\CQConnection;
use ZM\Utils\ZMUtil;
/**
@ -24,8 +24,8 @@ class Hello
* @SwooleEventAt("open",rule="connectType:qq")
* @param $conn
*/
public function onConnect(CQConnection $conn) {
Console::info("机器人 " . $conn->getQQ() . " 已连接!");
public function onConnect(ConnectionObject $conn) {
Console::info("机器人 " . $conn->getOption("connect_id") . " 已连接!");
}
/**
@ -34,7 +34,7 @@ class Hello
*/
public function onDisconnect() {
$conn = ctx()->getConnection();
Console::info("机器人 " . $conn->getQQ() . " 已断开连接!");
Console::info("机器人 " . $conn->getOption("connect_id") . " 已断开连接!");
}
/**
@ -60,13 +60,13 @@ class Hello
*/
public function randNum($arg) {
// 获取第一个数字类型的参数
$num1 = context()->getArgs($arg, ZM_MATCH_NUMBER, "请输入第一个数字");
$num1 = ctx()->getArgs($arg, ZM_MATCH_NUMBER, "请输入第一个数字");
// 获取第二个数字类型的参数
$num2 = context()->getArgs($arg, ZM_MATCH_NUMBER, "请输入第二个数字");
$num2 = ctx()->getArgs($arg, ZM_MATCH_NUMBER, "请输入第二个数字");
$a = min(intval($num1), intval($num2));
$b = max(intval($num1), intval($num2));
// 回复用户结果
context()->reply("随机数是:".mt_rand($a, $b));
ctx()->reply("随机数是:".mt_rand($a, $b));
}
/**
@ -94,6 +94,6 @@ class Hello
*/
public function closeUnknownConn() {
Console::info("Unknown connection , I will close it.");
context()->getConnection()->close();
server()->close(ctx()->getConnection()->getFd());
}
}

View File

@ -2,10 +2,10 @@
namespace Module\Middleware;
use Framework\Console;
use ZM\Annotation\Http\After;
use ZM\Annotation\Http\Before;
use ZM\Annotation\Http\MiddlewareClass;
use ZM\Console\Console;
use ZM\Http\MiddlewareInterface;
/**

View File

@ -1,29 +0,0 @@
<?php
namespace Scheduler;
use Swoole\Coroutine\Http\Client;
use Swoole\WebSocket\Frame;
class MessageEvent
{
/**
* @var Frame
*/
private $frame;
/**
* @var Client
*/
private $client;
public function __construct(Client $client, Frame $frame) {
$this->client = $client;
$this->frame = $frame;
}
public function onActivate() {
//TODO: 写Scheduler计时器内的处理逻辑
}
}

View File

@ -1,140 +0,0 @@
<?php
namespace Scheduler;
use Exception;
use Framework\Console;
use Framework\GlobalConfig;
use Swoole\Coroutine;
use Swoole\Coroutine\Http\Client;
use Swoole\Process;
use Swoole\WebSocket\Frame;
class Scheduler
{
const PROCESS = 1;
const REMOTE = 2;
/**
* @var Process
*/
private $process = null;
/**
* @var int
*/
private $m_pid;
private $pid;
/**
* @var Scheduler
*/
private static $instance;
/**
* @var GlobalConfig
*/
private $settings;
/**
* @var Client
*/
private $client;
public function __construct($method = self::PROCESS, $option = []) {
if (self::$instance !== null) die("Cannot run two scheduler in on process!");
self::$instance = $this;
if ($method == self::PROCESS) $this->initProcess();
elseif ($method == self::REMOTE) $this->initRemote();
}
private function initProcess() { //TODO: 完成Process模式的代码
$m_pid = posix_getpid();
$this->process = new Process(function (Process $worker) use ($m_pid) { self::onWork($worker, $m_pid); }, false, 2, true);
$this->pid = $this->process->start();
while (1) {
$ret = Process::wait();
if ($ret) {
$this->process = new Process(function (Process $worker) use ($m_pid) { self::onWork($worker, $m_pid); }, false, 2, true);
$this->pid = $this->process->start();
echo "Reboot done.\n";
}
}
}
private function initRemote() {
define('WORKING_DIR', __DIR__ . '../..');
$this->requireGlobalFunctions();
$this->registerAutoloader('classLoader');
$this->settings = new GlobalConfig();
if (!$this->settings->success) die("Failed to load global config. Please check config/global.php file");
$this->defineProperties();
//start swoole Framework
$this->selfCheck();
try {
$host = $this->settings->get("scheduler")["host"];
$port = $this->settings->get("scheduler")["port"];
$token = $this->settings->get("scheduler")["token"];
$this->client = new Client($host, $port);
$path = "/" . ($token != "" ? ("?token=" . urlencode($token)) : "");
while (true) {
if ($this->client->upgrade($path)) {
while (true) {
$recv = $this->client->recv();
if ($recv instanceof Frame) {
(new MessageEvent($this->client, $recv))->onActivate();
} else {
break;
}
}
} else {
Console::warning("无法连接Framework将在5秒后重连...");
Coroutine::sleep(5);
}
}
} catch (Exception $e) {
Console::error($e);
}
}
private function requireGlobalFunctions() {
/** @noinspection PhpIncludeInspection */
require WORKING_DIR . '/src/Framework/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", $this->settings->get("zm_data"));
//define("CONFIG_DIR", $this->settings->get("config_dir"));
define("CRASH_DIR", $this->settings->get("crash_dir"));
}
private function selfCheck() {
if (!extension_loaded("swoole")) die("Can not find swoole extension.\n");
if (!extension_loaded("sockets")) die("Can not find sockets extension.\n");
if (!function_exists("mb_substr")) die("Can not find mbstring extension.\n");
if (substr(PHP_VERSION, 0, 1) != "7") die("PHP >=7 required.\n");
//if (!class_exists("ZipArchive")) die("Can not find Zip extension.\n");
if (!file_exists(CRASH_DIR . "last_error.log")) die("Can not find log file.\n");
return true;
}
private static function onWork(Process $worker, $m_pid) {
swoole_set_process_name('php-scheduler');
for ($j = 0; $j < 16000; $j++) {
self::checkMpid($worker, $m_pid);
echo "msg: {$j}\n";
sleep(1);
}
}
private static function checkMpid(Process $worker, $m_pid) {
if (!Process::kill($m_pid, 0)) {
$worker->exit(); //主进程死了我也死
// 这句提示,实际是看不到的.需要写到日志中
echo "Master process exited, I [{$worker['pid']}] also quit\n";
}
}
}

View File

@ -4,7 +4,7 @@
namespace ZM\API;
use Framework\Console;
use ZM\Console\Console;
use ZM\Utils\ZMUtil;
class CQ

View File

@ -3,14 +3,11 @@
namespace ZM\API;
use Co;
use Framework\Console;
use Framework\ZMBuf;
use ZM\Connection\ConnectionManager;
use ZM\Connection\CQConnection;
use ZM\ConnectionManager\ConnectionObject;
use ZM\Console\Console;
use ZM\Event\EventHandler;
use ZM\Utils\ZMRobot;
use ZM\Store\ZMBuf;
/**
* @method static send_private_msg($self_id, $params, $function = null)
@ -67,143 +64,15 @@ use ZM\Utils\ZMRobot;
* @method static set_friend_add_request_async($self_id, $params, $function = null)
* @method static set_group_add_request_async($self_id, $params, $function = null)
*/
class CQAPI
trait CQAPI
{
public static function quick_reply(CQConnection $conn, $data, $msg, $yield = null) {
switch ($data["message_type"]) {
case "group":
return (new ZMRobot($conn))->setCallback($yield)->sendGroupMsg($data["group_id"], $msg);
case "private":
return (new ZMRobot($conn))->setCallback($yield)->sendPrivateMsg($data["user_id"], $msg);
case "discuss":
return (new ZMRobot($conn))->setCallback($yield)->sendDiscussMsg($data["discuss_id"], $msg);
}
return null;
}
/**
* @param $name
* @param $arg
* @return bool
* @deprecated
*/
public static function __callStatic($name, $arg) {
trigger_error("This dynamic CQAPI calling method will be removed after 2.0 version.", E_USER_DEPRECATED);
$all = self::getSupportedAPIs();
$find = null;
if (in_array($name, $all)) $find = $name;
else {
foreach ($all as $v) {
if (strtolower($name) == strtolower(str_replace("_", "", $v))) {
$find = $v;
break;
}
}
}
if ($find === null) {
Console::warning("Unknown API " . $name);
return false;
}
$reply = ["action" => $find];
if (!is_array($arg[1])) {
Console::warning("Error when parsing params. Please make sure your params is an array.");
return false;
}
if ($arg[1] != []) {
$reply["params"] = $arg[1];
}
if (!($arg[0] instanceof CQConnection)) {
$robot = ConnectionManager::getByType("qq", ["self_id" => $arg[0]]);
if ($robot == []) {
Console::warning("发送错误,机器人连接不存在!");
return false;
}
$arg[0] = $robot[0];
}
return self::processAPI($arg[0], $reply, $arg[2] ?? null);
}
/********************** non-API Part **********************/
private static function getSupportedAPIs() {
return [
"send_private_msg",
"send_group_msg",
"send_discuss_msg",
"send_msg",
"delete_msg",
"send_like",
"set_group_kick",
"set_group_ban",
"set_group_anonymous_ban",
"set_group_whole_ban",
"set_group_admin",
"set_group_anonymous",
"set_group_card",
"set_group_leave",
"set_group_special_title",
"set_discuss_leave",
"set_friend_add_request",
"set_group_add_request",
"get_login_info",
"get_stranger_info",
"get_group_list",
"get_group_member_info",
"get_group_member_list",
"get_cookies",
"get_csrf_token",
"get_credentials",
"get_record",
"get_status",
"get_version_info",
"set_restart",
"set_restart_plugin",
"clean_data_dir",
"clean_plugin_log",
"_get_friend_list",
"_get_group_info",
"_get_vip_info",
//异步API
"send_private_msg_async",
"send_group_msg_async",
"send_discuss_msg_async",
"send_msg_async",
"delete_msg_async",
"set_group_kick_async",
"set_group_ban_async",
"set_group_anonymous_ban_async",
"set_group_whole_ban_async",
"set_group_admin_async",
"set_group_anonymous_async",
"set_group_card_async",
"set_group_leave_async",
"set_group_special_title_async",
"set_discuss_leave_async",
"set_friend_add_request_async",
"set_group_add_request_async"
];
}
public static function getLoggedAPIs() {
return [
"send_private_msg",
"send_group_msg",
"send_discuss_msg",
"send_msg",
"send_private_msg_async",
"send_group_msg_async",
"send_discuss_msg_async",
"send_msg_async"
];
}
/**
* @param CQConnection $connection
* @param ConnectionObject $connection
* @param $reply
* @param |null $function
* @return bool|array
*/
public static function processAPI($connection, $reply, $function = null) {
private function processAPI($connection, $reply, $function = null) {
$api_id = ZMBuf::$atomics["wait_msg_id"]->get();
$reply["echo"] = $api_id;
ZMBuf::$atomics["wait_msg_id"]->add(1);

View File

@ -1,11 +1,10 @@
<?php
<?php /** @noinspection PhpUnused */
namespace ZM\Utils;
namespace ZM\API;
use ZM\API\CQAPI;
use ZM\Connection\ConnectionManager;
use ZM\Connection\CQConnection;
use ZM\ConnectionManager\ConnectionObject;
use ZM\ConnectionManager\ManagerGM;
use ZM\Exception\RobotNotFoundException;
/**
@ -15,10 +14,12 @@ use ZM\Exception\RobotNotFoundException;
*/
class ZMRobot
{
use CQAPI;
const API_ASYNC = 1;
const API_NORMAL = 0;
const API_RATE_LIMITED = 2;
/** @var ConnectionObject|null */
private $connection;
private $callback = null;
@ -30,9 +31,11 @@ class ZMRobot
* @throws RobotNotFoundException
*/
public static function get($robot_id) {
$r = ConnectionManager::getByType("qq", ["self_id" => $robot_id]);
if ($r == []) throw new RobotNotFoundException("机器人 " . $robot_id . " 未连接到框架!");
return new ZMRobot($r[0]);
$r = ManagerGM::getAllByName('qq');
foreach($r as $v) {
if($v->getOption('connect_id') == $robot_id) return new ZMRobot($v);
}
throw new RobotNotFoundException("机器人 " . $robot_id . " 未连接到框架!");
}
/**
@ -40,12 +43,12 @@ class ZMRobot
* @return ZMRobot
*/
public static function getRandom() {
$r = ConnectionManager::getByType("qq");
$r = ManagerGM::getAllByName('qq');
if($r == []) throw new RobotNotFoundException("没有任何机器人连接到框架!");
return new ZMRobot($r[array_rand($r)]);
}
public function __construct(CQConnection $connection) {
public function __construct(ConnectionObject $connection) {
$this->connection = $connection;
}
@ -60,7 +63,7 @@ class ZMRobot
}
public function sendPrivateMsg($user_id, $message, $auto_escape = false) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'user_id' => $user_id,
@ -71,7 +74,7 @@ class ZMRobot
}
public function sendGroupMsg($group_id, $message, $auto_escape = false) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
@ -82,7 +85,7 @@ class ZMRobot
}
public function sendDiscussMsg($discuss_id, $message, $auto_escape = false) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'discuss_id' => $discuss_id,
@ -93,7 +96,7 @@ class ZMRobot
}
public function sendMsg($message_type, $target_id, $message, $auto_escape = false) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'message_type' => $message_type,
@ -105,7 +108,7 @@ class ZMRobot
}
public function deleteMsg($message_id) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'message_id' => $message_id
@ -114,7 +117,7 @@ class ZMRobot
}
public function sendLike($user_id, $times = 1) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'user_id' => $user_id,
@ -124,7 +127,7 @@ class ZMRobot
}
public function setGroupKick($group_id, $user_id, $reject_add_request = false) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
@ -135,7 +138,7 @@ class ZMRobot
}
public function setGroupBan($group_id, $user_id, $duration = 1800) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
@ -146,7 +149,7 @@ class ZMRobot
}
public function setGroupAnonymousBan($group_id, $anonymous_or_flag, $duration = 1800) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
@ -157,7 +160,7 @@ class ZMRobot
}
public function setGroupWholeBan($group_id, $enable = true) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
@ -167,7 +170,7 @@ class ZMRobot
}
public function setGroupAdmin($group_id, $user_id, $enable = true) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
@ -178,7 +181,7 @@ class ZMRobot
}
public function setGroupAnonymous($group_id, $enable = true) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
@ -188,7 +191,7 @@ class ZMRobot
}
public function setGroupCard($group_id, $user_id, $card = "") {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
@ -199,7 +202,7 @@ class ZMRobot
}
public function setGroupLeave($group_id, $is_dismiss = false) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
@ -209,7 +212,7 @@ class ZMRobot
}
public function setGroupSpecialTitle($group_id, $user_id, $special_title = "", $duration = -1) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
@ -221,7 +224,7 @@ class ZMRobot
}
public function setDiscussLeave($discuss_id) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'discuss_id' => $discuss_id
@ -230,7 +233,7 @@ class ZMRobot
}
public function setFriendAddRequest($flag, $approve = true, $remark = "") {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'flag' => $flag,
@ -241,7 +244,7 @@ class ZMRobot
}
public function setGroupAddRequest($flag, $sub_type, $approve = true, $reason = "") {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'flag' => $flag,
@ -253,11 +256,11 @@ class ZMRobot
}
public function getLoginInfo() {
return CQAPI::processAPI($this->connection, ['action' => $this->getActionName(__FUNCTION__)], $this->callback);
return $this->processAPI($this->connection, ['action' => $this->getActionName(__FUNCTION__)], $this->callback);
}
public function getStrangerInfo($user_id, $no_cache = false) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'user_id' => $user_id,
@ -267,19 +270,19 @@ class ZMRobot
}
public function getFriendList() {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function getGroupList() {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function getGroupInfo($group_id, $no_cache = false) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
@ -289,7 +292,7 @@ class ZMRobot
}
public function getGroupMemberInfo($group_id, $user_id, $no_cache = false) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
@ -300,7 +303,7 @@ class ZMRobot
}
public function getGroupMemberList($group_id) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id
@ -309,7 +312,7 @@ class ZMRobot
}
public function getCookies($domain = "") {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'domain' => $domain
@ -318,13 +321,13 @@ class ZMRobot
}
public function getCsrfToken() {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function getCredentials($domain = "") {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'domain' => $domain
@ -333,7 +336,7 @@ class ZMRobot
}
public function getRecord($file, $out_format, $full_path = false) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'file' => $file,
@ -344,7 +347,7 @@ class ZMRobot
}
public function getImage($file) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'file' => $file
@ -353,31 +356,31 @@ class ZMRobot
}
public function canSendImage() {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function canSendRecord() {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function getStatus() {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function getVersionInfo() {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function setRestartPlugin($delay = 0) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'delay' => $delay
@ -386,7 +389,7 @@ class ZMRobot
}
public function cleanDataDir($data_dir) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'data_dir' => $data_dir
@ -395,7 +398,7 @@ class ZMRobot
}
public function cleanPluginLog() {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}

View File

@ -1,11 +1,10 @@
<?php
namespace ZM\Utils;
namespace ZM\API;
use ZM\API\CQAPI;
use ZM\Connection\CQConnection;
use ZM\ConnectionManager\ConnectionObject;
/**
* Class ZMRobotExperiment
@ -14,15 +13,14 @@ use ZM\Connection\CQConnection;
*/
class ZMRobotExperiment
{
/**
* @var CQConnection
*/
use CQAPI;
private $connection;
private $callback = null;
private $prefix = 0;
public function __construct(CQConnection $connection, $callback, $prefix) {
public function __construct(ConnectionObject $connection, $callback, $prefix) {
$this->connection = $connection;
$this->callback = $callback;
$this->prefix = $prefix;
@ -39,7 +37,7 @@ class ZMRobotExperiment
}
public function getFriendList($flat = false) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => '_' . $this->getActionName(__FUNCTION__),
'params' => [
'flat' => $flat
@ -48,7 +46,7 @@ class ZMRobotExperiment
}
public function getGroupInfo($group_id) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => '_' . $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id
@ -57,7 +55,7 @@ class ZMRobotExperiment
}
public function getVipInfo($user_id) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => '_' . $this->getActionName(__FUNCTION__),
'params' => [
'user_id' => $user_id
@ -66,7 +64,7 @@ class ZMRobotExperiment
}
public function getGroupNotice($group_id) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => '_' . $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id
@ -75,7 +73,7 @@ class ZMRobotExperiment
}
public function sendGroupNotice($group_id, $title, $content) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => '_' . $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
@ -86,7 +84,7 @@ class ZMRobotExperiment
}
public function setRestart($clean_log = false, $clean_cache = false, $clean_event = false) {
return CQAPI::processAPI($this->connection, [
return $this->processAPI($this->connection, [
'action' => '_' . $this->getActionName(__FUNCTION__),
'params' => [
'clean_log' => $clean_log,

View File

@ -3,144 +3,205 @@
namespace ZM\Annotation;
use Doctrine\Common\Annotations\{AnnotationException, AnnotationReader};
use Doctrine\Common\Annotations\AnnotationReader;
use Co;
use Framework\{Console, ZMBuf};
use ZM\ConnectionManager\ConnectionObject;
use ZM\Console\Console;
use ZM\Store\ZMBuf;
use Error;
use Exception;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
use ZM\Annotation\CQ\{CQAfter,
CQAPIResponse,
CQAPISend,
CQBefore,
CQCommand,
CQMessage,
CQMetaEvent,
CQNotice,
CQRequest
};
use ZM\Annotation\Http\{After, Before, Controller, HandleException, Middleware, MiddlewareClass, RequestMapping};
use ZM\Annotation\CQ\CQAfter;
use ZM\Annotation\CQ\CQAPIResponse;
use ZM\Annotation\CQ\CQAPISend;
use ZM\Annotation\CQ\CQBefore;
use ZM\Annotation\CQ\CQCommand;
use ZM\Annotation\CQ\CQMessage;
use ZM\Annotation\CQ\CQMetaEvent;
use ZM\Annotation\CQ\CQNotice;
use ZM\Annotation\CQ\CQRequest;
use ZM\Annotation\Http\After;
use ZM\Annotation\Http\Before;
use ZM\Annotation\Http\Controller;
use ZM\Annotation\Http\HandleException;
use ZM\Annotation\Http\Middleware;
use ZM\Annotation\Http\MiddlewareClass;
use ZM\Annotation\Http\RequestMapping;
use Swoole\Timer;
use ZM\Annotation\Interfaces\CustomAnnotation;
use ZM\Annotation\Interfaces\Level;
use ZM\Annotation\Module\{Closed, InitBuffer, LoadBuffer, SaveBuffer};
use ZM\Annotation\Swoole\{OnSave, OnStart, OnTick, SwooleEventAfter, SwooleEventAt};
use ZM\Annotation\Module\Closed;
use ZM\Annotation\Module\InitBuffer;
use ZM\Annotation\Module\LoadBuffer;
use ZM\Annotation\Module\SaveBuffer;
use ZM\Annotation\Swoole\OnSave;
use ZM\Annotation\Swoole\OnStart;
use ZM\Annotation\Swoole\OnTick;
use ZM\Annotation\Swoole\SwooleEventAfter;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Annotation\Interfaces\Rule;
use ZM\Connection\WSConnection;
use ZM\Event\EventHandler;
use ZM\Http\MiddlewareInterface;
use Framework\DataProvider;
use ZM\Utils\DataProvider;
use ZM\Utils\ZMUtil;
class AnnotationParser
{
/**
* 注册各个模块类的注解和模块level的排序
* @throws ReflectionException
* @throws AnnotationException
*/
public static function registerMods() {
self::loadAnnotationClasses();
$all_class = getAllClasses(DataProvider::getWorkingDir() . "/src/Module/", "Module");
private $path_list = [];
private $start_time;
private $annotation_map = [];
public function __construct() {
$this->start_time = microtime(true);
$this->loadAnnotationClasses();
ZMBuf::$req_mapping = [];
ZMBuf::$req_mapping[0] = [
'id' => 0,
'pid' => -1,
'name' => '/'
];
$reader = new AnnotationReader();
foreach ($all_class as $v) {
Console::debug("正在检索 " . $v);
$reflection_class = new ReflectionClass($v);
$class_prefix = '';
$methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC);
$class_annotations = $reader->getClassAnnotations($reflection_class);
$middleware_addon = [];
foreach ($class_annotations as $vs) {
if ($vs instanceof Closed) {
continue 2;
} elseif ($vs instanceof Controller) {
Console::debug("找到 Controller 中间件: " . $vs->class);
$class_prefix = $vs->prefix;
} elseif ($vs instanceof SaveBuffer) {
Console::debug("注册自动保存的缓存变量: " . $vs->buf_name . " (Dir:" . $vs->sub_folder . ")");
DataProvider::addSaveBuffer($vs->buf_name, $vs->sub_folder);
} elseif ($vs instanceof LoadBuffer) {
Console::debug("注册到内存的缓存变量: " . $vs->buf_name . " (Dir:" . $vs->sub_folder . ")");
ZMBuf::set($vs->buf_name, DataProvider::getJsonData(($vs->sub_folder ?? "") . "/" . $vs->buf_name . ".json"));
} elseif ($vs instanceof InitBuffer) {
ZMBuf::set($vs->buf_name, []);
} elseif ($vs instanceof MiddlewareClass) {
Console::verbose("正在注册中间件 " . $reflection_class->getName());
$result = [
"class" => "\\" . $reflection_class->getName()
];
foreach ($methods as $vss) {
if ($vss->getName() == "getName") {
/** @var MiddlewareInterface $tmp */
$tmp = new $v();
$result["name"] = $tmp->getName();
continue;
}
/**
* 注册各个模块类的注解和模块level的排序
* @throws ReflectionException
*/
public function registerMods() {
foreach($this->path_list as $path) {
$all_class = getAllClasses($path[0], $path[1]);
$reader = new AnnotationReader();
foreach ($all_class as $v) {
Console::debug("正在检索 " . $v);
$reflection_class = new ReflectionClass($v);
$methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC);
$class_annotations = $reader->getClassAnnotations($reflection_class);
//这里将每个类里面所有的类注解、方法注解通通加到一颗大树上,后期解析
/*
$annotation_map: {
Module\Example\Hello: {
class_annotations: [
注解对象1, 注解对象2, ...
],
methods: [
ReflectionMethod, ReflectionMethod, ...
],
methods_annotations: {
foo: [ 注解对象1, 注解对象2, ... ],
bar: [ 注解对象1, 注解对象2, ... ],
}
$method_annotations = $reader->getMethodAnnotations($vss);
foreach ($method_annotations as $vsss) {
if ($vss instanceof Rule) $vss = self::registerRuleEvent($vsss, $vss, $reflection_class);
else $vss = self::registerMethod($vsss, $vss, $reflection_class);
//echo get_class($vsss) . PHP_EOL;
if ($vsss instanceof Before) $result["before"] = $vsss->method;
if ($vsss instanceof After) $result["after"] = $vsss->method;
if ($vsss instanceof HandleException) {
$result["exceptions"][$vsss->class_name] = $vsss->method;
}
}
*/
$this->annotation_map[$v]["class_annotations"] = $class_annotations;
$this->annotation_map[$v]["methods"] = $methods;
foreach ($methods as $method) {
$this->annotation_map[$v]["methods_annotations"][$method->getName()] = $reader->getMethodAnnotations($method);
}
/*
$middleware_addon = [];
foreach ($class_annotations as $vs) {
if ($vs instanceof Closed) {
continue 2;
} elseif ($vs instanceof Controller) {
Console::debug("找到 Controller 中间件: " . $vs->class);
$class_prefix = $vs->prefix;
} elseif ($vs instanceof SaveBuffer) {
Console::debug("注册自动保存的缓存变量: " . $vs->buf_name . " (Dir:" . $vs->sub_folder . ")");
DataProvider::addSaveBuffer($vs->buf_name, $vs->sub_folder);
} elseif ($vs instanceof LoadBuffer) {
Console::debug("注册到内存的缓存变量: " . $vs->buf_name . " (Dir:" . $vs->sub_folder . ")");
ZMBuf::set($vs->buf_name, DataProvider::getJsonData(($vs->sub_folder ?? "") . "/" . $vs->buf_name . ".json"));
} elseif ($vs instanceof InitBuffer) {
ZMBuf::set($vs->buf_name, []);
} elseif ($vs instanceof MiddlewareClass) {
Console::verbose("正在注册中间件 " . $reflection_class->getName());
$result = [
"class" => "\\" . $reflection_class->getName()
];
foreach ($methods as $vss) {
if ($vss->getName() == "getName") {
/** @var MiddlewareInterface $tmp *
$tmp = new $v();
$result["name"] = $tmp->getName();
continue;
}
$method_annotations = $reader->getMethodAnnotations($vss);
foreach ($method_annotations as $vsss) {
if ($vss instanceof Rule) $vss = self::registerRuleEvent($vsss, $vss, $reflection_class);
else $vss = self::registerMethod($vsss, $vss, $reflection_class);
//echo get_class($vsss) . PHP_EOL;
if ($vsss instanceof Before) $result["before"] = $vsss->method;
if ($vsss instanceof After) $result["after"] = $vsss->method;
if ($vsss instanceof HandleException) {
$result["exceptions"][$vsss->class_name] = $vsss->method;
}
}
}
}
ZMBuf::$events[MiddlewareClass::class][$result["name"]] = $result;
continue 2;
} elseif ($vs instanceof Middleware) {
$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 !== []) {
foreach($middleware_addon as $value){
Console::debug("Added middleware " . $value->middleware . " to $v -> " . $vs->getName());
ZMBuf::$events[MiddlewareInterface::class][$v][$vs->getName()][] = $value->middleware;
ZMBuf::$events[MiddlewareClass::class][$result["name"]] = $result;
continue 2;
} elseif ($vs instanceof Middleware) {
$middleware_addon[] = $vs;
} elseif ($vs instanceof CustomAnnotation) {
$vs->class = $reflection_class->getName();
ZMBuf::$events[get_class($vs)][] = $vs;
}
}
$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));
foreach ($methods as $vs) {
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;
elseif ($vss instanceof CQMessage) ZMBuf::$events[CQMessage::class][] = $vss;
elseif ($vss instanceof CQNotice) ZMBuf::$events[CQNotice::class][] = $vss;
elseif ($vss instanceof CQRequest) ZMBuf::$events[CQRequest::class][] = $vss;
elseif ($vss instanceof CQMetaEvent) ZMBuf::$events[CQMetaEvent::class][] = $vss;
elseif ($vss instanceof CQCommand) ZMBuf::$events[CQCommand::class][] = $vss;
elseif ($vss instanceof RequestMapping) {
self::registerRequestMapping($vss, $vs, $reflection_class, $class_prefix);
} elseif ($vss instanceof CustomAnnotation) ZMBuf::$events[get_class($vss)][] = $vss;
elseif ($vss instanceof CQBefore) ZMBuf::$events[CQBefore::class][$vss->cq_event][] = $vss;
elseif ($vss instanceof CQAfter) ZMBuf::$events[CQAfter::class][$vss->cq_event][] = $vss;
elseif ($vss instanceof OnStart) ZMBuf::$events[OnStart::class][] = $vss;
elseif ($vss instanceof OnSave) ZMBuf::$events[OnSave::class][] = $vss;
elseif ($vss instanceof Middleware) ZMBuf::$events[MiddlewareInterface::class][$vss->class][$vss->method][] = $vss->middleware;
elseif ($vss instanceof OnTick) self::addTimerTick($vss);
elseif ($vss instanceof CQAPISend) ZMBuf::$events[CQAPISend::class][] = $vss;
elseif ($vss instanceof CQAPIResponse) ZMBuf::$events[CQAPIResponse::class][$vss->retcode] = [$vss->class, $vss->method];
}
if ($vss instanceof SwooleEventAt) ZMBuf::$events[SwooleEventAt::class][] = $vss;
elseif ($vss instanceof SwooleEventAfter) ZMBuf::$events[SwooleEventAfter::class][] = $vss;
elseif ($vss instanceof CQMessage) ZMBuf::$events[CQMessage::class][] = $vss;
elseif ($vss instanceof CQNotice) ZMBuf::$events[CQNotice::class][] = $vss;
elseif ($vss instanceof CQRequest) ZMBuf::$events[CQRequest::class][] = $vss;
elseif ($vss instanceof CQMetaEvent) ZMBuf::$events[CQMetaEvent::class][] = $vss;
elseif ($vss instanceof CQCommand) ZMBuf::$events[CQCommand::class][] = $vss;
elseif ($vss instanceof RequestMapping) {
self::registerRequestMapping($vss, $vs, $reflection_class, $class_prefix);
} elseif ($vss instanceof CustomAnnotation) ZMBuf::$events[get_class($vss)][] = $vss;
elseif ($vss instanceof CQBefore) ZMBuf::$events[CQBefore::class][$vss->cq_event][] = $vss;
elseif ($vss instanceof CQAfter) ZMBuf::$events[CQAfter::class][$vss->cq_event][] = $vss;
elseif ($vss instanceof OnStart) ZMBuf::$events[OnStart::class][] = $vss;
elseif ($vss instanceof OnSave) ZMBuf::$events[OnSave::class][] = $vss;
elseif ($vss instanceof Middleware) ZMBuf::$events[MiddlewareInterface::class][$vss->class][$vss->method][] = $vss->middleware;
elseif ($vss instanceof OnTick) self::addTimerTick($vss);
elseif ($vss instanceof CQAPISend) ZMBuf::$events[CQAPISend::class][] = $vss;
elseif ($vss instanceof CQAPIResponse) ZMBuf::$events[CQAPIResponse::class][$vss->retcode] = [$vss->class, $vss->method];
}
}*/
}
}
$tree = self::genTree(ZMBuf::$req_mapping);
ZMBuf::$req_mapping = $tree[0];
//给支持level的排个序
Console::debug("解析注解完毕!");
if (ZMBuf::isset("timer_count")) {
Console::info("Added " . ZMBuf::get("timer_count") . " timer(s)!");
ZMBuf::unsetCache("timer_count");
}
}
public function sortLevels() {
foreach (ZMBuf::$events as $class_name => $v) {
if (is_a($class_name, Level::class, true)) {
for ($i = 0; $i < count(ZMBuf::$events[$class_name]) - 1; ++$i) {
@ -156,11 +217,6 @@ class AnnotationParser
}
}
}
Console::debug("解析注解完毕!");
if (ZMBuf::isset("timer_count")) {
Console::info("Added " . ZMBuf::get("timer_count") . " timer(s)!");
ZMBuf::unsetCache("timer_count");
}
}
public static function getRuleCallback($rule_str) {
@ -173,9 +229,9 @@ class AnnotationParser
//Swoole 事件时走此switch
switch ($asp_name) {
case "connectType": //websocket连接类型
$func = function (?WSConnection $connection) use ($rest) {
$func = function (?ConnectionObject $connection) use ($rest) {
if ($connection === null) return false;
return $connection->getType() == $rest ? true : false;
return $connection->getName() == $rest ? true : false;
};
break;
case "containsGet": //handle http request事件时才能用
@ -323,7 +379,7 @@ class AnnotationParser
ZMBuf::$req_mapping = $array;
}
private static function loadAnnotationClasses() {
private function loadAnnotationClasses() {
$class = getAllClasses(WORKING_DIR . "/src/ZM/Annotation/", "ZM\\Annotation");
foreach ($class as $v) {
$s = WORKING_DIR . '/src/' . str_replace("\\", "/", $v) . ".php";
@ -387,4 +443,8 @@ class AnnotationParser
});
ZMBuf::append("paused_tick", $cid);
}
public function addRegisterPath($path, $indoor_name) {
$this->path_list[] = [$path, $indoor_name];
}
}

View File

@ -6,6 +6,7 @@ namespace ZM\Annotation\Http;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\Interfaces\ErgodicAnnotation;
/**
* Class Controller
@ -13,11 +14,11 @@ use ZM\Annotation\AnnotationBase;
* @Target("CLASS")
* @package ZM\Annotation\Http
*/
class Controller extends AnnotationBase
class Controller extends AnnotationBase implements ErgodicAnnotation
{
/**
* @var string
* @Required()
*/
public $prefix = '';
}
}

View File

@ -7,6 +7,7 @@ namespace ZM\Annotation\Http;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\Interfaces\ErgodicAnnotation;
/**
* Class Middleware
@ -14,7 +15,7 @@ use ZM\Annotation\AnnotationBase;
* @Annotation
* @Target("ALL")
*/
class Middleware extends AnnotationBase
class Middleware extends AnnotationBase implements ErgodicAnnotation
{
/**
* @var string

View File

@ -10,7 +10,7 @@ use ZM\Annotation\AnnotationBase;
/**
* Class RequestMapping
* @Annotation
* @Target("ALL")
* @Target("METHOD")
* @package ZM\Annotation\Http
*/
class RequestMapping extends AnnotationBase
@ -36,4 +36,4 @@ class RequestMapping extends AnnotationBase
* @var array
*/
public $params = [];
}
}

View File

@ -9,7 +9,6 @@ use ZM\Annotation\AnnotationBase;
/**
* Class RequestMethod
* @Annotation
*
* @package ZM\Annotation\Http
*/
class RequestMethod extends AnnotationBase
@ -27,4 +26,4 @@ class RequestMethod extends AnnotationBase
public const DELETE = 'DELETE';
public const OPTIONS = 'OPTIONS';
public const HEAD = 'HEAD';
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace ZM\Annotation\Interfaces;
interface ErgodicAnnotation
{
}

View File

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

View File

@ -0,0 +1,73 @@
<?php
namespace ZM\Command;
use Phar;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use ZM\Console\TermColor;
class BuildCommand extends Command
{
// the name of the command (the part after "bin/console")
protected static $defaultName = 'build';
/**
* @var OutputInterface
*/
private $output = null;
protected function configure() {
$this->setDescription("Build an \".phar\" file | 将项目构建一个phar包");
$this->setHelp("此功能将会把炸毛框架的模块打包为\".phar\",供发布和执行。");
$this->addOption("target", "D", InputOption::VALUE_REQUIRED, "Output Directory | 指定输出目录");
// ...
}
protected function execute(InputInterface $input, OutputInterface $output) {
$this->output = $output;
$target_dir = $input->getOption("target") ?? (__DIR__ . '/../../../resources/');
if (mb_strpos($target_dir, "../")) $target_dir = realpath($target_dir);
if ($target_dir === false) {
$output->writeln(TermColor::color8(31) . "Error: No such file or directory (".__DIR__ . '/../../../resources/'.")" . TermColor::RESET);
return Command::FAILURE;
}
$output->writeln("Target: " . $target_dir . " , Version: " . ($version = json_decode(file_get_contents(__DIR__ . "/../../../composer.json"), true)["version"]));
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) . "See: https://stackoverflow.com/questions/34667606/cant-enable-phar-writing");
return Command::FAILURE;
}
if (!is_dir($target_dir)) {
$output->writeln(TermColor::color8(31) . "Error: No such file or directory ($target_dir)" . TermColor::RESET);
return Command::FAILURE;
}
$filename = "server.phar";
$this->build($target_dir, $filename);
return Command::SUCCESS;
}
private function build ($target_dir, $filename) {
@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'));
$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");
}
}

View File

@ -0,0 +1,101 @@
<?php
namespace ZM\Command;
use Phar;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class InitCommand extends Command
{
private $extract_files = [
"/config/global.php",
"/.gitignore",
"/config/file_header.json",
"/config/console_color.json",
"/config/motd.txt",
"/src/Module/Example/Hello.php",
"/src/Module/Middleware/TimerMiddleware.php",
"/src/Custom/global_function.php"
];
// the name of the command (the part after "bin/console")
protected static $defaultName = 'init';
protected function configure() {
$this->setDescription("Initialize framework starter | 初始化框架运行的基础文件");
$this->setHelp("此命令将会解压以下文件到项目的根目录:\n" . implode("\n", $this->getExtractFiles()));
// ...
}
protected function execute(InputInterface $input, OutputInterface $output) {
if (LOAD_MODE === 1) { // 从composer依赖而来的项目模式最基本的需要初始化的模式
$output->writeln("<comment>Initializing files</comment>");
$base_path = LOAD_MODE_COMPOSER_PATH;
foreach ($this->extract_files as $file) {
if (!file_exists($base_path . $file)) {
$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"]);
copy($base_path . "/vendor/" . $package_name . $file, $base_path . $file);
} else {
echo "Skipping " . $file . " , file exists." . PHP_EOL;
}
}
$autoload = [
"psr-4" => [
"Module\\" => "src/Module",
"Custom\\" => "src/Custom"
],
"files" => [
"src/Custom/global_function.php"
]
];
if (file_exists($base_path . "/composer.json")) {
$composer = json_decode(file_get_contents($base_path . "/composer.json"), true);
if (!isset($composer["autoload"])) {
$composer["autoload"] = $autoload;
} else {
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) {
if (!in_array($v, $composer["autoload"]["files"])) $composer["autoload"]["files"][] = $v;
}
}
file_put_contents($base_path . "/composer.json", json_encode($composer, 64 | 128 | 256));
$output->writeln("<info>Executing composer update command</info>");
exec("composer update");
echo PHP_EOL;
} else {
echo("Error occurred. Please check your updates.\n");
return Command::FAILURE;
}
return Command::SUCCESS;
} elseif (LOAD_MODE === 2) { //从phar启动的框架包初始化的模式
$phar_link = new Phar(__DIR__);
$current_dir = pathinfo($phar_link->getPath())["dirname"];
chdir($current_dir);
$phar_link = "phar://" . $phar_link->getPath();
foreach ($this->extract_files as $file) {
if (!file_exists($current_dir . $file)) {
$info = pathinfo($file);
@mkdir($current_dir . $info["dirname"], 0777, true);
echo "Copying " . $file . PHP_EOL;
file_put_contents($current_dir . $file, file_get_contents($phar_link . $file));
} else {
echo "Skipping " . $file . " , file exists." . PHP_EOL;
}
}
}
$output->writeln("initialization must be started with composer-project mode!");
return Command::FAILURE;
}
private function getExtractFiles() {
return $this->extract_files;
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace ZM\Command;
use Framework\FrameworkLoader;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use ZM\Framework;
class RunServerCommand extends Command
{
// the name of the command (the part after "bin/console")
protected static $defaultName = 'server';
protected function configure() {
$this->setDescription("Run zhamao-framework | 启动框架");
$this->setHelp("直接运行可以启动");
$this->addOption("debug-mode", "D", null, "开启调试模式 (这将关闭协程化)");
$this->addOption("log-debug", null, null, "调整消息等级到debug (log-level=4)");
$this->addOption("log-verbose", null, null, "调整消息等级到verbose (log-level=3)");
$this->addOption("log-info", null, null, "调整消息等级到info (log-level=2)");
$this->addOption("log-warning", null, null, "调整消息等级到warning (log-level=1)");
$this->addOption("log-error", null, null, "调整消息等级到error (log-level=0)");
$this->addOption("log-theme", null, InputOption::VALUE_REQUIRED, "改变终端的主题配色");
$this->addOption("disable-console-input", null, null, "禁止终端输入内容 (后台服务时需要)");
$this->addOption("env", null, InputOption::VALUE_REQUIRED, "设置环境类型 (production, development, staging)");
// ...
}
protected function execute(InputInterface $input, OutputInterface $output) {
if(($opt = $input->getOption("env")) !== null) {
if(!in_array($opt, ["production", "staging", "development"])) {
$output->writeln("<error> \"--env\" option only accept production, development and staging ! </error>");
}
}
// ... put here the code to run in your command
// this method must return an integer number with the "exit status code"
// of the command. You can also use these constants to make code more readable
new Framework($input->getOptions());
// return this if there was no problem running the command
// (it's equivalent to returning int(0))
return Command::SUCCESS;
// or return this if some error happened during the execution
// (it's equivalent to returning int(1))
// return Command::FAILURE;
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace ZM\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class SystemdCommand extends Command
{
// the name of the command (the part after "bin/console")
protected static $defaultName = 'systemd:generate';
protected function execute(InputInterface $input, OutputInterface $output) {
//TODO: 写一个生成systemd配置的功能给2.0
return Command::SUCCESS;
}
}

View File

@ -1,23 +0,0 @@
<?php
namespace ZM\Connection;
class CQConnection extends WSConnection
{
public $self_id = null;
public function __construct($server, $fd, $self_id) {
parent::__construct($server, $fd);
$this->self_id = $self_id;
}
public function getQQ(){
return $this->self_id;
}
public function getType() {
return "qq";
}
}

View File

@ -1,76 +0,0 @@
<?php
namespace ZM\Connection;
use Framework\ZMBuf;
use Framework\DataProvider;
class ConnectionManager
{
/**
* 通过server的fd获取WSConnection实例化对象
* @param int $fd
* @return WSConnection|CQConnection|ProxyConnection
*/
public static function get(int $fd) {
foreach (ZMBuf::$connect as $v) {
if ($v->fd == $fd) return $v;
}
return null;
}
/**
* @param string $type
* @param array $option
* @return WSConnection[]|CQConnection[]
*/
public static function getByType(string $type, $option = []) {
$conn = [];
foreach (ZMBuf::$connect as $v) {
foreach ($option as $ks => $vs) {
if (($v->$ks ?? "") == $vs) continue;
else continue 2;
}
if ($v->getType() == $type) $conn[] = $v;
}
return $conn;
}
public static function getTypeClassName(string $type) {
switch (strtolower($type)) {
case "qq":
case "universal":
return CQConnection::class;
case "webconsole":
return WCConnection::class;
case "proxy":
return ProxyConnection::class;
case "terminal":
return TerminalConnection::class;
default:
foreach (ZMBuf::$custom_connection_class as $v) {
/** @var WSConnection $r */
$r = new $v(ZMBuf::$server, -1);
if ($r->getType() == strtolower($type)) return $v;
}
return UnknownConnection::class;
}
}
public static function close($fd) {
foreach (ZMBuf::$connect as $k => $v) {
if ($v->fd == $fd) {
ZMBuf::$server->close($fd);
unset(ZMBuf::$connect[$k]);
break;
}
}
}
public static function registerCustomClass() {
$classes = getAllClasses(DataProvider::getWorkingDir(). "/src/Custom/Connection/", "Custom\\Connection");
ZMBuf::$custom_connection_class = $classes;
}
}

View File

@ -1,13 +0,0 @@
<?php
namespace ZM\Connection;
class ProxyConnection extends WSConnection
{
public function getType() {
return "proxy";
}
}

View File

@ -1,13 +0,0 @@
<?php
namespace ZM\Connection;
class TerminalConnection extends WSConnection
{
public function getType() {
return "terminal";
}
}

View File

@ -1,13 +0,0 @@
<?php
namespace ZM\Connection;
class UnknownConnection extends WSConnection
{
public function getType() {
return "unknown";
}
}

View File

@ -1,10 +0,0 @@
<?php
namespace ZM\Connection;
class WCConnection extends WSConnection
{
public function getType() { return "wc"; }
}

View File

@ -1,51 +0,0 @@
<?php
namespace ZM\Connection;
use Framework\Console;
use swoole_websocket_server;
abstract class WSConnection
{
public $fd;
/** @var swoole_websocket_server */
protected $server;
public $available = false;
public function __construct($server, $fd) {
$this->server = $server;
$this->fd = $fd;
}
public abstract function getType();
public function exists() {
return $this->available = $this->server->exist($this->fd);
}
public function close() {
ConnectionManager::close($this->fd);
}
public function push($data, $push_error_record = true) {
if ($data === null || $data == "") {
Console::warning("推送了空消息");
return false;
}
if (!$this->server->exist($this->fd)) {
Console::warning("Swoole 原生 websocket连接池中无此连接");
return false;
}
if ($this->server->push($this->fd, $data) === false) {
$data = unicode_decode($data);
if ($push_error_record) Console::warning("API push failed. Data: " . $data);
Console::warning("websocket数据未成功推送长度" . strlen($data));
return false;
}
return true;
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace ZM;
use Exception;
use ZM\Command\BuildCommand;
use ZM\Command\InitCommand;
use ZM\Command\RunServerCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ConsoleApplication extends Application
{
public function __construct(string $name = 'UNKNOWN') {
$version = json_decode(file_get_contents(__DIR__ . "/../../composer.json"), true)["version"] ?? "UNKNOWN";
parent::__construct($name, $version);
}
public function initEnv() {
$this->addCommands([
new RunServerCommand(), //运行主服务的指令控制器
new InitCommand() //初始化用的用于项目初始化和phar初始化
]);
if (LOAD_MODE === 0) $this->add(new BuildCommand()); //只有在git源码模式才能使用打包指令
}
/**
* @param InputInterface|null $input
* @param OutputInterface|null $output
* @return int
*/
public function run(InputInterface $input = null, OutputInterface $output = null) {
try {
return parent::run($input, $output);
} catch (Exception $e) {
die("{$e->getMessage()} at {$e->getFile()}({$e->getLine()})");
}
}
}

View File

@ -5,18 +5,16 @@ namespace ZM\Context;
use Co;
use Framework\ZMBuf;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
use swoole_server;
use ZM\API\CQAPI;
use ZM\Connection\ConnectionManager;
use ZM\Connection\CQConnection;
use ZM\Connection\WSConnection;
use ZM\ConnectionManager\ConnectionObject;
use ZM\ConnectionManager\ManagerGM;
use ZM\Exception\InvalidArgumentException;
use ZM\Exception\WaitTimeoutException;
use ZM\Http\Response;
use ZM\Utils\ZMRobot;
use ZM\API\ZMRobot;
use ZM\Store\ZMBuf;
class Context implements ContextInterface
{
@ -53,8 +51,8 @@ class Context implements ContextInterface
*/
public function getResponse() { return ZMBuf::$context[$this->cid]["response"] ?? null; }
/** @return WSConnection */
public function getConnection() { return ConnectionManager::get($this->getFd()); }
/** @return ConnectionObject|null */
public function getConnection() { return ManagerGM::get($this->getFd()); }
/**
* @return int|null
@ -65,8 +63,8 @@ class Context implements ContextInterface
* @return ZMRobot|null
*/
public function getRobot() {
$conn = ConnectionManager::get($this->getFrame()->fd);
return $conn instanceof CQConnection ? new ZMRobot($conn) : null;
$conn = ManagerGM::get($this->getFrame()->fd);
return $conn instanceof ConnectionObject ? new ZMRobot($conn) : null;
}
public function getMessage() { return ZMBuf::$context[$this->cid]["data"]["message"] ?? null; }
@ -109,7 +107,17 @@ class Context implements ContextInterface
case "private":
case "discuss":
$this->setCache("has_reply", true);
return CQAPI::quick_reply(ConnectionManager::get($this->getFrame()->fd), $this->getData(), $msg, $yield);
$data = $this->getData();
$conn = $this->getConnection();
switch ($data["message_type"]) {
case "group":
return (new ZMRobot($conn))->setCallback($yield)->sendGroupMsg($data["group_id"], $msg);
case "private":
return (new ZMRobot($conn))->setCallback($yield)->sendPrivateMsg($data["user_id"], $msg);
case "discuss":
return (new ZMRobot($conn))->setCallback($yield)->sendDiscussMsg($data["discuss_id"], $msg);
}
return null;
}
return false;
}

View File

@ -7,9 +7,9 @@ namespace ZM\Context;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;
use ZM\Connection\WSConnection;
use ZM\ConnectionManager\ConnectionObject;
use ZM\Http\Response;
use ZM\Utils\ZMRobot;
use ZM\API\ZMRobot;
interface ContextInterface
{
@ -26,7 +26,7 @@ interface ContextInterface
public function setData($data);
/** @return WSConnection */
/** @return ConnectionObject */
public function getConnection();
/** @return int|null */

View File

@ -5,8 +5,9 @@ namespace ZM\DB;
use Exception;
use framework\Console;
use framework\ZMBuf;
use ZM\Config\ZMConfig;
use ZM\Console\Console;
use ZM\Store\ZMBuf;
use PDOException;
use PDOStatement;
use Swoole\Coroutine;
@ -23,7 +24,7 @@ class DB
*/
public static function initTableList() {
if (!extension_loaded("mysqlnd")) throw new Exception("Can not find mysqlnd PHP extension.");
$result = self::rawQuery("select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='" . ZMBuf::globals("sql_config")["sql_database"] . "';", []);
$result = self::rawQuery("select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='" . ZMConfig::get("global", "sql_config")["sql_database"] . "';", []);
foreach ($result as $v) {
self::$table_list[] = $v['TABLE_NAME'];
}
@ -38,7 +39,7 @@ class DB
public static function table($table_name, $enable_cache = null) {
if (Table::getTableInstance($table_name) === null) {
if (in_array($table_name, self::$table_list))
return new Table($table_name, $enable_cache ?? ZMBuf::globals("sql_config")["sql_enable_cache"]);
return new Table($table_name, $enable_cache ?? ZMConfig::get("global", "sql_config")["sql_enable_cache"]);
elseif(ZMBuf::$sql_pool !== null){
throw new DbException("Table " . $table_name . " not exist in database.");
} else {
@ -98,7 +99,7 @@ class DB
if (ZMBuf::get("sql_log") === true) {
$starttime = microtime(true);
}
Console::debug("MySQL: ".$line);
Console::debug("MySQL: ".$line." | ". implode(", ", $params));
try {
$conn = ZMBuf::$sql_pool->get();
if ($conn === false) {

View File

@ -4,7 +4,7 @@
namespace ZM\DB;
use Framework\Console;
use ZM\Console\Console;
use ZM\Exception\DbException;
class SelectBody

View File

@ -6,25 +6,23 @@ namespace ZM\Event\CQ;
use Co;
use Doctrine\Common\Annotations\AnnotationException;
use Framework\Console;
use Framework\ZMBuf;
use ZM\ConnectionManager\ConnectionObject;
use ZM\Console\Console;
use ZM\Store\ZMBuf;
use ZM\Annotation\CQ\CQAfter;
use ZM\Annotation\CQ\CQBefore;
use ZM\Annotation\CQ\CQCommand;
use ZM\Annotation\CQ\CQMessage;
use ZM\Connection\WSConnection;
use ZM\Event\EventHandler;
use ZM\Exception\WaitTimeoutException;
use ZM\Http\Response;
use ZM\ModBase;
use ZM\ModHandleType;
class MessageEvent
{
private $function_call = false;
private $data;
private $circle;
/** @var WSConnection|Response */
/** @var ConnectionObject|Response */
private $connection;
public function __construct($data, $conn_or_response, $circle = 0) {
@ -84,6 +82,7 @@ class MessageEvent
/**
* @throws AnnotationException
* @noinspection PhpRedundantCatchClauseInspection
*/
public function onActivate() {
try {
@ -96,7 +95,6 @@ class MessageEvent
$word[$k] = trim($word[$k]);
}
}
/** @var ModBase[] $obj */
$obj = [];
foreach (ZMBuf::$events[CQCommand::class] ?? [] as $v) {
/** @var CQCommand $v */
@ -107,30 +105,26 @@ class MessageEvent
($v->message_type == '' || ($v->message_type != '' && $v->message_type == context()->getData()["message_type"]))
) {
$c = $v->class;
$class_construct = [
"data" => context()->getData(),
"connection" => context()->getConnection()
];
if (!isset($obj[$c])) {
$obj[$c] = new $c($class_construct);
$obj[$c] = new $c();
}
if ($word[0] != "" && $v->match == $word[0]) {
Console::debug("Calling $c -> {$v->method}");
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, $class_construct, [$word], function ($r) {
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, [], [$word], function ($r) {
if (is_string($r)) context()->reply($r);
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) {
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, [], [$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) {
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, [], [$args], function ($r) {
if (is_string($r)) context()->reply($r);
return true;
});
@ -138,7 +132,7 @@ class MessageEvent
} 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) {
$this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, [], [$args], function ($r) {
if (is_string($r)) context()->reply($r);
return true;
});
@ -158,10 +152,7 @@ class MessageEvent
$c = $v->class;
Console::debug("Calling CQMessage: $c -> {$v->method}");
if (!isset($obj[$c]))
$obj[$c] = new $c([
"data" => context()->getData(),
"connection" => $this->connection
], ModHandleType::CQ_MESSAGE);
$obj[$c] = new $c();
EventHandler::callWithMiddleware($obj[$c], $v->method, [], [context()->getData()["message"]], function ($r) {
if (is_string($r)) context()->reply($r);
});

View File

@ -5,19 +5,15 @@ namespace ZM\Event\CQ;
use Doctrine\Common\Annotations\AnnotationException;
use Framework\ZMBuf;
use ZM\Annotation\CQ\CQBefore;
use ZM\Annotation\CQ\CQMetaEvent;
use ZM\Connection\CQConnection;
use ZM\Event\EventHandler;
use ZM\Exception\WaitTimeoutException;
use ZM\ModBase;
use ZM\ModHandleType;
use ZM\Store\ZMBuf;
class MetaEvent
{
private $data;
/** @var CQConnection */
private $connection;
private $circle;
@ -53,7 +49,6 @@ class MetaEvent
*/
public function onActivate() {
try {
/** @var ModBase[] $obj */
$obj = [];
foreach (ZMBuf::$events[CQMetaEvent::class] ?? [] as $v) {
/** @var CQMetaEvent $v */
@ -62,10 +57,7 @@ class MetaEvent
($v->sub_type == 0 || ($v->sub_type != 0 && $v->sub_type == $this->data["sub_type"]))) {
$c = $v->class;
if (!isset($obj[$c]))
$obj[$c] = new $c([
"data" => $this->data,
"connection" => $this->connection
], ModHandleType::CQ_META_EVENT);
$obj[$c] = new $c();
EventHandler::callWithMiddleware($obj[$c],$v->method, [], [], function($r) {
if (is_string($r)) context()->reply($r);
});

View File

@ -5,20 +5,16 @@ namespace ZM\Event\CQ;
use Doctrine\Common\Annotations\AnnotationException;
use Framework\ZMBuf;
use ZM\Annotation\CQ\CQAfter;
use ZM\Annotation\CQ\CQBefore;
use ZM\Annotation\CQ\CQNotice;
use ZM\Connection\CQConnection;
use ZM\Event\EventHandler;
use ZM\Exception\WaitTimeoutException;
use ZM\ModBase;
use ZM\ModHandleType;
use ZM\Store\ZMBuf;
class NoticeEvent
{
private $data;
/** @var CQConnection */
private $connection;
private $circle;
@ -54,7 +50,6 @@ class NoticeEvent
*/
public function onActivate() {
try {
/** @var ModBase[] $obj */
$obj = [];
foreach (ZMBuf::$events[CQNotice::class] ?? [] as $v) {
/** @var CQNotice $v */
@ -65,10 +60,7 @@ class NoticeEvent
($v->operator_id == 0 || ($v->operator_id != 0 && $v->operator_id == ($this->data["operator_id"] ?? 0)))) {
$c = $v->class;
if (!isset($obj[$c]))
$obj[$c] = new $c([
"data" => $this->data,
"connection" => $this->connection
], ModHandleType::CQ_NOTICE);
$obj[$c] = new $c();
EventHandler::callWithMiddleware($obj[$c],$v->method, [], [], function($r) {
if (is_string($r)) context()->reply($r);
});

View File

@ -5,20 +5,16 @@ namespace ZM\Event\CQ;
use Doctrine\Common\Annotations\AnnotationException;
use Framework\ZMBuf;
use ZM\Annotation\CQ\CQAfter;
use ZM\Annotation\CQ\CQBefore;
use ZM\Annotation\CQ\CQRequest;
use ZM\Connection\CQConnection;
use ZM\Event\EventHandler;
use ZM\Exception\WaitTimeoutException;
use ZM\ModBase;
use ZM\ModHandleType;
use ZM\Store\ZMBuf;
class RequestEvent
{
private $data;
/** @var CQConnection */
private $connection;
private $circle;
@ -51,10 +47,10 @@ class RequestEvent
/**
* @throws AnnotationException
* @noinspection PhpRedundantCatchClauseInspection
*/
public function onActivate() {
try {
/** @var ModBase[] $obj */
$obj = [];
foreach (ZMBuf::$events[CQRequest::class] ?? [] as $v) {
/** @var CQRequest $v */
@ -65,10 +61,7 @@ class RequestEvent
($v->comment == 0 || ($v->comment != 0 && $v->comment == ($this->data["comment"] ?? 0)))) {
$c = $v->class;
if (!isset($obj[$c]))
$obj[$c] = new $c([
"data" => $this->data,
"connection" => $this->connection
], ModHandleType::CQ_REQUEST);
$obj[$c] = new $c();
EventHandler::callWithMiddleware($obj[$c],$v->method, [], [], function($r) {
if (is_string($r)) context()->reply($r);
});

View File

@ -8,8 +8,9 @@ use Co;
use Doctrine\Common\Annotations\AnnotationException;
use Error;
use Exception;
use Framework\Console;
use Framework\ZMBuf;
use ZM\ConnectionManager\ConnectionObject;
use ZM\ConnectionManager\ManagerGM;
use ZM\Console\Console;
use ZM\Event\Swoole\{MessageEvent, RequestEvent, WorkerStartEvent, WSCloseEvent, WSOpenEvent};
use Swoole\Http\Request;
use Swoole\Server;
@ -17,11 +18,10 @@ use Swoole\WebSocket\Frame;
use ZM\Annotation\CQ\CQAPIResponse;
use ZM\Annotation\CQ\CQAPISend;
use ZM\Annotation\Http\MiddlewareClass;
use ZM\Connection\ConnectionManager;
use ZM\Connection\CQConnection;
use ZM\Http\MiddlewareInterface;
use ZM\Http\Response;
use Framework\DataProvider;
use ZM\Store\ZMBuf;
use ZM\Utils\DataProvider;
use ZM\Utils\ZMUtil;
class EventHandler
@ -51,7 +51,7 @@ class EventHandler
});
ZMBuf::$server = $param0;
$r = (new WorkerStartEvent($param0, $param1))->onActivate();
Console::log("\n=== Worker #" . $param0->worker_id . " 已启动 ===\n", "gold");
Console::success("Worker #" . $param0->worker_id . " 已启动");
$r->onAfter();
self::startTick();
} catch (Exception $e) {
@ -69,14 +69,14 @@ class EventHandler
case "message":
/** @var Frame $param1 */
/** @var Server $param0 */
$conn = ConnectionManager::get($param1->fd);
$conn = ManagerGM::get($param1->fd);
set_coroutine_params(["server" => $param0, "frame" => $param1, "connection" => $conn]);
try {
(new MessageEvent($param0, $param1))->onActivate()->onAfter();
} catch (Error $e) {
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
Console::error("Fatal error when calling $event_name: " . $error_msg);
Console::stackTrace();
Console::trace();
}
break;
case "request":
@ -89,7 +89,12 @@ class EventHandler
Console::info($param0->server["remote_addr"] . ":" . $param0->server["remote_port"] .
" [" . $param1->getStatusCode() . "] " . $param0->server["request_uri"]
);
if (!$param1->isEnd()) $param1->end("Internal server error: " . $e->getMessage());
if (!$param1->isEnd()) {
if (\ZM\Config\ZMConfig::get("global", "debug_mode"))
$param1->end("Internal server error: " . $e->getMessage());
else
$param1->end("Internal server error.");
}
Console::error("Internal server exception (500), caused by " . get_class($e));
Console::log($e->getTraceAsString(), "gray");
} catch (Error $e) {
@ -100,7 +105,7 @@ class EventHandler
);
$doc = "Internal server error<br>";
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
if (ZMBuf::$atomics["info_level"]->get() >= 4) $doc .= $error_msg;
if (Console::getLevel() >= 4) $doc .= $error_msg;
if (!$param1->isEnd()) $param1->end($doc);
Console::error("Internal server error (500): " . $error_msg);
Console::log($e->getTraceAsString(), "gray");
@ -114,7 +119,7 @@ class EventHandler
} catch (Error $e) {
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
Console::error("Fatal error when calling $event_name: " . $error_msg);
Console::stackTrace();
Console::trace();
}
break;
case "close":
@ -124,7 +129,7 @@ class EventHandler
} catch (Error $e) {
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
Console::error("Fatal error when calling $event_name: " . $error_msg);
Console::stackTrace();
Console::trace();
}
break;
}
@ -142,7 +147,7 @@ class EventHandler
ctx()->setCache("level", $level);
if ($level >= 5) {
Console::warning("Recursive call reached " . $level . " times");
Console::stackTrace();
Console::trace();
return false;
}
$starttime = microtime(true);
@ -220,7 +225,7 @@ class EventHandler
}
}
public static function callCQAPISend($reply, ?CQConnection $connection) {
public static function callCQAPISend($reply, ?ConnectionObject $connection) {
$action = $reply["action"] ?? null;
if ($action === null) {
Console::warning("API 激活事件异常!");
@ -235,7 +240,7 @@ class EventHandler
foreach (ZMBuf::$events[CQAPISend::class] ?? [] as $k => $v) {
if (($v->action == "" || $v->action == $action) && !$v->with_result) {
$c = $v->class;
self::callWithMiddleware($c, $v->method, context()->copy(), [$reply["action"], $reply["params"] ?? [], $connection->getQQ()]);
self::callWithMiddleware($c, $v->method, context()->copy(), [$reply["action"], $reply["params"] ?? [], $connection->getOption('connect_id')]);
if (context()->getCache("block_continue") === true) break;
}
}

View File

@ -1,19 +1,18 @@
<?php
namespace Framework;
namespace ZM\Event;
use Co;
use Doctrine\Common\Annotations\AnnotationException;
use ZM\Console\Console;
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;
use ZM\Store\ZMBuf;
class ServerEventHandler
{
@ -22,17 +21,13 @@ class ServerEventHandler
* @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();
//AnnotationParser::registerMods();
ob_get_clean();
}
}
@ -44,7 +39,8 @@ class ServerEventHandler
* @throws AnnotationException
*/
public function onMessage($server, Frame $frame) {
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd);
if ($frame->fd !== ZMBuf::get("terminal_fd"))
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd);
EventHandler::callSwooleEvent("message", $server, $frame);
}

View File

@ -5,17 +5,15 @@ namespace ZM\Event\Swoole;
use Closure;
use Framework\Console;
use Framework\ZMBuf;
use ZM\ConnectionManager\ManagerGM;
use ZM\Console\Console;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;
use ZM\Annotation\Swoole\SwooleEventAfter;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Connection\ConnectionManager;
use Exception;
use ZM\Event\EventHandler;
use ZM\ModBase;
use ZM\ModHandleType;
use ZM\Store\ZMBuf;
use ZM\Utils\ZMUtil;
class MessageEvent implements SwooleEvent
@ -39,15 +37,15 @@ class MessageEvent implements SwooleEvent
*/
public function onActivate() {
ZMUtil::checkWait();
$conn = ConnectionManager::get(context()->getFrame()->fd);
$conn = ManagerGM::get(context()->getFrame()->fd);
try {
if ($conn->getType() == "qq") {
if ($conn->getName() == "qq") {
$data = json_decode(context()->getFrame()->data, true);
if (isset($data["post_type"])) {
set_coroutine_params(["data" => $data, "connection" => $conn]);
ctx()->setCache("level", 0);
Console::debug("Calling CQ Event from fd=" . $conn->fd);
EventHandler::callCQEvent($data, ConnectionManager::get(context()->getFrame()->fd), 0);
Console::debug("Calling CQ Event from fd=" . $conn->getFd());
EventHandler::callCQEvent($data, ManagerGM::get(context()->getFrame()->fd), 0);
} else{
set_coroutine_params(["connection" => $conn]);
EventHandler::callCQResponse($data);
@ -78,10 +76,8 @@ class MessageEvent implements SwooleEvent
public function onAfter() {
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
if (strtolower($v->type) == "message" && $this->parseSwooleRule($v) === true) {
$conn = ConnectionManager::get($this->frame->fd);
$c = $v->class;
/** @var ModBase $class */
$class = new $c(["server" => $this->server, "frame" => $this->frame, "connection" => $conn], ModHandleType::SWOOLE_MESSAGE);
$class = new $c();
call_user_func_array([$class, $v->method], []);
if (context()->getCache("block_continue") === true) break;
}
@ -92,7 +88,7 @@ class MessageEvent 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->frame->fd));
if ($v->callback instanceof Closure) return call_user_func($v->callback, ManagerGM::get($this->frame->fd));
break;
case "dataEqual": //handle websocket message事件时才能用
if ($v->callback instanceof Closure) return call_user_func($v->callback, $this->frame->data);

View File

@ -6,14 +6,14 @@ namespace ZM\Event\Swoole;
use Closure;
use Exception;
use Framework\Console;
use ZM\Console\Console;
use Framework\ZMBuf;
use Swoole\Http\Request;
use ZM\Annotation\Swoole\SwooleEventAfter;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Event\EventHandler;
use ZM\Http\Response;
use Framework\DataProvider;
use ZM\Utils\DataProvider;
use ZM\Utils\ZMUtil;
class RequestEvent implements SwooleEvent
@ -38,7 +38,7 @@ class RequestEvent implements SwooleEvent
*/
public function onActivate() {
ZMUtil::checkWait();
foreach (ZMBuf::globals("http_header") as $k => $v) {
foreach (\ZM\Config\ZMConfig::get("global", "http_header") as $k => $v) {
$this->response->setHeader($k, $v);
}
$uri = $this->request->server["request_uri"];
@ -81,9 +81,9 @@ class RequestEvent implements SwooleEvent
}
}
if (ZMBuf::globals("static_file_server")["status"]) {
$base_dir = ZMBuf::globals("static_file_server")["document_root"];
$base_index = ZMBuf::globals("static_file_server")["document_index"];
if (\ZM\Config\ZMConfig::get("global", "static_file_server")["status"]) {
$base_dir = \ZM\Config\ZMConfig::get("global", "static_file_server")["document_root"];
$base_index = \ZM\Config\ZMConfig::get("global", "static_file_server")["document_index"];
$uri = $this->request->server["request_uri"];
$path = realpath($base_dir . urldecode($uri));
if ($path !== false) {
@ -116,7 +116,6 @@ class RequestEvent implements SwooleEvent
$this->response->end(ZMUtil::getHttpCodePage(404));
return $this;
}
context()->setCache("params", $params);
if (in_array(strtoupper($this->request->server["request_method"]), $node["request_method"] ?? [])) { //判断目标方法在不在里面

View File

@ -6,12 +6,13 @@ namespace ZM\Event\Swoole;
use Closure;
use Doctrine\Common\Annotations\AnnotationException;
use Framework\ZMBuf;
use ZM\ConnectionManager\ManagerGM;
use ZM\Console\Console;
use Swoole\Server;
use ZM\Annotation\Swoole\SwooleEventAfter;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Connection\ConnectionManager;
use ZM\Event\EventHandler;
use ZM\Store\ZMBuf;
use ZM\Utils\ZMUtil;
class WSCloseEvent implements SwooleEvent
@ -30,8 +31,9 @@ class WSCloseEvent implements SwooleEvent
* @throws AnnotationException
*/
public function onActivate() {
Console::info("Closed #{$this->fd}");
ZMUtil::checkWait();
set_coroutine_params(["server" => $this->server, "fd" => $this->fd, "connection" => ConnectionManager::get($this->fd)]);
set_coroutine_params(["server" => $this->server, "fd" => $this->fd, "connection" => ManagerGM::get($this->fd)]);
foreach(ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
if(strtolower($v->type) == "close" && $this->parseSwooleRule($v)) {
$c = $v->class;
@ -39,7 +41,7 @@ class WSCloseEvent implements SwooleEvent
if(context()->getCache("block_continue") === true) break;
}
}
ConnectionManager::close($this->fd);
ManagerGM::popConnect($this->fd);
return $this;
}
@ -61,7 +63,7 @@ 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));
if ($v->callback instanceof Closure) return call_user_func($v->callback, ManagerGM::get($this->fd));
break;
}
return true;

View File

@ -6,19 +6,16 @@ namespace ZM\Event\Swoole;
use Closure;
use Doctrine\Common\Annotations\AnnotationException;
use Framework\Console;
use Framework\ZMBuf;
use ZM\Config\ZMConfig;
use ZM\ConnectionManager\ConnectionObject;
use ZM\ConnectionManager\ManagerGM;
use ZM\Console\Console;
use Swoole\Http\Request;
use Swoole\WebSocket\Server;
use ZM\Annotation\Swoole\SwooleEventAfter;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Connection\ConnectionManager;
use ZM\Connection\CQConnection;
use ZM\Connection\UnknownConnection;
use ZM\Connection\WSConnection;
use ZM\Event\EventHandler;
use ZM\ModBase;
use ZM\ModHandleType;
use ZM\Store\ZMBuf;
use ZM\Utils\ZMUtil;
class WSOpenEvent implements SwooleEvent
@ -31,9 +28,7 @@ class WSOpenEvent implements SwooleEvent
* @var Request
*/
private $request;
/**
* @var WSConnection
*/
/** @var ConnectionObject */
private $conn;
public function __construct(Server $server, Request $request) {
@ -47,25 +42,28 @@ class WSOpenEvent implements SwooleEvent
*/
public function onActivate() {
ZMUtil::checkWait();
ManagerGM::pushConnect($this->request->fd);
$type = strtolower($this->request->get["type"] ?? $this->request->header["x-client-role"] ?? "");
$type_conn = ConnectionManager::getTypeClassName($type);
if ($type_conn == CQConnection::class) {
$type_conn = $this->getTypeClassName($type);
if ($type_conn == "qq") {
ManagerGM::setName($this->request->fd, "qq");
$qq = $this->request->get["qq"] ?? $this->request->header["x-self-id"] ?? "";
$self_token = ZMBuf::globals("access_token") ?? "";
if(isset($this->request->header["authorization"])) {
$self_token = ZMConfig::get("global", "access_token") ?? "";
if (isset($this->request->header["authorization"])) {
Console::debug($this->request->header["authorization"]);
}
$remote_token = $this->request->get["token"] ?? (isset($this->request->header["authorization"]) ? explode(" ", $this->request->header["authorization"])[1] : "");
if ($qq != "" && ($self_token == $remote_token)) $this->conn = new CQConnection($this->server, $this->request->fd, $qq);
else {
$this->conn = new UnknownConnection($this->server, $this->request->fd);
if ($qq != "" && ($self_token == $remote_token)) {
ManagerGM::setOption($this->request->fd, "connect_id", $qq);
$this->conn = ManagerGM::get($this->request->fd);
} else {
$this->conn = ManagerGM::get($this->request->fd);
Console::warning("connection of CQ has invalid QQ or token!");
Console::debug("Remote token: ".$remote_token);
Console::debug("Remote token: " . $remote_token);
}
} else {
$this->conn = new $type_conn($this->server, $this->request->fd);
$this->conn = ManagerGM::get($this->request->fd);
}
ZMBuf::$connect[$this->request->fd] = $this->conn;
set_coroutine_params(["server" => $this->server, "request" => $this->request, "connection" => $this->conn]);
foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
if (strtolower($v->type) == "open" && $this->parseSwooleRule($v) === true) {
@ -86,11 +84,10 @@ class WSOpenEvent implements SwooleEvent
* @inheritDoc
*/
public function onAfter() {
if (!$this->conn->exists()) return $this;
if (!$this->server->exists($this->conn->getFd())) return $this;
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
if (strtolower($v->type) == "open" && $this->parseSwooleRule($v) === true) {
/** @var ModBase $class */
$class = new $v["class"](["server" => $this->server, "request" => $this->request, "connection" => $this->conn], ModHandleType::SWOOLE_OPEN);
$class = new $v["class"]();
call_user_func_array([$class, $v["method"]], [$this->conn]);
if (context()->getCache("block_continue") === true) break;
}
@ -106,4 +103,15 @@ class WSOpenEvent implements SwooleEvent
}
return true;
}
private function getTypeClassName(string $type) {
$map = [
"qq" => "qq",
"universal" => "qq",
"webconsole" => "webconsole",
"proxy" => "proxy",
"terminal" => "terminal"
];
return $map[$type] ?? "default";
}
}

View File

@ -4,7 +4,6 @@
namespace ZM\Event\Swoole;
use Co;
use Doctrine\Common\Annotations\AnnotationException;
use Exception;
use PDO;
@ -14,20 +13,18 @@ use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;
use Swoole\Process;
use Swoole\Timer;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\AnnotationParser;
use ZM\Annotation\Swoole\OnStart;
use ZM\Annotation\Swoole\SwooleEventAfter;
use ZM\Connection\ConnectionManager;
use ZM\Config\ZMConfig;
use ZM\Context\ContextInterface;
use ZM\DB\DB;
use Framework\Console;
use Framework\GlobalConfig;
use Framework\ZMBuf;
use ZM\Console\Console;
use Swoole\Server;
use ZM\Event\EventHandler;
use ZM\Exception\DbException;
use Framework\DataProvider;
use ZM\Store\ZMBuf;
use ZM\Utils\DataProvider;
use ZM\Utils\Terminal;
use ZM\Utils\ZMUtil;
class WorkerStartEvent implements SwooleEvent
@ -50,50 +47,46 @@ class WorkerStartEvent implements SwooleEvent
* @throws DbException
*/
public function onActivate(): WorkerStartEvent {
Console::info("Worker启动中");
ZMBuf::$server = $this->server;
Console::listenConsole(); //这个方法只能在这里调用且如果worker_num不为1的话此功能不可用
Process::signal(SIGINT, function () {
Console::warning("Server interrupted by keyboard.");
ZMUtil::stop(true);
});
Console::info("Worker #{$this->server->worker_id} 启动中");
ZMBuf::$server = $this->server;
ZMBuf::resetCache(); //清空变量缓存
ZMBuf::set("wait_start", []); //添加队列在workerStart运行完成前先让其他协程等待执行
$this->resetConnections();//释放所有与framework的连接
//设置炸毛buf中储存的对象
ZMBuf::$globals = new GlobalConfig();
ZMBuf::$config = [];
$file = scandir(DataProvider::getWorkingDir() . '/config/');
unset($file[0], $file[1]);
foreach ($file as $k => $v) {
if ($v == "global.php") continue;
$name = explode(".", $v);
if (($prefix = end($name)) == "json") {
ZMBuf::$config[$name[0]] = json_decode(Co::readFile(DataProvider::getWorkingDir() . '/config/' . $v), true);
Console::info("已读取配置文件:" . $v);
} elseif ($prefix == "php") {
ZMBuf::$config[$name[0]] = include_once DataProvider::getWorkingDir() . '/config/' . $v;
if (is_array(ZMBuf::$config[$name[0]]))
Console::info("已读取配置文件:" . $v);
}
global $terminal_id;
Terminal::listenConsole($terminal_id); //这个方法只能在这里调用且如果worker_num不为1的话此功能不可用
// 这里执行的是只需要执行一遍的代码,比如终端监听器和键盘监听器
if ($this->server->worker_id === 0) {
if($terminal_id !== null) Console::info("监听console输入");
Process::signal(SIGINT, function () {
echo PHP_EOL;
Console::warning("Server interrupted by keyboard.");
ZMUtil::stop();
});
ZMBuf::$atomics['reload_time']->add(1);
$this->setAutosaveTimer(ZMConfig::get("global", "auto_save_interval"));
} else {
Process::signal(SIGINT, function () {
// Do Nothing
});
}
if (ZMBuf::globals("sql_config")["sql_host"] != "") {
if (ZMConfig::get("global", "sql_config")["sql_host"] != "") {
Console::info("新建SQL连接池中");
ob_start();
phpinfo();
$str = ob_get_clean();
$str = explode("\n", $str);
foreach($str as $k => $v) {
foreach ($str as $k => $v) {
$v = trim($v);
if($v == "") continue;
if(mb_strpos($v, "API Extensions") === false) continue;
if(mb_strpos($v, "pdo_mysql") === false) {
if ($v == "") continue;
if (mb_strpos($v, "API Extensions") === false) continue;
if (mb_strpos($v, "pdo_mysql") === false) {
throw new DbException("未安装 mysqlnd php-mysql扩展。");
}
}
$sql = ZMBuf::globals("sql_config");
$sql = ZMConfig::get("global", "sql_config");
ZMBuf::$sql_pool = new PDOPool((new PDOConfig())
->withHost($sql["sql_host"])
->withPort($sql["sql_port"])
@ -107,11 +100,6 @@ class WorkerStartEvent implements SwooleEvent
DB::initTableList();
}
ZMBuf::$atomics['reload_time']->add(1);
Console::info("监听console输入");
$this->setAutosaveTimer(ZMBuf::globals("auto_save_interval"));
$this->loadAllClass(); //加载composer资源、phar外置包、注解解析注册等
return $this;
}
@ -126,22 +114,14 @@ class WorkerStartEvent implements SwooleEvent
}
ZMBuf::unsetCache("wait_start");
set_coroutine_params(["server" => $this->server, "worker_id" => $this->worker_id]);
foreach (ZMBuf::$events[OnStart::class] ?? [] as $v) {
$class_name = $v->class;
Console::debug("正在调用启动时函数: " . $class_name . " -> " . $v->method);
EventHandler::callWithMiddleware($class_name, $v->method, ["server" => $this->server, "worker_id" => $this->worker_id], []);
}
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
/** @var AnnotationBase $v */
if (strtolower($v->type) == "workerstart") {
if($this->server->worker_id === 0) {
foreach (ZMBuf::$events[OnStart::class] ?? [] as $v) {
$class_name = $v->class;
Console::debug("正在调用启动时函数after: " . $class_name . " -> " . $v->method);
Console::debug("正在调用启动时函数: " . $class_name . " -> " . $v->method);
EventHandler::callWithMiddleware($class_name, $v->method, ["server" => $this->server, "worker_id" => $this->worker_id], []);
if (context()->getCache("block_continue") === true) break;
}
Console::debug("@OnStart 执行完毕");
}
Console::debug("调用完毕!");
return $this;
}
@ -156,7 +136,6 @@ class WorkerStartEvent implements SwooleEvent
}
/**
* @throws AnnotationException
* @throws ReflectionException
* @throws Exception
*/
@ -181,15 +160,13 @@ class WorkerStartEvent implements SwooleEvent
//加载各个模块的注解类,以及反射
Console::info("检索Module中");
AnnotationParser::registerMods();
//加载Custom目录下的自定义的内部类
ConnectionManager::registerCustomClass();
$parser = new AnnotationParser();
$parser->addRegisterPath(DataProvider::getWorkingDir() . "/src/Module/", "Module");
$parser->registerMods();
$parser->sortLevels();
//加载自定义的全局函数
Console::debug("加载自定义的全局函数中");
if (file_exists(DataProvider::getWorkingDir() . "/src/Custom/global_function.php"))
require_once DataProvider::getWorkingDir() . "/src/Custom/global_function.php";
$this->afterCheck();
}
@ -204,7 +181,7 @@ class WorkerStartEvent implements SwooleEvent
* @throws Exception
*/
private function afterCheck() {
$context_class = ZMBuf::globals("context_class");
$context_class = ZMConfig::get("global", "context_class");
if (!is_a($context_class, ContextInterface::class, true)) {
throw new Exception("Context class must implemented from ContextInterface!");
}

View File

@ -6,15 +6,13 @@ namespace ZM\Exception;
use Exception;
use Throwable;
use ZM\ModBase;
class WaitTimeoutException extends Exception
{
/** @var ModBase */
public $module;
public function __construct($module, $message = "", $code = 0, Throwable $previous = null) {
parent::__construct($message, $code, $previous);
$this->module = $module;
}
}
}

292
src/ZM/Framework.php Normal file
View File

@ -0,0 +1,292 @@
<?php
namespace ZM;
use Doctrine\Common\Annotations\AnnotationReader;
use Exception;
use ZM\Config\ZMConfig;
use ZM\ConnectionManager\ManagerGM;
use ZM\Event\ServerEventHandler;
use ZM\Store\ZMBuf;
use ZM\Utils\DataProvider;
use Framework\RemoteShell;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
use Swoole\Runtime;
use Swoole\WebSocket\Server;
use ZM\Annotation\Swoole\OnEvent;
use ZM\Console\Console;
class Framework
{
/**
* @var array
*/
private static $argv;
/**
* @var Server
*/
public static $server;
/**
* @var array|bool|mixed|null
*/
private $server_set;
public function __construct($args = []) {
$tty_width = $this->getTtyWidth();
if (LOAD_MODE == 0) define("WORKING_DIR", getcwd());
elseif (LOAD_MODE == 1) define("WORKING_DIR", realpath(__DIR__ . "/../../"));
elseif (LOAD_MODE == 2) echo "Phar mode: " . WORKING_DIR . PHP_EOL;
require_once "Utils/DataProvider.php";
if (file_exists(DataProvider::getWorkingDir() . "/vendor/autoload.php")) {
/** @noinspection PhpIncludeInspection */
require_once DataProvider::getWorkingDir() . "/vendor/autoload.php";
}
if (LOAD_MODE == 2) {
// Phar 模式2.0 不提供哦
//require_once FRAMEWORK_DIR . "/vendor/autoload.php";
spl_autoload_register('phar_classloader');
} elseif (LOAD_MODE == 0) {
/** @noinspection PhpIncludeInspection
* @noinspection RedundantSuppression
*/
require_once WORKING_DIR . "/vendor/autoload.php";
}
if (!is_dir(DataProvider::getWorkingDir() . '/src/')) {
die("Unable to find source directory.\nMaybe you need to run \"init\"?");
}
ZMConfig::setDirectory(DataProvider::getWorkingDir().'/config');
ZMConfig::env($args["env"] ?? "");
if(ZMConfig::get("global") === false) die("Global config load failed: ".ZMConfig::$last_error);
self::$argv = $args;
$this->defineProperties();
ZMBuf::initAtomic();
ManagerGM::init(1024, 0.2, [
[
"key" => "connect_id",
"type" => "string",
"size" => 30
]
]);
//start swoole Framework
$this->selfCheck();
try {
self::$server = new Server(ZMConfig::get("global", "host"), ZMConfig::get("global", "port"));
$this->server_set = ZMConfig::get("global", "swoole");
Console::init(
ZMConfig::get("global", "info_level"),
self::$server,
$args["log-theme"] ?? "default",
($o = ZMConfig::get("console_color")) === false ? [] : $o
);
// 注册 Swoole Server 的事件
$this->registerServerEvents();
$timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai";
date_default_timezone_set($timezone);
$this->parseCliArgs(self::$argv);
$out = [
"host" => ZMConfig::get("global", "host"),
"port" => ZMConfig::get("global", "port"),
"log_level" => Console::getLevel(),
"version" => ZM_VERSION,
"config" => $args["env"] === null ? 'global.php' : $args["env"],
"working_dir" => DataProvider::getWorkingDir()
];
if (isset(ZMConfig::get("global", "swoole")["task_worker_num"])) {
$out["task_worker_num"] = ZMConfig::get("global", "swoole")["task_worker_num"];
}
if (($num = ZMConfig::get("global", "swoole")["worker_num"] ?? swoole_cpu_num()) != 1) {
$out["worker_num"] = $num;
}
$store = "";
foreach ($out as $k => $v) {
$line = $k . ": " . $v;
if (strlen($line) > 19 && $store == "" || $tty_width < 53) {
Console::log($line);
} else {
if ($store === "") $store = str_pad($line, 19, " ", STR_PAD_RIGHT);
else {
$store .= (" | " . $line);
Console::log($store);
$store = "";
}
}
}
if ($store != "") Console::log($store);
self::$server->set($this->server_set);
if (file_exists(DataProvider::getWorkingDir() . "/config/motd.txt")) {
$motd = file_get_contents(DataProvider::getWorkingDir() . "/config/motd.txt");
} else {
$motd = file_get_contents(__DIR__."/../../config/motd.txt");
}
$motd = explode("\n", $motd);
foreach ($motd as $k => $v) {
$motd[$k] = substr($v, 0, $tty_width);
}
$motd = implode("\n", $motd);
echo $motd;
global $asd;
$asd = get_included_files();
self::$server->start();
} catch (Exception $e) {
Console::error("Framework初始化出现错误请检查");
Console::error($e->getMessage());
die;
}
}
private function defineProperties() {
define("ZM_START_TIME", microtime(true));
define("ZM_DATA", ZMConfig::get("global", "zm_data"));
define("ZM_VERSION", json_decode(file_get_contents(__DIR__ . "/../../composer.json"), true)["version"] ?? "unknown");
define("CONFIG_DIR", ZMConfig::get("global", "config_dir"));
define("CRASH_DIR", ZMConfig::get("global", "crash_dir"));
@mkdir(ZM_DATA);
@mkdir(CONFIG_DIR);
@mkdir(CRASH_DIR);
define("ZM_MATCH_ALL", 0);
define("ZM_MATCH_FIRST", 1);
define("ZM_MATCH_NUMBER", 2);
define("ZM_MATCH_SECOND", 3);
define("ZM_BREAKPOINT", 'if(Framework::$argv["debug-mode"]) extract(\Psy\debug(get_defined_vars(), isset($this) ? $this : @get_called_class()));');
define("BP", ZM_BREAKPOINT);
define("ZM_DEFAULT_FETCH_MODE", 4);
}
private function selfCheck() {
if (!extension_loaded("swoole")) die("Can not find swoole extension.\n");
if (version_compare(SWOOLE_VERSION, "4.4.13") == -1) die("You must install swoole version >= 4.4.13 !");
//if (!extension_loaded("gd")) die("Can not find gd extension.\n");
if (!extension_loaded("sockets")) die("Can not find sockets extension.\n");
if (substr(PHP_VERSION, 0, 1) != "7") die("PHP >=7 required.\n");
//if (!function_exists("curl_exec")) die("Can not find curl extension.\n");
//if (!class_exists("ZipArchive")) die("Can not find Zip extension.\n");
//if (!file_exists(CRASH_DIR . "last_error.log")) die("Can not find log file.\n");
return true;
}
/**
* 从全局配置文件里读取注入系统事件的类
* @throws ReflectionException
* @throws ReflectionException
*/
private function registerServerEvents() {
$all_event_class = ZMConfig::get("global", "server_event_handler_class") ?? [];
if (!in_array(ServerEventHandler::class, $all_event_class)) {
$all_event_class[] = ServerEventHandler::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) {
self::$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);
});
}
}
/**
* 解析命令行的 $argv 参数们
* @param $args
* @throws Exception
*/
private function parseCliArgs($args) {
$coroutine_mode = true;
global $terminal_id;
$terminal_id = call_user_func(function () {
try {
$data = random_bytes(16);
} catch (Exception $e) {
return "";
}
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return strtoupper(vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)));
});
foreach ($args as $x => $y) {
switch ($x) {
case 'debug-mode':
if ($y) {
$coroutine_mode = false;
$terminal_id = null;
Console::warning("You are in debug mode, do not use in production!");
}
break;
case 'daemon':
if ($y) {
$this->server_set["daemonize"] = 1;
Console::log("已启用守护进程,输出重定向到 " . $this->server_set["log_file"]);
$terminal_id = null;
}
break;
case 'disable-console-input':
if ($y) $terminal_id = null;
break;
case 'remote-shell':
if ($y) {
$host = "127.0.0.1";
$port = 9599;
RemoteShell::listen(self::$server, $host, $port);
Console::log(Console::setColor("正在监听" . $host . ":" . strval($port) . "的调试接口,请注意安全", "yellow"));
}
break;
case 'log-error':
if ($y) Console::setLevel(0);
break;
case 'log-warning':
if ($y) Console::setLevel(1);
break;
case 'log-info':
if ($y) Console::setLevel(2);
break;
case 'log-verbose':
if ($y) Console::setLevel(3);
break;
case 'log-debug':
if ($y) Console::setLevel(4);
break;
case 'log-theme':
if($y !== null) {
Console::$theme = $y;
}
break;
}
}
if ($coroutine_mode) Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL);
}
private function getTtyWidth() {
return explode(" ", trim(exec("stty size")))[1];
}
public static function getServer() {
return self::$server;
}
}

View File

@ -4,7 +4,7 @@
namespace ZM\Http;
use Framework\Console;
use ZM\Console\Console;
use Framework\ZMBuf;
use ZM\Utils\ZMUtil;

View File

@ -1,170 +0,0 @@
<?php
namespace ZM;
use Co;
use Framework\ZMBuf;
use Swoole\Http\Request;
use ZM\API\CQAPI;
use ZM\Connection\WSConnection;
use ZM\Exception\InvalidArgumentException;
use ZM\Exception\WaitTimeoutException;
use ZM\Http\Response;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;
/**
* Class ModBase
* @package ZM
* @deprecated
*/
abstract class ModBase
{
/** @var Server */
protected $server;
/** @var Frame */
protected $frame;
/** @var array */
protected $data;
/** @var Request */
protected $request;
/** @var Response */
protected $response;
/** @var int */
protected $fd;
/** @var int */
protected $worker_id;
/** @var WSConnection */
protected $connection;
protected $handle_type = ModHandleType::CQ_MESSAGE;
public $block_continue = false;
public function __construct($param0 = [], $handle_type = 0) {
if (isset($param0["server"])) $this->server = $param0["server"];
if (isset($param0["frame"])) $this->frame = $param0["frame"];
if (isset($param0["data"])) $this->data = $param0["data"];
if (isset($param0["request"])) $this->request = $param0["request"];
if (isset($param0["response"])) $this->response = $param0["response"];
if (isset($param0["fd"])) $this->fd = $param0["fd"];
if (isset($param0["worker_id"])) $this->worker_id = $param0["worker_id"];
if (isset($param0["connection"])) $this->connection = $param0["connection"];
$this->handle_type = $handle_type;
}
/**
* only can used by cq->message event function
* @param $msg
* @param bool $yield
* @return mixed
*/
public function reply($msg, $yield = false) {
switch ($this->data["message_type"]) {
case "group":
case "private":
case "discuss":
return CQAPI::quick_reply($this->connection, $this->data, $msg, $yield);
}
return false;
}
public function finalReply($msg, $yield = false) {
$this->setBlock();
if ($msg == "") return true;
return $this->reply($msg, $yield);
}
/**
* @param string $prompt
* @param int $timeout
* @param string $timeout_prompt
* @return string
* @throws InvalidArgumentException
* @throws WaitTimeoutException
*/
public function waitMessage($prompt = "", $timeout = 600, $timeout_prompt = "") {
if ($prompt != "") $this->reply($prompt);
if (!isset($this->data["user_id"], $this->data["message"], $this->data["self_id"]))
throw new InvalidArgumentException("协程等待参数缺失");
$cid = Co::getuid();
$api_id = ZMBuf::$atomics["wait_msg_id"]->get();
ZMBuf::$atomics["wait_msg_id"]->add(1);
$hang = [
"coroutine" => $cid,
"user_id" => $this->data["user_id"],
"message" => $this->data["message"],
"self_id" => $this->data["self_id"],
"message_type" => $this->data["message_type"],
"result" => null
];
if ($hang["message_type"] == "group" || $hang["message_type"] == "discuss") {
$hang[$hang["message_type"] . "_id"] = $this->data[$this->data["message_type"] . "_id"];
}
ZMBuf::appendKey("wait_api", $api_id, $hang);
$id = swoole_timer_after($timeout * 1000, function () use ($api_id, $timeout_prompt) {
$r = ZMBuf::get("wait_api")[$api_id] ?? null;
if ($r !== null) {
Co::resume($r["coroutine"]);
}
});
Co::suspend();
$sess = ZMBuf::get("wait_api")[$api_id];
ZMBuf::unsetByValue("wait_api", $api_id);
$result = $sess["result"];
if (isset($id)) swoole_timer_clear($id);
if ($result === null) throw new WaitTimeoutException($this, $timeout_prompt);
return $result;
}
/**
* @param $arg
* @param $mode
* @param $prompt_msg
* @return mixed|string
* @throws InvalidArgumentException
* @throws WaitTimeoutException
*/
public function getArgs(&$arg, $mode, $prompt_msg) {
switch ($mode) {
case ZM_MATCH_ALL:
$p = $arg;
array_shift($p);
return trim(implode(" ", $p)) == "" ? $this->waitMessage($prompt_msg) : trim(implode(" ", $p));
case ZM_MATCH_NUMBER:
foreach ($arg as $k => $v) {
if (is_numeric($v)) {
array_splice($arg, $k, 1);
return $v;
}
}
return $this->waitMessage($prompt_msg);
case ZM_MATCH_FIRST:
if (isset($arg[1])) {
$a = $arg[1];
array_splice($arg, 1, 1);
return $a;
} else {
return $this->waitMessage($prompt_msg);
}
}
throw new InvalidArgumentException();
}
public function getMessage() { return $this->data["message"] ?? null; }
public function getUserId() { return $this->data["user_id"] ?? null; }
public function getGroupId() { return $this->data["group_id"] ?? null; }
public function getMessageType() { return $this->data["message_type"] ?? null; }
public function getRobotId() { return $this->data["self_id"]; }
public function getConnection() { return $this->connection; }
public function setBlock($result = true) { context()->setCache("block_continue", $result); }
}

View File

@ -1,20 +0,0 @@
<?php
namespace ZM;
class ModHandleType
{
const CQ_MESSAGE = 0;
const CQ_REQUEST = 1;
const CQ_NOTICE = 2;
const CQ_META_EVENT = 3;
const SWOOLE_OPEN = 4;
const SWOOLE_CLOSE = 5;
const SWOOLE_MESSAGE = 6;
const SWOOLE_REQUEST = 7;
const SWOOLE_WORKER_START = 8;
const SWOOLE_WORKER_STOP = 9;
}

View File

@ -6,20 +6,18 @@
* Time: 下午11:11
*/
namespace Framework;
namespace ZM\Store;
use Swoole\Atomic;
use Swoole\Database\PDOPool;
use swoole_atomic;
use ZM\connection\WSConnection;
use ZM\Config\ZMConfig;
class ZMBuf
{
//读写的缓存数据需要在worker_num = 1下才能正常使用
/** @var mixed[] ZMBuf的data */
private static $cache = [];
/** @var WSConnection[] */
static $connect = [];//储存连接实例的数组
//Scheduler计划任务连接实例只可以在单worker_num时使用
static $scheduler = null; //This is stupid warning...
@ -27,9 +25,6 @@ class ZMBuf
/** @var PDOPool */
static $sql_pool = null;//保存sql连接池的类
//只读的数据可以在多worker_num下使用
/** @var null|\Framework\GlobalConfig */
static $globals = null;
// swoole server操作对象每个进程均分配
/** @var \swoole_websocket_server $server */
@ -95,17 +90,12 @@ class ZMBuf
return in_array($val, self::$cache[$name]);
}
static function globals($key) {
return self::$globals->get($key);
}
static function config($config_name) {
return self::$config[$config_name] ?? null;
}
public static function resetCache() {
self::$cache = [];
self::$connect = [];
self::$time_nlp = null;
self::$instance = [];
}
@ -114,8 +104,9 @@ class ZMBuf
* 初始化atomic计数器
*/
public static function initAtomic() {
foreach (ZMBuf::globals("init_atomics") as $k => $v) {
foreach (ZMConfig::get("global", "init_atomics") as $k => $v) {
self::$atomics[$k] = new Atomic($v);
}
self::$atomics["show_log_worker"] = new Atomic(999999);
}
}

View File

@ -1,10 +1,13 @@
<?php
namespace Framework;
namespace ZM\Utils;
use ZM\Config\ZMConfig;
use ZM\Console\Console;
use ZM\Annotation\Swoole\OnSave;
use ZM\Store\ZMBuf;
class DataProvider
{
@ -34,7 +37,7 @@ class DataProvider
public static function saveBuffer() {
$head = Console::setColor(date("[H:i:s] ") . "[V] Saving buffer......", "blue");
if (ZMBuf::$atomics["info_level"]->get() >= 3)
if (Console::getLevel() >= 3)
echo $head;
foreach (self::$buffer_list as $k => $v) {
Console::debug("Saving " . $k . " to " . $v);
@ -47,12 +50,12 @@ class DataProvider
Console::debug("Calling @OnSave: $c -> $method");
$class->$method();
}
if (ZMBuf::$atomics["info_level"]->get() >= 3)
if (Console::getLevel() >= 3)
echo Console::setColor("saved", "blue") . PHP_EOL;
}
public static function getFrameworkLink() {
return ZMBuf::globals("http_reverse_link");
return ZMConfig::get("global", "http_reverse_link");
}
public static function getJsonData(string $string) {

View File

@ -40,7 +40,6 @@ class RemoteShell
* @throws Exception
*/
static function listen($serv, $host = "127.0.0.1", $port = 9599) {
Console::warning("正在监听".$host.":".strval($port)."的调试接口,请注意安全");
$port = $serv->listen($host, $port, SWOOLE_SOCK_TCP);
if (!$port) {
throw new Exception("listen fail.");
@ -261,4 +260,4 @@ function get_arg(&$arg) {
}
$arg = get_class($arg) . ' Object (' . implode(',', $args) . ')';
}
}
}

View File

@ -1,122 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: jerry
* Date: 2019/1/5
* Time: 4:48 PM
*/
namespace ZM\Utils;
use framework\Console;
use framework\ZMBuf;
use PDO;
use PDOException;
use SplQueue;
use Swoole\Coroutine;
class SQLPool
{
protected $available = true;
protected $pool;
private $info;
public $co_list = [];
public $connect_cnt = 0;
public function __construct() {
$this->pool = new SplQueue;
$this->info = [
"host" => ZMBuf::globals("sql_config")["sql_host"],
"port" => ZMBuf::globals("sql_config")["sql_port"],
"user" => ZMBuf::globals("sql_config")["sql_username"],
"password" => ZMBuf::globals("sql_config")["sql_password"],
"database" => ZMBuf::globals("sql_config")["sql_database"]
];
Console::debug("新建检测 MySQL 连接的计时器");
zm_timer_tick(10000, function () {
//Console::debug("正在检测是否有坏死的MySQL连接当前连接池有 ".count($this->pool) . " 个连接");
if (count($this->pool) > 0) {
/** @var PDO $cnn */
$cnn = $this->pool->pop();
$this->connect_cnt -= 1;
try {
$cnn->getAttribute(PDO::ATTR_SERVER_INFO);
} catch (PDOException $e) {
if (strpos($e->getMessage(), 'MySQL server has gone away') !== false) {
Console::info("MySQL 长连接丢失,取消连接");
unset($cnn);
return;
}
}
$this->pool->push($cnn);
$this->connect_cnt += 1;
}
});
}
/**
* 将利用过的连接入队
* @param $mysql
*/
public function put($mysql) {
$this->pool->push($mysql);
if (($a = array_shift($this->co_list)) !== null) {
Coroutine::resume($a);
}
}
/**
* 获取队中的连接,如果不存在则创建新的
* @param bool $no_new_conn
* @return bool|mixed|PDO
*/
public function get($no_new_conn = false) {
if (count($this->pool) == 0 && $this->connect_cnt <= 70) {
if ($no_new_conn) return false;
$this->connect_cnt += 1;
$r = $this->newConnect();
if ($r !== false) {
return $r;
} else {
$this->connect_cnt -= 1;
return false;
}
} elseif (count($this->pool) > 0) {
/** @var PDO $con */
$con = $this->pool->pop();
return $con;
} elseif ($this->connect_cnt > 70) {
$this->co_list[] = Coroutine::getuid();
Console::warning("数据库连接过多,协程等待重复利用中...当前协程数 " . Coroutine::stats()["coroutine_num"]);
Coroutine::suspend();
return $this->get($no_new_conn);
}
return false;
}
public function getCount() {
return $this->pool->count();
}
public function destruct() {
// 连接池销毁, 置不可用状态, 防止新的客户端进入常驻连接池, 导致服务器无法平滑退出
$this->available = false;
while (!$this->pool->isEmpty()) {
$this->pool->pop();
}
}
private function newConnect() {
//无空闲连接,创建新连接
$dsn = "mysql:host=" . $this->info["host"] . ";dbname=" . $this->info["database"] . ";charset=utf8";
try {
$mysql = new PDO($dsn, $this->info["user"], $this->info["password"], array(PDO::ATTR_PERSISTENT => true));
} catch (PDOException $e) {
Console::error("PDO Error: " . $e->getMessage());
return false;
}
Console::info("创建SQL连接中当前有" . $this->connect_cnt . "个连接");
return $mysql;
}
}

View File

@ -1,10 +0,0 @@
<?php
namespace ZM\Utils;
class ScheduleManager
{
//TODO: 写framework部分的schedule控制代码
}

126
src/ZM/Utils/Terminal.php Normal file
View File

@ -0,0 +1,126 @@
<?php
namespace ZM\Utils;
use Exception;
use ZM\Config\ZMConfig;
use ZM\ConnectionManager\ConnectionObject;
use ZM\Console\Console;
use ZM\Store\ZMBuf;
use ZM\Annotation\Swoole\SwooleEventAt;
class Terminal
{
/**
* @var false|resource
*/
public static $console_proc = null;
public static $pipes = [];
static function listenConsole($terminal_id) {
if ($terminal_id === null) {
if (ZMBuf::$server->worker_id === 0) Console::info("ConsoleCommand disabled.");
return;
}
global $terminal_id;
global $port;
$port = ZMConfig::get("global", "port");
$vss = new SwooleEventAt();
$vss->type = "open";
$vss->level = 256;
$vss->rule = "connectType:terminal";
$vss->callback = function (?ConnectionObject $conn) use ($terminal_id) {
$req = ctx()->getRequest();
if ($conn->getName() != "terminal") return false;
Console::debug("Terminal fd: " . $conn->getFd());
ZMBuf::set("terminal_fd", $conn->getFd());
if (($req->header["x-terminal-id"] ?? "") != $terminal_id) {
ZMBuf::$server->close($conn->getFd());
return false;
}
return false;
};
ZMBuf::$events[SwooleEventAt::class][] = $vss;
$vss2 = new SwooleEventAt();
$vss2->type = "message";
$vss2->rule = "connectType:terminal";
$vss2->callback = function (?ConnectionObject $conn) {
if ($conn === null) return false;
if ($conn->getName() != "terminal") return false;
$cmd = ctx()->getFrame()->data;
self::executeCommand($cmd);
return false;
};
ZMBuf::$events[SwooleEventAt::class][] = $vss2;
if (ZMBuf::$server->worker_id === 0) {
go(function () {
global $terminal_id, $port;
$descriptorspec = array(
0 => STDIN,
1 => STDOUT,
2 => STDERR
);
self::$console_proc = proc_open('php -r \'$terminal_id = "' . $terminal_id . '";$port = ' . $port . ';require "' . __DIR__ . '/terminal_listener.php";\'', $descriptorspec, self::$pipes);
});
}
}
/**
* @param string $cmd
* @return bool
*/
private static function executeCommand(string $cmd) {
$it = explodeMsg($cmd);
switch ($it[0] ?? '') {
case 'logtest':
Console::log(date("[H:i:s]") . " [L] This is normal msg. (0)");
Console::error("This is error msg. (0)");
Console::warning("This is warning msg. (1)");
Console::info("This is info msg. (2)");
Console::success("This is success msg. (2)");
Console::verbose("This is verbose msg. (3)");
Console::debug("This is debug msg. (4)");
return true;
case 'call':
$class_name = $it[1];
$function_name = $it[2];
$class = new $class_name([]);
call_user_func_array([$class, $function_name], []);
return true;
case 'bc':
$code = base64_decode($it[1] ?? '', true);
try {
eval($code);
} catch (Exception $e) {
}
return true;
case 'echo':
Console::info($it[1]);
return true;
case 'color':
Console::log($it[2], $it[1]);
return true;
case 'stop':
ZMUtil::stop();
return false;
case 'reload':
case 'r':
ZMUtil::reload();
return false;
case 'save':
//$origin = ZMBuf::$atomics["info_level"]->get();
//ZMBuf::$atomics["info_level"]->set(3);
DataProvider::saveBuffer();
//ZMBuf::$atomics["info_level"]->set($origin);
return true;
case '':
return true;
default:
Console::info("Command not found: " . $cmd);
return true;
}
}
}

View File

@ -4,8 +4,7 @@
namespace ZM\Utils;
use Framework\Console;
use Swlib\Saber;
use ZM\Console\Console;
use Swoole\Coroutine\Http\Client;
class ZMRequest
@ -79,14 +78,6 @@ class ZMRequest
return new ZMWebSocket($url, $set, $header);
}
/**
* @param $option
* @return Saber
*/
public static function session($option) {
return Saber::session($option);
}
/**
* @param $url
* @param array $attribute

View File

@ -5,10 +5,10 @@ namespace ZM\Utils;
use Co;
use framework\Console;
use Framework\DataProvider;
use Framework\ZMBuf;
use ZM\Config\ZMConfig;
use ZM\Console\Console;
use ZM\API\CQ;
use ZM\Store\ZMBuf;
class ZMUtil
{
@ -34,8 +34,8 @@ class ZMUtil
}
public static function getHttpCodePage(int $http_code) {
if (isset(ZMBuf::globals("http_default_code_page")[$http_code])) {
return Co::readFile(DataProvider::getResourceFolder() . "html/" . ZMBuf::globals("http_default_code_page")[$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]);
} else return null;
}
@ -51,20 +51,10 @@ class ZMUtil
ZMBuf::$server->reload();
}
/**
* 解析CQ码
* @param $msg
* @return array|null
*/
static function getCQ($msg) {
return CQ::getCQ($msg);
}
public static function getModInstance($class) {
if (!isset(ZMBuf::$instance[$class])) {
Console::debug("Class instance $class not exist, so I created it.");
ZMBuf::$instance[$class] = new $class();
return ZMBuf::$instance[$class];
return ZMBuf::$instance[$class] = new $class();
} else {
return ZMBuf::$instance[$class];
}

View File

@ -4,7 +4,7 @@
namespace ZM\Utils;
use Framework\Console;
use ZM\Console\Console;
use Swoole\Coroutine\Http\Client;
use Swoole\WebSocket\Frame;

View File

@ -1,16 +1,18 @@
<?php
use Framework\Console;
use Framework\DataProvider;
use Framework\ZMBuf;
use ZM\Config\ZMConfig;
use ZM\Console\Console;
use ZM\Framework;
use ZM\Store\ZMBuf;
use ZM\Utils\DataProvider;
use Swoole\Coroutine\System;
use ZM\Context\ContextInterface;
use ZM\Utils\ZMUtil;
function phar_classloader($p){
function phar_classloader($p) {
$filepath = getClassPath($p);
if($filepath === null) {
if ($filepath === null) {
Console::debug("F:Warning: get class path wrongs.$p");
return;
}
@ -70,10 +72,10 @@ function unicode_decode($str) {
* @return array
*/
function getAllClasses($dir, $indoor_name) {
if(!is_dir($dir)) return [];
if (!is_dir($dir)) return [];
$list = scandir($dir);
$classes = [];
unset($list[0], $list[1]);
if ($list[0] == '.') unset($list[0], $list[1]);
foreach ($list as $v) {
//echo "Finding " . $dir . $v . PHP_EOL;
//echo "At " . $indoor_name . PHP_EOL;
@ -182,7 +184,7 @@ function context() {
*/
function ctx() {
$cid = Co::getCid();
$c_class = ZMBuf::globals("context_class");
$c_class = ZMConfig::get("global", "context_class");
if (isset(ZMBuf::$context[$cid])) {
return ZMBuf::$context_class[$cid] ?? (ZMBuf::$context_class[$cid] = new $c_class($cid));
} else {
@ -222,6 +224,10 @@ function zm_timer_tick($ms, callable $callable) {
});
}
function server() {
return Framework::getServer();
}