mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-21 23:55:35 +08:00
initial 2.0.0-a2 commit
This commit is contained in:
51
src/ZM/Utils/CoroutinePool.php
Normal file
51
src/ZM/Utils/CoroutinePool.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
use Swoole\Coroutine;
|
||||
|
||||
class CoroutinePool
|
||||
{
|
||||
private static $cids = [];
|
||||
|
||||
private static $default_size = 30;
|
||||
|
||||
private static $sizes = [];
|
||||
|
||||
private static $yields = [];
|
||||
|
||||
public static function go(callable $func, $name = "default") {
|
||||
if (!isset(self::$cids[$name])) self::$cids[$name] = [];
|
||||
if (count(self::$cids[$name]) >= (self::$sizes[$name] ?? self::$default_size)) {
|
||||
self::$yields[] = Coroutine::getCid();
|
||||
Coroutine::suspend();
|
||||
}
|
||||
go(function () use ($func, $name) {
|
||||
self::$cids[$name][] = Coroutine::getCid();
|
||||
//Console::debug("正在执行协程,当前协程池中有 " . count(self::$cids[$name]) . " 个正在运行的协程: ".implode(", ", self::$cids[$name]));
|
||||
$func();
|
||||
self::checkCids($name);
|
||||
});
|
||||
}
|
||||
|
||||
public static function defaultSize(int $size) {
|
||||
self::$default_size = $size;
|
||||
}
|
||||
|
||||
public static function setSize($name, int $size) {
|
||||
self::$sizes[$name] = $size;
|
||||
}
|
||||
|
||||
private static function checkCids($name) {
|
||||
if (in_array(Coroutine::getCid(), self::$cids[$name])) {
|
||||
$a = array_search(Coroutine::getCid(), self::$cids[$name]);
|
||||
array_splice(self::$cids[$name], $a, 1);
|
||||
$r = array_shift(self::$yields);
|
||||
if ($r !== null) {
|
||||
Coroutine::resume($r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
src/ZM/Utils/HttpUtil.php
Normal file
117
src/ZM/Utils/HttpUtil.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
use Co;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\EventManager;
|
||||
use ZM\Http\Response;
|
||||
|
||||
class HttpUtil
|
||||
{
|
||||
public static function parseUri($request, $response, $uri, &$node, &$params) {
|
||||
$uri = explode("/", $uri);
|
||||
$uri = array_diff($uri, ["..", "", "."]);
|
||||
$node = EventManager::$req_mapping;
|
||||
$params = [];
|
||||
while (true) {
|
||||
$r = array_shift($uri);
|
||||
if ($r === null) break;
|
||||
if (($cnt = count($node["son"] ?? [])) == 1) {
|
||||
if (isset($node["param_route"])) {
|
||||
foreach ($node["son"] as $k => $v) {
|
||||
if ($v["id"] == $node["param_route"]) {
|
||||
$node = $v;
|
||||
$params[mb_substr($v["name"], 1, -1)] = $r;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
} elseif ($node["son"][0]["name"] == $r) {
|
||||
$node = $node["son"][0];
|
||||
continue;
|
||||
}
|
||||
} elseif ($cnt >= 1) {
|
||||
if (isset($node["param_route"])) {
|
||||
foreach ($node["son"] as $k => $v) {
|
||||
if ($v["id"] == $node["param_route"]) {
|
||||
$node = $v;
|
||||
$params[mb_substr($v["name"], 1, -1)] = $r;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($node["son"] as $k => $v) {
|
||||
if ($v["name"] == $r) {
|
||||
$node = $v;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ZMConfig::get("global", "static_file_server")["status"]) {
|
||||
HttpUtil::handleStaticPage($request->server["request_uri"], $response);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function getHttpCodePage(int $http_code) {
|
||||
if (isset(ZMConfig::get("global", "http_default_code_page")[$http_code])) {
|
||||
return Co::readFile(DataProvider::getResourceFolder() . "html/" . ZMConfig::get("global", "http_default_code_page")[$http_code]);
|
||||
} else return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $uri
|
||||
* @param Response|\Swoole\Http\Response $response
|
||||
* @param array $settings
|
||||
* @return bool
|
||||
*/
|
||||
public static function handleStaticPage($uri, $response, $settings = []) {
|
||||
$base_dir = $settings["document_root"] ?? ZMConfig::get("global", "static_file_server")["document_root"];
|
||||
$base_index = $settings["document_index"] ?? ZMConfig::get("global", "static_file_server")["document_index"];
|
||||
$path = realpath($base_dir . urldecode($uri));
|
||||
if ($path !== false) {
|
||||
if (is_dir($path)) $path = $path . '/';
|
||||
$work = realpath($base_dir) . '/';
|
||||
if (strpos($path, $work) !== 0) {
|
||||
Console::info("[403] " . $uri);
|
||||
self::responseCodePage($response, 403);
|
||||
return true;
|
||||
}
|
||||
if (is_dir($path)) {
|
||||
if(mb_substr($uri, -1, 1) != "/") {
|
||||
Console::info("[302] " . $uri);
|
||||
$response->redirect($uri."/", 302);
|
||||
return true;
|
||||
}
|
||||
foreach ($base_index as $vp) {
|
||||
if (is_file($path . "/" . $vp)) {
|
||||
Console::info("[200] " . $uri);
|
||||
$exp = strtolower(pathinfo($path . $vp)['extension'] ?? "unknown");
|
||||
$response->setHeader("Content-Type", ZMConfig::get("file_header")[$exp] ?? "application/octet-stream");
|
||||
$response->end(file_get_contents($path . $vp));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif (is_file($path)) {
|
||||
Console::info("[200] " . $uri);
|
||||
$exp = strtolower(pathinfo($path)['extension'] ?? "unknown");
|
||||
$response->setHeader("Content-Type", ZMConfig::get("file_header")[$exp] ?? "application/octet-stream");
|
||||
$response->end(file_get_contents($path));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Console::info("[404] " . $uri);
|
||||
self::responseCodePage($response, 404);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function responseCodePage($response, $code) {
|
||||
$response->status($code);
|
||||
$response->end(self::getHttpCodePage($code));
|
||||
}
|
||||
}
|
||||
@@ -5,74 +5,18 @@ namespace ZM\Utils;
|
||||
|
||||
|
||||
use Exception;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use Swoole\Event;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Store\ZMBuf;
|
||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||
use ZM\Framework;
|
||||
|
||||
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
|
||||
* @param $resource
|
||||
* @return bool
|
||||
*/
|
||||
private static function executeCommand(string $cmd) {
|
||||
public static function executeCommand(string $cmd, $resource) {
|
||||
$it = explodeMsg($cmd);
|
||||
switch ($it[0] ?? '') {
|
||||
case 'logtest':
|
||||
@@ -88,7 +32,13 @@ class Terminal
|
||||
$class_name = $it[1];
|
||||
$function_name = $it[2];
|
||||
$class = new $class_name([]);
|
||||
call_user_func_array([$class, $function_name], []);
|
||||
$class->$function_name();
|
||||
return true;
|
||||
case 'psysh':
|
||||
if (Framework::$argv["disable-coroutine"])
|
||||
eval(\Psy\sh());
|
||||
else
|
||||
Console::error("Only \"--disable-coroutine\" mode can use psysh!!!");
|
||||
return true;
|
||||
case 'bc':
|
||||
$code = base64_decode($it[1] ?? '', true);
|
||||
@@ -104,18 +54,13 @@ class Terminal
|
||||
Console::log($it[2], $it[1]);
|
||||
return true;
|
||||
case 'stop':
|
||||
Event::del($resource);
|
||||
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:
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
use ZM\Console\Console;
|
||||
use Swoole\Coroutine\Http\Client;
|
||||
|
||||
class ZMRequest
|
||||
{
|
||||
/**
|
||||
* 使用Swoole协程客户端发起HTTP GET请求
|
||||
* @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) {
|
||||
/** @var Client $cli */
|
||||
list($cli, $parse) = self::getNewClient($url);
|
||||
if($cli === null) return false;
|
||||
$cli->set($set == [] ? ['timeout' => 15.0] : $set);
|
||||
$cli->setHeaders($headers);
|
||||
$cli->get($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""));
|
||||
if ($return_body) {
|
||||
if ($cli->errCode != 0 || $cli->statusCode != 200) return false;
|
||||
$a = $cli->body;
|
||||
$cli->close();
|
||||
return $a;
|
||||
} else {
|
||||
$cli->close();
|
||||
return $cli;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Swoole协程客户端发起HTTP POST请求
|
||||
* 返回请求后的body
|
||||
* 如果请求失败或返回状态不是200,则返回 false
|
||||
* @param $url
|
||||
* @param array $header
|
||||
* @param $data
|
||||
* @param array $set
|
||||
* @param bool $return_body
|
||||
* @return bool|string|Client
|
||||
*/
|
||||
public static function post($url, array $header, $data, $set = [], $return_body = true) {
|
||||
/** @var Client $cli */
|
||||
list($cli, $parse) = self::getNewClient($url);
|
||||
if($cli === null) return false;
|
||||
$cli->set($set == [] ? ['timeout' => 15.0] : $set);
|
||||
$cli->setHeaders($header);
|
||||
$cli->post($parse["path"] . (isset($parse["query"]) ? ("?" . $parse["query"]) : ""), $data);
|
||||
if ($return_body) {
|
||||
if ($cli->errCode != 0 || $cli->statusCode != 200) return false;
|
||||
$a = $cli->body;
|
||||
$cli->close();
|
||||
return $a;
|
||||
} else {
|
||||
$cli->close();
|
||||
return $cli;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 $url
|
||||
* @param array $attribute
|
||||
* @param bool $return_body
|
||||
* @return bool|string|Client
|
||||
*/
|
||||
public static function request($url, $attribute = [], $return_body = true) {
|
||||
/** @var Client $cli */
|
||||
list($cli, $parse) = self::getNewClient($url);
|
||||
if($cli === null) return false;
|
||||
$cli->set($attribute["set"] ?? ["timeout" => 15.0]);
|
||||
$cli->setMethod($attribute["method"] ?? "GET");
|
||||
$cli->setHeaders($attribute["headers"] ?? []);
|
||||
if(isset($attribute["data"])) $cli->setData($attribute["data"]);
|
||||
if(isset($attribute["file"])) {
|
||||
foreach($attribute["file"] as $k => $v) {
|
||||
$cli->addFile($v["path"], $v["name"], $v["mime_type"] ?? null, $v["filename"] ?? null, $v["offset"] ?? 0, $v["length"] ?? 0);
|
||||
}
|
||||
}
|
||||
$cli->execute($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""));
|
||||
if ($return_body) {
|
||||
if ($cli->errCode != 0 || $cli->statusCode != 200) return false;
|
||||
$a = $cli->body;
|
||||
$cli->close();
|
||||
return $a;
|
||||
} else {
|
||||
$cli->close();
|
||||
return $cli;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @param null|bool $dst
|
||||
* @return bool
|
||||
*/
|
||||
public static function downloadFile($url, $dst = null) {
|
||||
/** @var Client $cli */
|
||||
list($cli, $parse) = self::getNewClient($url);
|
||||
if($cli === null) return false;
|
||||
$cli->set(["timeout" => 60.0]);
|
||||
$save_path = $dst === null ? "/tmp/_zm_".mt_rand(1000000, 9999999) : $dst;
|
||||
$result = $cli->download($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""), $save_path);
|
||||
if($result === false) return false;
|
||||
elseif ($dst === null) return $save_path;
|
||||
else return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @return bool|array
|
||||
*/
|
||||
private static function getNewClient($url) {
|
||||
$parse = parse_url($url);
|
||||
if (!isset($parse["host"])) {
|
||||
Console::warning("ZMRequest: url must contains scheme such as \"http(s)://\"");
|
||||
return false;
|
||||
}
|
||||
if(!isset($parse["path"])) $parse["path"] = "/";
|
||||
$port = $parse["port"] ?? (($parse["scheme"] ?? "http") == "https" ? 443 : 80);
|
||||
$cli = new Client($parse["host"], $port, ($parse["scheme"] ?? "http") == "https");
|
||||
return [$cli, $parse];
|
||||
}
|
||||
}
|
||||
@@ -5,55 +5,45 @@ namespace ZM\Utils;
|
||||
|
||||
|
||||
use Co;
|
||||
use ZM\Config\ZMConfig;
|
||||
use Exception;
|
||||
use Swoole\Event;
|
||||
use Swoole\Timer;
|
||||
use ZM\Console\Console;
|
||||
use ZM\API\CQ;
|
||||
use ZM\Store\LightCache;
|
||||
use ZM\Store\ZMBuf;
|
||||
|
||||
class ZMUtil
|
||||
{
|
||||
/**
|
||||
* 检查workerStart是否运行结束
|
||||
*/
|
||||
public static function checkWait() {
|
||||
if (ZMBuf::isset("wait_start")) {
|
||||
ZMBuf::append("wait_start", Co::getCid());
|
||||
Co::suspend();
|
||||
public static function stop() {
|
||||
Console::warning(Console::setColor("Stopping server...", "red"));
|
||||
if (ZMBuf::$terminal !== null)
|
||||
Event::del(ZMBuf::$terminal);
|
||||
ZMBuf::atomic("stop_signal")->set(1);
|
||||
try {
|
||||
LightCache::set('stop', 'OK');
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
server()->shutdown();
|
||||
server()->stop();
|
||||
}
|
||||
|
||||
public static function stop($without_shutdown = false) {
|
||||
Console::info(Console::setColor("Stopping server...", "red"));
|
||||
foreach ((ZMBuf::$server->connections ?? []) as $v) {
|
||||
ZMBuf::$server->close($v);
|
||||
}
|
||||
DataProvider::saveBuffer();
|
||||
if (!$without_shutdown)
|
||||
ZMBuf::$server->shutdown();
|
||||
ZMBuf::$server->stop();
|
||||
}
|
||||
|
||||
public static function getHttpCodePage(int $http_code) {
|
||||
if (isset(ZMConfig::get("global", "http_default_code_page")[$http_code])) {
|
||||
return Co::readFile(DataProvider::getResourceFolder() . "html/" . ZMConfig::get("global", "http_default_code_page")[$http_code]);
|
||||
} else return null;
|
||||
}
|
||||
|
||||
public static function reload() {
|
||||
public static function reload($delay = 800) {
|
||||
Console::info(Console::setColor("Reloading server...", "gold"));
|
||||
usleep($delay * 1000);
|
||||
foreach (ZMBuf::get("wait_api", []) as $k => $v) {
|
||||
if ($v["result"] === null) Co::resume($v["coroutine"]);
|
||||
}
|
||||
foreach (ZMBuf::$server->connections as $v) {
|
||||
ZMBuf::$server->close($v);
|
||||
foreach (server()->connections as $v) {
|
||||
server()->close($v);
|
||||
}
|
||||
DataProvider::saveBuffer();
|
||||
ZMBuf::$server->reload();
|
||||
Timer::clearAll();
|
||||
server()->reload();
|
||||
}
|
||||
|
||||
public static function getModInstance($class) {
|
||||
if (!isset(ZMBuf::$instance[$class])) {
|
||||
Console::debug("Class instance $class not exist, so I created it.");
|
||||
//Console::debug("Class instance $class not exist, so I created it.");
|
||||
return ZMBuf::$instance[$class] = new $class();
|
||||
} else {
|
||||
return ZMBuf::$instance[$class];
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
use ZM\Console\Console;
|
||||
use Swoole\Coroutine\Http\Client;
|
||||
use Swoole\WebSocket\Frame;
|
||||
|
||||
/**
|
||||
* Class ZMWebSocket
|
||||
* @package ZM\Utils
|
||||
* @since 1.5
|
||||
*/
|
||||
class ZMWebSocket
|
||||
{
|
||||
private $parse;
|
||||
private $client;
|
||||
|
||||
public $is_available = false;
|
||||
|
||||
private $close_func;
|
||||
private $message_func;
|
||||
|
||||
public function __construct($url, $set = ['websocket_mask' => true], $header = []) {
|
||||
$this->parse = parse_url($url);
|
||||
if (!isset($this->parse["host"])) {
|
||||
Console::warning("ZMRequest: url must contains scheme such as \"ws(s)://\"");
|
||||
return;
|
||||
}
|
||||
if (!isset($this->parse["path"])) $this->parse["path"] = "/";
|
||||
$port = $this->parse["port"] ?? (($this->parse["scheme"] ?? "ws") == "wss" ? 443 : 80);
|
||||
$this->client = new Client($this->parse["host"], $port, (($this->parse["scheme"] ?? "ws") == "wss" ? true : false));
|
||||
$this->client->set($set);
|
||||
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 $frame) {
|
||||
var_dump($frame);
|
||||
});
|
||||
$cli->onClose(function () {
|
||||
echo "Connection closed.\n";
|
||||
});
|
||||
if ($cli->upgrade()) {
|
||||
echo "成功连接!\n";
|
||||
} else {
|
||||
echo "连接失败!\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user