From 59fde3d0759aaae48c626baa42c32fb35e8e173a Mon Sep 17 00:00:00 2001 From: whale Date: Fri, 5 Jun 2020 13:36:30 +0800 Subject: [PATCH] update to 1.5 version --- README.md | 7 +- bin/phar-build | 6 +- bin/start | 26 +++++- bin/systemd | 11 ++- bin/update-composer | 3 - composer.json | 12 ++- config/global.php | 4 +- phar-starter.php | 2 + src/Framework/DataProvider.php | 10 +-- src/Framework/FrameworkLoader.php | 12 ++- src/Framework/global_functions.php | 4 +- src/Module/Example/Hello.php | 1 + src/ZM/DB/SelectBody.php | 10 +++ src/ZM/Event/Swoole/RequestEvent.php | 1 + src/ZM/Event/Swoole/WorkerStartEvent.php | 4 +- src/ZM/Utils/ZMRequest.php | 34 ++++++-- src/ZM/Utils/ZMWebSocket.php | 105 +++++++++++++++++++++++ 17 files changed, 202 insertions(+), 50 deletions(-) delete mode 100644 bin/update-composer create mode 100644 src/ZM/Utils/ZMWebSocket.php diff --git a/README.md b/README.md index 346b6628..5f4c06a3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![作者QQ](https://img.shields.io/badge/作者QQ-627577391-orange.svg)]() [![zhamao License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/zhamao-robot/zhamao-framework/blob/master/LICENSE) -[![版本](https://img.shields.io/badge/version-1.4-green.svg)]() +[![版本](https://img.shields.io/badge/version-1.5-green.svg)]() [![stupid counter](https://img.shields.io/github/search/zhamao-robot/zhamao-framework/stupid.svg)](https://github.com/zhamao-robot/zhamao-framework/search?q=stupid) [![TODO counter](https://img.shields.io/github/search/zhamao-robot/zhamao-framework/TODO.svg)](https://github.com/zhamao-robot/zhamao-framework/search?q=TODO) @@ -19,8 +19,9 @@ zhamao-framework 是一个基于 酷Q 的 PHP Swoole 的机器人框架,它会 除了起到解析消息的作用,炸毛框架 还提供了完整的 WebSocket + HTTP 服务器,你还能用此框架构建出高性能的 API 接口服务器。 ## 开始 -1. 你可以使用项目的 `Use this template` 功能将框架克隆到你的公开或私有仓库进行开发 -2. 你也可以直接到 **Release** 中下载最新的 phar 包,放入文件夹后快速启动框架 +先安装环境,环境安装见下方文档。 +1. `composer create-project zhamao/framework-starter` 从模板新建基础文档结构进行使用 +2. 你也可以直接到 **Release** 中下载最新的 phar 包,放入文件夹后 `php server.phar` 快速启动框架 3. 还可以使用 Dockerfile 构建 Docker 容器 ## 文档 diff --git a/bin/phar-build b/bin/phar-build index 30f9ddaa..9a5e437c 100755 --- a/bin/phar-build +++ b/bin/phar-build @@ -2,14 +2,10 @@ 30000, ]); +global $vendor_mode; +$vendor_mode = false; +if (mb_strpos(__DIR__, getcwd()) !== false && substr(str_replace(getcwd(), "", __DIR__), 0, 8) == "/vendor/") { + define("LOAD_MODE", 1); //composer项目模式 + define("LOAD_MODE_COMPOSER_PATH", getcwd()); +} else { + define("LOAD_MODE", 0); //正常模式 +} date_default_timezone_set("Asia/Shanghai"); @@ -24,13 +32,21 @@ switch ($argv[1] ?? '') { } }); break; + case 'phar-build': + array_shift($argv); + require_once 'phar-build'; + break; + case 'systemd': + array_shift($argv); + require_once 'systemd'; + break; case '': case 'framework': case 'server': - if(!is_dir(__DIR__.'/../vendor/')){ + if (!is_dir(__DIR__ . '/../vendor/') && LOAD_MODE == 0) { echo "Warning: you have not update composer!\n"; exec("composer update", $out, $var); - if($var != 0) { + if ($var != 0) { echo "You need to run \"composer update\" at root of zhamao-framework!\n"; die; } @@ -39,10 +55,12 @@ switch ($argv[1] ?? '') { break; case '--help': case '-h': - echo "\nUsage: ".$argv[0]." [OPTION]\n"; + 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\n\n"; + echo "\n framework, server\tstart main framework, this is default option"; + echo "\n phar-build\t\tbuild a new phar archive"; + 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"; diff --git a/bin/systemd b/bin/systemd index acdecac0..ed75b300 100644 --- a/bin/systemd +++ b/bin/systemd @@ -2,7 +2,8 @@ =7.2", "swoole/ide-helper": "@dev", @@ -25,11 +29,5 @@ "ext-ctype": "*", "ext-pdo": "*", "psy/psysh": "@stable" - }, - "repositories": { - "packagist": { - "type": "composer", - "url": "https://mirrors.aliyun.com/composer/" - } } } diff --git a/config/global.php b/config/global.php index 7fe76507..4d367baf 100644 --- a/config/global.php +++ b/config/global.php @@ -14,7 +14,7 @@ $config['http_reverse_link'] = "http://127.0.0.1:".$config['port']; $config['debug_mode'] = false; /** 存放框架内文件数据的目录 */ -$config['zm_data'] = WORKING_DIR.'/zm_data/'; +$config['zm_data'] = realpath(__DIR__ . "/../").'/zm_data/'; /** 存放各个模块配置文件的目录 */ $config['config_dir'] = $config['zm_data'].'config/'; @@ -73,7 +73,7 @@ $config['context_class'] = \ZM\Context\Context::class; /** 静态文件访问 */ $config['static_file_server'] = [ 'status' => false, - 'document_root' => WORKING_DIR . '/resources/html', + 'document_root' => realpath(__DIR__ . "/../") . '/resources/html', 'document_index' => [ 'index.html' ] diff --git a/phar-starter.php b/phar-starter.php index f019dd7e..ba2fc9a0 100644 --- a/phar-starter.php +++ b/phar-starter.php @@ -30,6 +30,8 @@ Swoole\Coroutine::set([ date_default_timezone_set("Asia/Shanghai"); define('WORKING_DIR', __DIR__); +define('FRAMEWORK_DIR', __DIR__); +define('LOAD_MODE', 2); $s = new FrameworkLoader($argv); diff --git a/src/Framework/DataProvider.php b/src/Framework/DataProvider.php index 2fa99c8d..13de875e 100644 --- a/src/Framework/DataProvider.php +++ b/src/Framework/DataProvider.php @@ -15,12 +15,10 @@ class DataProvider } public static function getWorkingDir() { - global $is_phar; - if ($is_phar === true) { - return realpath('.'); - } else { - return WORKING_DIR; - } + if(LOAD_MODE == 0) return WORKING_DIR; + elseif (LOAD_MODE == 1) return LOAD_MODE_COMPOSER_PATH; + elseif (LOAD_MODE == 2) return realpath('.'); + return null; } public static function getDataConfig() { diff --git a/src/Framework/FrameworkLoader.php b/src/Framework/FrameworkLoader.php index db06a085..344a810d 100644 --- a/src/Framework/FrameworkLoader.php +++ b/src/Framework/FrameworkLoader.php @@ -42,11 +42,9 @@ class FrameworkLoader $this->requireGlobalFunctions(); - if (!isPharMode()) { - define('WORKING_DIR', getcwd()); - } else { - echo "Phar mode: " . WORKING_DIR . PHP_EOL; - } + 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'); self::$settings = new GlobalConfig(); if (self::$settings->get("debug_mode") === true) { @@ -66,7 +64,6 @@ class FrameworkLoader $this->selfCheck(); try { $this->server = new Server(self::$settings->get("host"), self::$settings->get("port")); - if (in_array("--remote-shell", $args)) RemoteShell::listen($this->server, "127.0.0.1"); $settings = self::$settings->get("swoole"); if (in_array("--daemon", $args)) { $settings["daemonize"] = 1; @@ -93,6 +90,7 @@ class FrameworkLoader EventHandler::callSwooleEvent("close", $server, $fd); }); 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); @@ -103,7 +101,7 @@ class FrameworkLoader ", port: " . self::$settings->get("port") . ", log_level: " . ZMBuf::$atomics["info_level"]->get() . ", version: " . json_decode(file_get_contents(WORKING_DIR . "/composer.json"), true)["version"] . - "\nworking_dir: " . (isPharMode() ? realpath('.') : WORKING_DIR) + "\nworking_dir: " . DataProvider::getWorkingDir() ); global $motd; if (!file_exists(DataProvider::getWorkingDir() . "/config/motd.txt")) { diff --git a/src/Framework/global_functions.php b/src/Framework/global_functions.php index e2989115..ff86fe22 100644 --- a/src/Framework/global_functions.php +++ b/src/Framework/global_functions.php @@ -7,9 +7,6 @@ use Swoole\Coroutine\System; use ZM\Context\ContextInterface; use ZM\Utils\ZMUtil; -function isPharMode() { - return substr(__DIR__, 0, 7) == 'phar://'; -} function classLoader($p) { $filepath = getClassPath($p); @@ -72,6 +69,7 @@ function unicode_decode($str) { * @return array */ function getAllClasses($dir, $indoor_name) { + if(!is_dir($dir)) return []; $list = scandir($dir); $classes = []; unset($list[0], $list[1]); diff --git a/src/Module/Example/Hello.php b/src/Module/Example/Hello.php index 9a94d417..a5a76656 100644 --- a/src/Module/Example/Hello.php +++ b/src/Module/Example/Hello.php @@ -67,6 +67,7 @@ class Hello extends ModBase * @Middleware("timer") */ public function timer() { + eval(ZM_BREAKPOINT); return "This page is used as testing TimerMiddleware! Do not use it in production."; } diff --git a/src/ZM/DB/SelectBody.php b/src/ZM/DB/SelectBody.php index c4164603..553894c2 100644 --- a/src/ZM/DB/SelectBody.php +++ b/src/ZM/DB/SelectBody.php @@ -30,6 +30,16 @@ class SelectBody */ public function get() { return $this->fetchAll(); } + /** + * @throws DbException + */ + public function count() { + $this->select_thing = ["count(*)"]; + $str = $this->queryPrepare(); + $this->result = DB::rawQuery($str[0], $str[1]); + return intval($this->result[0]["count(*)"]); + } + /** * @return null * @throws DbException diff --git a/src/ZM/Event/Swoole/RequestEvent.php b/src/ZM/Event/Swoole/RequestEvent.php index 64e50b6f..e4424c1e 100644 --- a/src/ZM/Event/Swoole/RequestEvent.php +++ b/src/ZM/Event/Swoole/RequestEvent.php @@ -42,6 +42,7 @@ class RequestEvent implements SwooleEvent $this->response->setHeader($k, $v); } $uri = $this->request->server["request_uri"]; + Console::verbose($this->request->server["remote_addr"]." request ".$uri); $uri = explode("/", $uri); $uri = array_diff($uri, ["..", "", "."]); $node = ZMBuf::$req_mapping; diff --git a/src/ZM/Event/Swoole/WorkerStartEvent.php b/src/ZM/Event/Swoole/WorkerStartEvent.php index 24c7f519..a996c356 100644 --- a/src/ZM/Event/Swoole/WorkerStartEvent.php +++ b/src/ZM/Event/Swoole/WorkerStartEvent.php @@ -7,6 +7,7 @@ namespace ZM\Event\Swoole; use Co; use Doctrine\Common\Annotations\AnnotationException; use Exception; +use PDO; use ReflectionException; use Swoole\Coroutine; use Swoole\Database\PDOConfig; @@ -101,6 +102,7 @@ class WorkerStartEvent implements SwooleEvent ->withCharset('utf8mb4') ->withUsername($sql["sql_username"]) ->withPassword($sql["sql_password"]) + ->withOptions([PDO::ATTR_STRINGIFY_FETCHES => false]) ); DB::initTableList(); } @@ -180,7 +182,7 @@ class WorkerStartEvent implements SwooleEvent if (file_exists(DataProvider::getWorkingDir() . "/vendor/autoload.php")) { require_once DataProvider::getWorkingDir() . "/vendor/autoload.php"; } - if (isPharMode()) require_once WORKING_DIR . "/vendor/autoload.php"; + if (LOAD_MODE == 2) require_once FRAMEWORK_DIR . "/vendor/autoload.php"; //加载各个模块的注解类,以及反射 Console::info("检索Module中"); diff --git a/src/ZM/Utils/ZMRequest.php b/src/ZM/Utils/ZMRequest.php index 12bda77b..18b56d3d 100644 --- a/src/ZM/Utils/ZMRequest.php +++ b/src/ZM/Utils/ZMRequest.php @@ -4,6 +4,7 @@ namespace ZM\Utils; +use Framework\Console; use Swlib\Saber; use Swoole\Coroutine\Http\Client; @@ -11,18 +12,23 @@ class ZMRequest { /** * 使用Swoole协程客户端发起HTTP GET请求 - * @version 1.1 - * 返回请求后的body - * 如果请求失败或返回状态不是200,则返回 false * @param $url * @param array $headers * @param array $set * @param bool $return_body * @return bool|string|Client + * @version 1.1 + * 返回请求后的body + * 如果请求失败或返回状态不是200,则返回 false */ public static function get($url, $headers = [], $set = [], $return_body = true) { $parse = parse_url($url); - $cli = new Client($parse["host"], ($parse["scheme"] == "https" ? 443 : (isset($parse["port"]) ? $parse["port"] : 80)), ($parse["scheme"] == "https" ? true : false)); + if (!isset($parse["host"])) { + Console::warning("ZMRequest: url must contains scheme such as \"http(s)\""); + return false; + } + $port = $parse["port"] ?? (($parse["scheme"] ?? "http") == "https" ? 443 : 80); + $cli = new Client($parse["host"], $port, (($parse["scheme"] ?? "http") == "https" ? true : false)); $cli->setHeaders($headers); $cli->set($set == [] ? ['timeout' => 15.0] : $set); $cli->get($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : "")); @@ -50,7 +56,12 @@ class ZMRequest */ public static function post($url, array $header, $data, $set = [], $return_body = true) { $parse = parse_url($url); - $cli = new Client($parse["host"], ($parse["scheme"] == "https" ? 443 : (isset($parse["port"]) ? $parse["port"] : 80)), ($parse["scheme"] == "https" ? true : false)); + if (!isset($parse["host"])) { + Console::warning("ZMRequest: url must contains scheme such as \"http(s)://\""); + return false; + } + $port = $parse["port"] ?? (($parse["scheme"] ?? "http") == "https" ? 443 : 80); + $cli = new Client($parse["host"], $port, (($parse["scheme"] ?? "http") == "https" ? true : false)); $cli->set($set == [] ? ['timeout' => 15.0] : $set); $cli->setHeaders($header); $cli->post($parse["path"] . (isset($parse["query"]) ? ("?" . $parse["query"]) : ""), $data); @@ -65,6 +76,17 @@ class ZMRequest } } + /** + * @param $url + * @param array $set + * @param array $header + * @return ZMWebSocket + * @since 1.5 + */ + public static function websocket($url, $set = ['websocket_mask' => true], $header = []) { + return new ZMWebSocket($url, $set, $header); + } + /** * @param $option * @return Saber @@ -72,4 +94,4 @@ class ZMRequest public static function session($option) { return Saber::session($option); } -} \ No newline at end of file +} diff --git a/src/ZM/Utils/ZMWebSocket.php b/src/ZM/Utils/ZMWebSocket.php new file mode 100644 index 00000000..1d96fc7a --- /dev/null +++ b/src/ZM/Utils/ZMWebSocket.php @@ -0,0 +1,105 @@ + true], $header = []) { + $this->parse = parse_url($url); + if (!isset($this->parse["host"])) { + Console::warning("ZMRequest: url must contains scheme such as \"ws(s)://\""); + return; + } + $port = $this->parse["port"] ?? (($this->parse["scheme"] ?? "ws") == "wss" ? 443 : 80); + $this->client = new Client($this->parse["host"], $port, (($this->parse["scheme"] ?? "ws") == "wss" ? true : false)); + $this->client->set($set); + if ($header != []) $this->client->setHeaders($header); + $this->is_available = true; + } + + /** + * @return bool + */ + public function upgrade() { + if (!$this->is_available) return false; + $r = $this->client->upgrade($this->parse["path"] . (isset($this->parse["query"]) ? ("?" . $this->parse["query"]) : "")); + if ($r) { + go(function () { + while (true) { + $result = $this->client->recv(60); + if ($result === false) { + if ($this->client->connected === false) { + go(function () { + call_user_func($this->close_func, $this->client); + }); + break; + } + } elseif ($result instanceof Frame) { + go(function () use ($result) { + $this->is_available = false; + call_user_func($this->message_func, $result, $this->client); + }); + } + } + }); + return true; + } + return false; + } + + /** + * @param callable $callable + * @return $this + */ + public function onMessage(callable $callable) { + $this->message_func = $callable; + return $this; + } + + /** + * @param callable $callable + * @return $this + */ + public function onClose(callable $callable) { + $this->close_func = $callable; + return $this; + } +} + +if(!debug_backtrace()) { + go(function () { + require_once __DIR__ . "/../../Framework/Console.php"; + $cli = new ZMWebSocket("ws://127.0.0.1:20001/"); + if (!$cli->is_available) die("Error!\n"); + $cli->onMessage(function ($frame){ + var_dump($frame); + }); + $cli->onClose(function($client){ + echo "Connection closed.\n"; + }); + if($cli->upgrade()){ + echo "成功连接!\n"; + } else { + echo "连接失败!\n"; + } + }); +}