正在显示
19 个修改的文件
包含
772 行增加
和
347 行删除
| 1 | <?php | 1 | <?php |
| 2 | - | ||
| 3 | -namespace jiaoyin; | 2 | +namespace Jiaoyin; |
| 4 | 3 | ||
| 5 | /* | 4 | /* |
| 6 | binance合约接口 | 5 | binance合约接口 |
| 7 | */ | 6 | */ |
| 8 | - | ||
| 9 | class BinanceFutures | 7 | class BinanceFutures |
| 10 | { | 8 | { |
| 11 | static private string $host = 'https://fapi.binance.com'; | 9 | static private string $host = 'https://fapi.binance.com'; |
| 12 | private string $apikey = ''; | 10 | private string $apikey = ''; |
| 13 | private string $secret = ''; | 11 | private string $secret = ''; |
| 14 | 12 | ||
| 15 | - public function __construct($apikey = '', $secret = '', $host = '') | 13 | + public function __construct($apikey='', $secret='',$host='') |
| 16 | { | 14 | { |
| 17 | $this->apikey = $apikey; | 15 | $this->apikey = $apikey; |
| 18 | $this->secret = $secret; | 16 | $this->secret = $secret; |
| 19 | - if (!empty($host)) { | 17 | + if(!empty($host)){ |
| 20 | self::$host = $host; | 18 | self::$host = $host; |
| 21 | } | 19 | } |
| 22 | } | 20 | } |
| @@ -44,9 +42,9 @@ class BinanceFutures | @@ -44,9 +42,9 @@ class BinanceFutures | ||
| 44 | 42 | ||
| 45 | public function requestAccount($method, $path, $param) | 43 | public function requestAccount($method, $path, $param) |
| 46 | { | 44 | { |
| 47 | - if (empty($this->apikey) || empty($this->secret)) { | 45 | + if(empty($this->apikey) || empty($this->secret)){ |
| 48 | output('api 或 secret 为空'); | 46 | output('api 或 secret 为空'); |
| 49 | - return ['code' => -1, 'msg' => 'apikey or secret is empty']; | 47 | + return ['code'=>-1,'msg'=>'apikey or secret is empty']; |
| 50 | } | 48 | } |
| 51 | $url = $this->createUrl($path); | 49 | $url = $this->createUrl($path); |
| 52 | $param['timestamp'] = getMicrotime(); | 50 | $param['timestamp'] = getMicrotime(); |
| @@ -248,3 +246,5 @@ class BinanceFutures | @@ -248,3 +246,5 @@ class BinanceFutures | ||
| 248 | return self::requestMarket($method, $path, $param); | 246 | return self::requestMarket($method, $path, $param); |
| 249 | } | 247 | } |
| 250 | } | 248 | } |
| 249 | + | ||
| 250 | +?> |
| 1 | <?php | 1 | <?php |
| 2 | - | ||
| 3 | -namespace jiaoyin; | 2 | +namespace Jiaoyin; |
| 4 | 3 | ||
| 5 | /* | 4 | /* |
| 6 | binance现货接口 | 5 | binance现货接口 |
| 7 | */ | 6 | */ |
| 8 | - | ||
| 9 | class BinanceSpot | 7 | class BinanceSpot |
| 10 | { | 8 | { |
| 11 | static private string $host = 'https://api.binance.com'; | 9 | static private string $host = 'https://api.binance.com'; |
| 12 | private string $apikey = ''; | 10 | private string $apikey = ''; |
| 13 | private string $secret = ''; | 11 | private string $secret = ''; |
| 14 | 12 | ||
| 15 | - public function __construct($apikey = '', $secret = '', $host = '') | 13 | + public function __construct($apikey='', $secret='',$host='') |
| 16 | { | 14 | { |
| 17 | $this->apikey = $apikey; | 15 | $this->apikey = $apikey; |
| 18 | $this->secret = $secret; | 16 | $this->secret = $secret; |
| 19 | - if (!empty($host)) { | 17 | + if(!empty($host)){ |
| 20 | self::$host = $host; | 18 | self::$host = $host; |
| 21 | } | 19 | } |
| 22 | } | 20 | } |
| @@ -35,7 +33,7 @@ class BinanceSpot | @@ -35,7 +33,7 @@ class BinanceSpot | ||
| 35 | { | 33 | { |
| 36 | $len = count($param); | 34 | $len = count($param); |
| 37 | if ($len == 0) { | 35 | if ($len == 0) { |
| 38 | - // output('param 为空,签名失败'); | 36 | +// output('param 为空,签名失败'); |
| 39 | return $param; | 37 | return $param; |
| 40 | } | 38 | } |
| 41 | $paramStr = http_build_query($param); | 39 | $paramStr = http_build_query($param); |
| @@ -67,7 +65,7 @@ class BinanceSpot | @@ -67,7 +65,7 @@ class BinanceSpot | ||
| 67 | private function requestListenKey($method, $path, $param) | 65 | private function requestListenKey($method, $path, $param) |
| 68 | { | 66 | { |
| 69 | $url = $this->createUrl($path); | 67 | $url = $this->createUrl($path); |
| 70 | - // $param['timestamp'] = getMicrotime(); | 68 | +// $param['timestamp'] = getMicrotime(); |
| 71 | $param['signature'] = $this->createSign($this->secret, $param); | 69 | $param['signature'] = $this->createSign($this->secret, $param); |
| 72 | $header = $this->createHeader($this->apikey); | 70 | $header = $this->createHeader($this->apikey); |
| 73 | if (!in_array(strtoupper($method), ['GET', 'POST', 'DELETE'])) { | 71 | if (!in_array(strtoupper($method), ['GET', 'POST', 'DELETE'])) { |
| @@ -114,14 +112,14 @@ class BinanceSpot | @@ -114,14 +112,14 @@ class BinanceSpot | ||
| 114 | * @param int $type (=1:行情等接口,不需要签名;=2:账户等接口需要签名) | 112 | * @param int $type (=1:行情等接口,不需要签名;=2:账户等接口需要签名) |
| 115 | * @return array|int|mixed | 113 | * @return array|int|mixed |
| 116 | */ | 114 | */ |
| 117 | - public function request(string $path, string $method, array $param, int $type = 1) | 115 | + public function request(string $path, string $method, array $param, int $type=1) |
| 118 | { | 116 | { |
| 119 | - if ($type == 2) { | 117 | + if($type==2){ |
| 120 | return self::requestAccount($method, $path, $param); | 118 | return self::requestAccount($method, $path, $param); |
| 121 | - } else if ($type == 1) { | 119 | + }else if($type==1){ |
| 122 | return self::requestMarket($method, $path, $param); | 120 | return self::requestMarket($method, $path, $param); |
| 123 | - } else { | ||
| 124 | - return ['code' => 1000, 'msg' => 'type参数错误']; | 121 | + }else{ |
| 122 | + return ['code'=>1000,'msg'=>'type参数错误']; | ||
| 125 | } | 123 | } |
| 126 | } | 124 | } |
| 127 | 125 | ||
| @@ -209,20 +207,25 @@ class BinanceSpot | @@ -209,20 +207,25 @@ class BinanceSpot | ||
| 209 | return $this->requestAccount($method, $path, $param); | 207 | return $this->requestAccount($method, $path, $param); |
| 210 | } | 208 | } |
| 211 | 209 | ||
| 212 | - public function cancelOrderById($param) | ||
| 213 | - { | 210 | + public function cancelOrderById($param){ |
| 214 | $path = '/api/v3/order'; | 211 | $path = '/api/v3/order'; |
| 215 | $method = 'DELETE'; | 212 | $method = 'DELETE'; |
| 216 | return $this->requestAccount($method, $path, $param); | 213 | return $this->requestAccount($method, $path, $param); |
| 217 | } | 214 | } |
| 218 | 215 | ||
| 219 | - public function openOrders($param) | ||
| 220 | - { | 216 | + public function openOrders($param){ |
| 221 | $path = '/api/v3/openOrders'; | 217 | $path = '/api/v3/openOrders'; |
| 222 | $method = 'GET'; | 218 | $method = 'GET'; |
| 223 | return $this->requestAccount($method, $path, $param); | 219 | return $this->requestAccount($method, $path, $param); |
| 224 | } | 220 | } |
| 225 | 221 | ||
| 222 | + //账户信息 | ||
| 223 | + public function account($param=[]){ | ||
| 224 | + $path = '/api/v3/account'; | ||
| 225 | + $method = 'GET'; | ||
| 226 | + return $this->requestAccount($method, $path, $param); | ||
| 227 | + } | ||
| 228 | + | ||
| 226 | /* | 229 | /* |
| 227 | * ================================== | 230 | * ================================== |
| 228 | * 以下杠杆账户接口 | 231 | * 以下杠杆账户接口 |
| @@ -236,29 +239,25 @@ class BinanceSpot | @@ -236,29 +239,25 @@ class BinanceSpot | ||
| 236 | return $this->requestListenKey($method, $path, $param); | 239 | return $this->requestListenKey($method, $path, $param); |
| 237 | } | 240 | } |
| 238 | //杠杆账户下单 | 241 | //杠杆账户下单 |
| 239 | - public function orderMargin($param) | ||
| 240 | - { | 242 | + public function orderMargin($param){ |
| 241 | $path = '/sapi/v1/margin/order'; | 243 | $path = '/sapi/v1/margin/order'; |
| 242 | $method = 'POST'; | 244 | $method = 'POST'; |
| 243 | return $this->requestAccount($method, $path, $param); | 245 | return $this->requestAccount($method, $path, $param); |
| 244 | } | 246 | } |
| 245 | //杠杆账户撤销订单 (TRADE) | 247 | //杠杆账户撤销订单 (TRADE) |
| 246 | - public function cancelOrderMargin($param) | ||
| 247 | - { | 248 | + public function cancelOrderMargin($param){ |
| 248 | $path = '/sapi/v1/margin/order'; | 249 | $path = '/sapi/v1/margin/order'; |
| 249 | $method = 'DELETE'; | 250 | $method = 'DELETE'; |
| 250 | return $this->requestAccount($method, $path, $param); | 251 | return $this->requestAccount($method, $path, $param); |
| 251 | } | 252 | } |
| 252 | //杠杆账户订单列表 | 253 | //杠杆账户订单列表 |
| 253 | - public function openOrdersMargin($param) | ||
| 254 | - { | 254 | + public function openOrdersMargin($param){ |
| 255 | $path = '/sapi/v1/margin/openOrders'; | 255 | $path = '/sapi/v1/margin/openOrders'; |
| 256 | $method = 'GET'; | 256 | $method = 'GET'; |
| 257 | return $this->requestAccount($method, $path, $param); | 257 | return $this->requestAccount($method, $path, $param); |
| 258 | } | 258 | } |
| 259 | //调整全仓最大杠杆 (USER_DATA) | 259 | //调整全仓最大杠杆 (USER_DATA) |
| 260 | - public function maxLeverageMargin($param) | ||
| 261 | - { | 260 | + public function maxLeverageMargin($param){ |
| 262 | $path = '/sapi/v1/margin/max-leverage'; | 261 | $path = '/sapi/v1/margin/max-leverage'; |
| 263 | $method = 'POST'; | 262 | $method = 'POST'; |
| 264 | return $this->requestAccount($method, $path, $param); | 263 | return $this->requestAccount($method, $path, $param); |
| @@ -269,12 +268,20 @@ class BinanceSpot | @@ -269,12 +268,20 @@ class BinanceSpot | ||
| 269 | * 以下公共接口,无限apikey | 268 | * 以下公共接口,无限apikey |
| 270 | * ================================== | 269 | * ================================== |
| 271 | */ | 270 | */ |
| 271 | + //市场最新价格 | ||
| 272 | + public function tickerPrice($param=[]){ | ||
| 273 | + $path ='/api/v3/ticker/price'; | ||
| 274 | + $method = 'GET'; | ||
| 275 | + return self::requestMarket($method,$path,$param); | ||
| 276 | + } | ||
| 272 | //24小时成交数据 | 277 | //24小时成交数据 |
| 273 | - // static public function ticker24hr($param = []) | ||
| 274 | - // { | ||
| 275 | - // $path = '/fapi/v1/ticker/24hr'; | ||
| 276 | - // $method = 'GET'; | ||
| 277 | - // return self::requestMarket($method, $path, $param); | ||
| 278 | - // } | 278 | +// static public function ticker24hr($param = []) |
| 279 | +// { | ||
| 280 | +// $path = '/fapi/v1/ticker/24hr'; | ||
| 281 | +// $method = 'GET'; | ||
| 282 | +// return self::requestMarket($method, $path, $param); | ||
| 283 | +// } | ||
| 279 | 284 | ||
| 280 | } | 285 | } |
| 286 | + | ||
| 287 | +?> |
jiaoyin/BinanceUnified.php
0 → 100644
| 1 | +<?php | ||
| 2 | +namespace Jiaoyin; | ||
| 3 | + | ||
| 4 | +/* | ||
| 5 | + binance现货接口 | ||
| 6 | +*/ | ||
| 7 | +class BinanceUnified | ||
| 8 | +{ | ||
| 9 | + static private string $host = 'https://papi.binance.com'; | ||
| 10 | + private string $apikey = ''; | ||
| 11 | + private string $secret = ''; | ||
| 12 | + | ||
| 13 | + public function __construct($apikey='', $secret='',$host='') | ||
| 14 | + { | ||
| 15 | + $this->apikey = $apikey; | ||
| 16 | + $this->secret = $secret; | ||
| 17 | + if(!empty($host)){ | ||
| 18 | + self::$host = $host; | ||
| 19 | + } | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + static private function createUrl($path): string | ||
| 23 | + { | ||
| 24 | + return self::$host . $path; | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + static private function createHeader($apikey): array | ||
| 28 | + { | ||
| 29 | + return ["X-MBX-APIKEY:" . $apikey]; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + static private function createSign($secret, $param) | ||
| 33 | + { | ||
| 34 | + $len = count($param); | ||
| 35 | + if ($len == 0) { | ||
| 36 | +// output('param 为空,签名失败'); | ||
| 37 | + return $param; | ||
| 38 | + } | ||
| 39 | + $paramStr = http_build_query($param); | ||
| 40 | + return hash_hmac('sha256', $paramStr, $secret); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + private function requestAccount($method, $path, $param=[]) | ||
| 44 | + { | ||
| 45 | + $url = $this->createUrl($path); | ||
| 46 | + $param['timestamp'] = getMicrotime(); | ||
| 47 | + $param['signature'] = $this->createSign($this->secret, $param); | ||
| 48 | + $header = $this->createHeader($this->apikey); | ||
| 49 | + if (!in_array(strtoupper($method), ['GET', 'POST', 'DELETE'])) { | ||
| 50 | + output('请求方法错误', $method, $path, $param); | ||
| 51 | + return 0; | ||
| 52 | + } | ||
| 53 | + $data = json_encode([]); | ||
| 54 | + if (strtoupper($method) == 'POST') { | ||
| 55 | + $data = Curl::httpPost($url, $param, $header); | ||
| 56 | + } | ||
| 57 | + if (strtoupper($method) == 'GET') { | ||
| 58 | + $data = Curl::httpGet($url, $param, $header); | ||
| 59 | + } | ||
| 60 | + if (strtoupper($method) == 'DELETE') { | ||
| 61 | + $data = Curl::httpDelete($url, $param, $header); | ||
| 62 | + } | ||
| 63 | + return json_decode($data, true); | ||
| 64 | + } | ||
| 65 | + private function requestListenKey($method, $path, $param) | ||
| 66 | + { | ||
| 67 | + $url = $this->createUrl($path); | ||
| 68 | +// $param['timestamp'] = getMicrotime(); | ||
| 69 | + $param['signature'] = $this->createSign($this->secret, $param); | ||
| 70 | + $header = $this->createHeader($this->apikey); | ||
| 71 | + if (!in_array(strtoupper($method), ['GET', 'POST', 'DELETE'])) { | ||
| 72 | + output('请求方法错误', $method, $path, $param); | ||
| 73 | + return 0; | ||
| 74 | + } | ||
| 75 | + $data = json_encode([]); | ||
| 76 | + if (strtoupper($method) == 'POST') { | ||
| 77 | + $data = Curl::httpPost($url, $param, $header); | ||
| 78 | + } | ||
| 79 | + if (strtoupper($method) == 'GET') { | ||
| 80 | + $data = Curl::httpGet($url, $param, $header); | ||
| 81 | + } | ||
| 82 | + if (strtoupper($method) == 'DELETE') { | ||
| 83 | + $data = Curl::httpDelete($url, $param, $header); | ||
| 84 | + } | ||
| 85 | + return json_decode($data, true); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + static private function requestMarket($method, $path, $param) | ||
| 89 | + { | ||
| 90 | + $url = self::createUrl($path); | ||
| 91 | + if (!in_array(strtoupper($method), ['GET', 'POST', 'DELETE'])) { | ||
| 92 | + output('请求方法错误', $method, $path, $param); | ||
| 93 | + return 0; | ||
| 94 | + } | ||
| 95 | + $data = json_encode([]); | ||
| 96 | + if (strtoupper($method) == 'POST') { | ||
| 97 | + $data = Curl::httpPost($url, $param); | ||
| 98 | + } | ||
| 99 | + if (strtoupper($method) == 'GET') { | ||
| 100 | + $data = Curl::httpGet($url, $param); | ||
| 101 | + } | ||
| 102 | + if (strtoupper($method) == 'DELETE') { | ||
| 103 | + $data = Curl::httpDelete($url, $param); | ||
| 104 | + } | ||
| 105 | + return json_decode($data, true); | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + /** | ||
| 109 | + * @param string $path (binance api path) | ||
| 110 | + * @param string $method (GET POST DELETE) | ||
| 111 | + * @param array $param (binance api param) | ||
| 112 | + * @param int $type (=1:行情等接口,不需要签名;=2:账户等接口需要签名) | ||
| 113 | + * @return array|int|mixed | ||
| 114 | + */ | ||
| 115 | + public function request(string $path, string $method, array $param, int $type=1) | ||
| 116 | + { | ||
| 117 | + if($type==2){ | ||
| 118 | + return self::requestAccount($method, $path, $param); | ||
| 119 | + }else if($type==1){ | ||
| 120 | + return self::requestMarket($method, $path, $param); | ||
| 121 | + }else{ | ||
| 122 | + return ['code'=>1000,'msg'=>'type参数错误']; | ||
| 123 | + } | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + //获取账户余额 | ||
| 127 | + public function getUserAsset($param=[]) | ||
| 128 | + { | ||
| 129 | + $path = '/papi/v1/balance'; | ||
| 130 | + $method = 'GET'; | ||
| 131 | + return $this->requestAccount($method, $path, $param); | ||
| 132 | + } | ||
| 133 | + //获取Listen Key (USER_STREAM) | ||
| 134 | + public function listenKey($param=[]) | ||
| 135 | + { | ||
| 136 | + $path = '/papi/v1/listenKey'; | ||
| 137 | + $method = 'POST'; | ||
| 138 | + return $this->requestListenKey($method, $path, $param); | ||
| 139 | + } | ||
| 140 | + //延长 Listen Key 有效期 (USER_STREAM) | ||
| 141 | + public function listenKeyDelay($param=[]) | ||
| 142 | + { | ||
| 143 | + $path = '/papi/v1/listenKey'; | ||
| 144 | + $method = 'PUT'; | ||
| 145 | + return $this->requestListenKey($method, $path, $param); | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + //现有UM账户资产和仓位信息【期货合约(永续合约、交割合约)】 | ||
| 149 | + public function accountUm($param=[]) | ||
| 150 | + { | ||
| 151 | + $path = '/papi/v1/um/account'; | ||
| 152 | + $method = 'GET'; | ||
| 153 | + return $this->requestAccount($method, $path, $param); | ||
| 154 | + } | ||
| 155 | + //获取现有CM账户资产和仓位信息【现货市场(包括杠杆借贷现货交易)】 | ||
| 156 | + public function accountCm($param=[]) | ||
| 157 | + { | ||
| 158 | + $path = '/papi/v1/cm/account'; | ||
| 159 | + $method = 'GET'; | ||
| 160 | + return $this->requestAccount($method, $path, $param); | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | +// public function order($param) | ||
| 164 | +// { | ||
| 165 | +// $path = '/api/v3/order'; | ||
| 166 | +// $method = 'POST'; | ||
| 167 | +// return $this->requestAccount($method, $path, $param); | ||
| 168 | +// } | ||
| 169 | +// | ||
| 170 | +// public function cancelOrderById($param){ | ||
| 171 | +// $path = '/api/v3/order'; | ||
| 172 | +// $method = 'DELETE'; | ||
| 173 | +// return $this->requestAccount($method, $path, $param); | ||
| 174 | +// } | ||
| 175 | +// | ||
| 176 | +// public function openOrders($param){ | ||
| 177 | +// $path = '/api/v3/openOrders'; | ||
| 178 | +// $method = 'GET'; | ||
| 179 | +// return $this->requestAccount($method, $path, $param); | ||
| 180 | +// } | ||
| 181 | +// | ||
| 182 | + //统一账户账户生成 Listen Key (USER_STREAM) | ||
| 183 | + | ||
| 184 | + | ||
| 185 | +} | ||
| 186 | + | ||
| 187 | +?> |
| 1 | <?php | 1 | <?php |
| 2 | 2 | ||
| 3 | -namespace jiaoyin; | 3 | +namespace Jiaoyin; |
| 4 | 4 | ||
| 5 | class Curl | 5 | class Curl |
| 6 | { | 6 | { |
| @@ -19,13 +19,15 @@ class Curl | @@ -19,13 +19,15 @@ class Curl | ||
| 19 | $output = curl_exec($ch); | 19 | $output = curl_exec($ch); |
| 20 | if ($output === false) { | 20 | if ($output === false) { |
| 21 | echo 'Curl error: ' . curl_error($ch); | 21 | echo 'Curl error: ' . curl_error($ch); |
| 22 | + curl_close($ch); | ||
| 23 | + return json_encode(['code' => '-1', 'msg' => 'curl error: ' . curl_error($ch)]); | ||
| 22 | } | 24 | } |
| 23 | curl_close($ch); | 25 | curl_close($ch); |
| 24 | return $output; | 26 | return $output; |
| 25 | } | 27 | } |
| 26 | 28 | ||
| 27 | //post 获取数据 | 29 | //post 获取数据 |
| 28 | - static public function httpPost($url, $param = [], $header = [], $asBody = false) | 30 | + static public function httpPost($url, $param = [], $header = []) |
| 29 | { | 31 | { |
| 30 | if (empty($url)) { | 32 | if (empty($url)) { |
| 31 | return false; | 33 | return false; |
| @@ -35,42 +37,21 @@ class Curl | @@ -35,42 +37,21 @@ class Curl | ||
| 35 | $opts = []; | 37 | $opts = []; |
| 36 | $opts[CURLOPT_POST] = 1; | 38 | $opts[CURLOPT_POST] = 1; |
| 37 | $opts[CURLOPT_USERAGENT] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36"; | 39 | $opts[CURLOPT_USERAGENT] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36"; |
| 38 | - | ||
| 39 | - // 修改判断逻辑 | ||
| 40 | - $isJson = false; | ||
| 41 | - foreach ($header as $h) { | ||
| 42 | - if (stripos($h, 'Content-Type: application/json') !== false) { | ||
| 43 | - $isJson = true; | ||
| 44 | - break; | ||
| 45 | - } | ||
| 46 | - } | ||
| 47 | - | ||
| 48 | - if ($asBody) { | ||
| 49 | - // form-data格式提交 | ||
| 50 | - $boundary = '----WebKitFormBoundary' . uniqid(); | ||
| 51 | - $body = ''; | ||
| 52 | - foreach ($param as $key => $value) { | ||
| 53 | - $body .= "--$boundary\r\n"; | ||
| 54 | - $body .= "Content-Disposition: form-data; name=\"$key\"\r\n\r\n"; | ||
| 55 | - $body .= "$value\r\n"; | ||
| 56 | - } | ||
| 57 | - $body .= "--$boundary--\r\n"; | ||
| 58 | - $opts[CURLOPT_POSTFIELDS] = $body; | ||
| 59 | - $header[] = "Content-Type: multipart/form-data; boundary=$boundary"; | ||
| 60 | - curl_setopt($ch, CURLOPT_HTTPHEADER, $header); | 40 | + if (!empty($header) && in_array('Content-Type:application/json', $header)) { |
| 41 | + $opts[CURLOPT_POSTFIELDS] = json_encode($param); | ||
| 61 | } else { | 42 | } else { |
| 62 | - $opts[CURLOPT_POSTFIELDS] = $isJson ? json_encode($param) : http_build_query($param); | 43 | + $opts[CURLOPT_POSTFIELDS] = http_build_query($param); |
| 63 | } | 44 | } |
| 64 | - | ||
| 65 | curl_setopt_array($ch, $opts); | 45 | curl_setopt_array($ch, $opts); |
| 66 | $output = curl_exec($ch); | 46 | $output = curl_exec($ch); |
| 67 | if ($output === false) { | 47 | if ($output === false) { |
| 68 | echo 'Curl error: ' . curl_error($ch); | 48 | echo 'Curl error: ' . curl_error($ch); |
| 49 | + curl_close($ch); | ||
| 50 | + return json_encode(['code' => '-1', 'msg' => 'curl error: ' . curl_error($ch)]); | ||
| 69 | } | 51 | } |
| 70 | curl_close($ch); | 52 | curl_close($ch); |
| 71 | return $output; | 53 | return $output; |
| 72 | } | 54 | } |
| 73 | - | ||
| 74 | //delete 请求 | 55 | //delete 请求 |
| 75 | static public function httpDelete($url, $param = [], $header = []) | 56 | static public function httpDelete($url, $param = [], $header = []) |
| 76 | { | 57 | { |
| @@ -86,6 +67,9 @@ class Curl | @@ -86,6 +67,9 @@ class Curl | ||
| 86 | $output = curl_exec($ch); | 67 | $output = curl_exec($ch); |
| 87 | if ($output === false) { | 68 | if ($output === false) { |
| 88 | echo 'Curl error: ' . curl_error($ch); | 69 | echo 'Curl error: ' . curl_error($ch); |
| 70 | + echo 'Curl error: ' . curl_error($ch); | ||
| 71 | + curl_close($ch); | ||
| 72 | + return json_encode(['code' => '-1', 'msg' => 'curl error: ' . curl_error($ch)]); | ||
| 89 | } | 73 | } |
| 90 | curl_close($ch); | 74 | curl_close($ch); |
| 91 | return $output; | 75 | return $output; |
| 1 | <?php | 1 | <?php |
| 2 | +namespace Jiaoyin; | ||
| 2 | 3 | ||
| 3 | -namespace jiaoyin; | ||
| 4 | - | ||
| 5 | -class Feishu | ||
| 6 | -{ | 4 | +class Feishu{ |
| 7 | const user_liquid = 'gd1d6b34'; | 5 | const user_liquid = 'gd1d6b34'; |
| 8 | const user_jiaoyin = 'efdgdecb'; | 6 | const user_jiaoyin = 'efdgdecb'; |
| 9 | const user_small = 'db9d9g4d'; | 7 | const user_small = 'db9d9g4d'; |
| @@ -11,13 +9,13 @@ class Feishu | @@ -11,13 +9,13 @@ class Feishu | ||
| 11 | const user_tanli = 'f16c2bd7'; | 9 | const user_tanli = 'f16c2bd7'; |
| 12 | const user_007 = '9de6cgf3'; | 10 | const user_007 = '9de6cgf3'; |
| 13 | //风控处置群 | 11 | //风控处置群 |
| 14 | - const url_risk = 'https://open.feishu.cn/open-apis/bot/v2/hook/7509c731-a1ab-4e2f-b781-9082a26fd11d'; | 12 | + const url_risk ='https://open.feishu.cn/open-apis/bot/v2/hook/7509c731-a1ab-4e2f-b781-9082a26fd11d'; |
| 15 | //零容忍 | 13 | //零容忍 |
| 16 | const url_error = 'https://open.feishu.cn/open-apis/bot/v2/hook/a344e313-4d5d-4aa4-912b-dda37b2e3ee8'; | 14 | const url_error = 'https://open.feishu.cn/open-apis/bot/v2/hook/a344e313-4d5d-4aa4-912b-dda37b2e3ee8'; |
| 17 | //零忽略 | 15 | //零忽略 |
| 18 | const url_warning = 'https://open.feishu.cn/open-apis/bot/v2/hook/01c53b94-378a-45a3-abed-415f84f57f4a'; | 16 | const url_warning = 'https://open.feishu.cn/open-apis/bot/v2/hook/01c53b94-378a-45a3-abed-415f84f57f4a'; |
| 19 | //软件异常报警 | 17 | //软件异常报警 |
| 20 | - const url_warning_soft = 'https://open.feishu.cn/open-apis/bot/v2/hook/d48c2e4a-0ef9-4684-88d4-e62d878fdaca'; | 18 | + const url_warning_soft ='https://open.feishu.cn/open-apis/bot/v2/hook/d48c2e4a-0ef9-4684-88d4-e62d878fdaca'; |
| 21 | //测试频道 | 19 | //测试频道 |
| 22 | const url_test = 'https://open.feishu.cn/open-apis/bot/v2/hook/34a9f127-3838-43fc-bc35-6f5e4e96bf6d'; | 20 | const url_test = 'https://open.feishu.cn/open-apis/bot/v2/hook/34a9f127-3838-43fc-bc35-6f5e4e96bf6d'; |
| 23 | //交易与跟单业务沟通 | 21 | //交易与跟单业务沟通 |
| @@ -26,36 +24,31 @@ class Feishu | @@ -26,36 +24,31 @@ class Feishu | ||
| 26 | const url_strategy = 'https://open.feishu.cn/open-apis/bot/v2/hook/53444f37-cfec-420c-a4e6-2f415b908dee'; | 24 | const url_strategy = 'https://open.feishu.cn/open-apis/bot/v2/hook/53444f37-cfec-420c-a4e6-2f415b908dee'; |
| 27 | //网格策略运营群 | 25 | //网格策略运营群 |
| 28 | const url_grid = 'https://open.feishu.cn/open-apis/bot/v2/hook/19471aa1-f926-46d7-a1d5-a367d1449092'; | 26 | const url_grid = 'https://open.feishu.cn/open-apis/bot/v2/hook/19471aa1-f926-46d7-a1d5-a367d1449092'; |
| 29 | - //量化机会通知群 | ||
| 30 | - const url_quant_chance = 'https://open.feishu.cn/open-apis/bot/v2/hook/b590bbd4-6a0a-4328-bf28-9fc15677643e'; | ||
| 31 | - static public function notice(string $content, string $channel_url, array $users = []) | 27 | + static public function notice($content,$channel_url,$users=[]) |
| 32 | { | 28 | { |
| 33 | $userXml = ''; | 29 | $userXml = ''; |
| 34 | foreach ($users as $user) { | 30 | foreach ($users as $user) { |
| 35 | $userXml .= "<at user_id='" . $user . "'></at>"; | 31 | $userXml .= "<at user_id='" . $user . "'></at>"; |
| 36 | } | 32 | } |
| 37 | $content = [ | 33 | $content = [ |
| 38 | - 'msg_type' => "text", | ||
| 39 | - 'content' => [ | ||
| 40 | - 'text' => $content . $userXml | 34 | + 'msg_type'=>"text", |
| 35 | + 'content'=>[ | ||
| 36 | + 'text'=>$content.$userXml | ||
| 41 | ] | 37 | ] |
| 42 | ]; | 38 | ]; |
| 43 | $con = json_encode($content); | 39 | $con = json_encode($content); |
| 44 | - return self::send_post_json($channel_url, $con); | 40 | + return self::send_post_json($channel_url,$con); |
| 45 | } | 41 | } |
| 46 | static public function send_post_json($url, $jsonStr) | 42 | static public function send_post_json($url, $jsonStr) |
| 47 | { | 43 | { |
| 48 | $ch = curl_init(); | 44 | $ch = curl_init(); |
| 49 | curl_setopt($ch, CURLOPT_POST, 1); | 45 | curl_setopt($ch, CURLOPT_POST, 1); |
| 50 | - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); | ||
| 51 | - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); | 46 | + curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); |
| 47 | + curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); | ||
| 52 | curl_setopt($ch, CURLOPT_URL, $url); | 48 | curl_setopt($ch, CURLOPT_URL, $url); |
| 53 | curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonStr); | 49 | curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonStr); |
| 54 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | 50 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); |
| 55 | - curl_setopt( | ||
| 56 | - $ch, | ||
| 57 | - CURLOPT_HTTPHEADER, | ||
| 58 | - array( | 51 | + curl_setopt($ch, CURLOPT_HTTPHEADER, array( |
| 59 | 'Content-Type: application/json; charset=utf-8', | 52 | 'Content-Type: application/json; charset=utf-8', |
| 60 | 'Content-Length: ' . strlen($jsonStr) | 53 | 'Content-Length: ' . strlen($jsonStr) |
| 61 | ) | 54 | ) |
| 1 | <?php | 1 | <?php |
| 2 | 2 | ||
| 3 | -namespace jiaoyin; | ||
| 4 | - | ||
| 5 | -require_once __DIR__ . '/func.php'; | ||
| 6 | -namespace jiaoyin; | 3 | +namespace Jiaoyin; |
| 4 | +require_once __DIR__ . '/functions.php'; | ||
| 7 | class Logger | 5 | class Logger |
| 8 | { | 6 | { |
| 9 | private string $logPath; | 7 | private string $logPath; |
| 10 | private bool $dateSlice; | 8 | private bool $dateSlice; |
| 11 | private array $types; | 9 | private array $types; |
| 12 | 10 | ||
| 13 | - public function __construct($logPath, $types = ['info', 'warning', 'error'], $dateSlice = false) | 11 | + public function __construct($logPath, $types = ['info','warning','error'], $dateSlice = false) |
| 14 | { | 12 | { |
| 15 | $this->logPath = $logPath; | 13 | $this->logPath = $logPath; |
| 16 | $this->dateSlice = $dateSlice; | 14 | $this->dateSlice = $dateSlice; |
| 17 | $this->types = $types; | 15 | $this->types = $types; |
| 18 | } | 16 | } |
| 19 | - public function debug($msg) | ||
| 20 | - { | ||
| 21 | - $this->log($msg, 'debug'); | 17 | + public function debug($msg){ |
| 18 | + $this->log($msg,'debug'); | ||
| 22 | } | 19 | } |
| 23 | - public function info($msg) | ||
| 24 | - { | ||
| 25 | - $this->log($msg, 'info'); | 20 | + public function info($msg){ |
| 21 | + $this->log($msg,'info'); | ||
| 26 | } | 22 | } |
| 27 | - public function warning($msg) | ||
| 28 | - { | ||
| 29 | - $this->log($msg, 'warning'); | 23 | + public function warning($msg){ |
| 24 | + $this->log($msg,'warning'); | ||
| 30 | } | 25 | } |
| 31 | - public function error($msg) | ||
| 32 | - { | ||
| 33 | - $this->log($msg, 'error'); | 26 | + public function error($msg){ |
| 27 | + $this->log($msg,'error'); | ||
| 34 | } | 28 | } |
| 35 | - private function log($msg, $type) | ||
| 36 | - { | ||
| 37 | - $msg = '[' . timeFormat('ms') . '][' . $type . ']:' . $msg . PHP_EOL; | 29 | + private function log($msg, $type){ |
| 30 | + $msg = '['.timeFormat('ms').']['.$type.']:'.$msg.PHP_EOL; | ||
| 38 | echo $msg; | 31 | echo $msg; |
| 39 | - if (!in_array($type, $this->types)) { | 32 | + if (!in_array($type,$this->types)){ |
| 40 | return; | 33 | return; |
| 41 | } | 34 | } |
| 42 | if ($this->dateSlice) { | 35 | if ($this->dateSlice) { |
| 43 | - $path = $this->logPath . '/' . date('Y-m-d'); | ||
| 44 | - } else { | 36 | + $path = $this->logPath.'/'.date('Y-m-d'); |
| 37 | + }else{ | ||
| 45 | $path = $this->logPath; | 38 | $path = $this->logPath; |
| 46 | } | 39 | } |
| 47 | - $file = $path . '/' . $type . '.log'; | ||
| 48 | - $this->save($file, $msg, $type); | 40 | + $file = $path.'/'.$type.'.log'; |
| 41 | + $this->save($file,$msg,$type); | ||
| 49 | } | 42 | } |
| 50 | - private function save($file, $msg, $type = 'info') | ||
| 51 | - { | ||
| 52 | - \Swoole\Coroutine::create(function () use ($file, $msg, $type) { | 43 | + private function save($file,$msg, $type='info'){ |
| 44 | + \Swoole\Coroutine::create(function () use ($file,$msg,$type) { | ||
| 53 | $this->checkFileDir($file); | 45 | $this->checkFileDir($file); |
| 54 | - file_put_contents($file, $msg, FILE_APPEND); | 46 | + file_put_contents($file,$msg,FILE_APPEND); |
| 55 | }); | 47 | }); |
| 56 | } | 48 | } |
| 57 | - private function checkFileDir($file) | ||
| 58 | - { | 49 | + private function checkFileDir($file){ |
| 59 | // 获取目录路径 | 50 | // 获取目录路径 |
| 60 | $directoryPath = dirname($file); | 51 | $directoryPath = dirname($file); |
| 61 | if (!is_dir($directoryPath)) { | 52 | if (!is_dir($directoryPath)) { |
| 1 | <?php | 1 | <?php |
| 2 | - | ||
| 3 | -namespace jiaoyin; | 2 | +namespace Jiaoyin; |
| 4 | 3 | ||
| 5 | class MongoCli | 4 | class MongoCli |
| 6 | { | 5 | { |
| 7 | private $pool; | 6 | private $pool; |
| 8 | private $database; | 7 | private $database; |
| 9 | - public function __construct($host, $port, $username, $password, $database, $num = 20) | 8 | + public function __construct($host,$port,$username,$password,$database, $num = 20) |
| 10 | { | 9 | { |
| 11 | $this->database = $database; | 10 | $this->database = $database; |
| 12 | - $userInfo = $username ? "{$username}:{$password}@" : ""; | 11 | + $userInfo = $username?"{$username}:{$password}@":""; |
| 13 | $dsn = "mongodb://{$userInfo}{$host}:{$port}/{$database}"; | 12 | $dsn = "mongodb://{$userInfo}{$host}:{$port}/{$database}"; |
| 14 | - $this->pool = new MongoPool($dsn, $num); | 13 | + $this->pool = new MongoPool($dsn,$num); |
| 15 | } | 14 | } |
| 16 | 15 | ||
| 17 | /* | 16 | /* |
| 18 | * 创建集合 | 17 | * 创建集合 |
| 19 | */ | 18 | */ |
| 20 | - public function createCollection($table, $createIndex = []) | ||
| 21 | - { | 19 | + public function createCollection($table,$createIndex=[]){ |
| 22 | try { | 20 | try { |
| 23 | $mongodb = $this->pool->get(); | 21 | $mongodb = $this->pool->get(); |
| 24 | $database = $mongodb->selectDatabase($this->database); | 22 | $database = $mongodb->selectDatabase($this->database); |
| @@ -61,7 +59,7 @@ class MongoCli | @@ -61,7 +59,7 @@ class MongoCli | ||
| 61 | * 写入数据 | 59 | * 写入数据 |
| 62 | * $collection->insertOne(['name' => 'John Doe']); | 60 | * $collection->insertOne(['name' => 'John Doe']); |
| 63 | * */ | 61 | * */ |
| 64 | - public function insertOne($table, $data = []) | 62 | + public function insertOne($table,$data = []) |
| 65 | { | 63 | { |
| 66 | try { | 64 | try { |
| 67 | $mongodb = $this->pool->get(); | 65 | $mongodb = $this->pool->get(); |
| @@ -80,7 +78,7 @@ class MongoCli | @@ -80,7 +78,7 @@ class MongoCli | ||
| 80 | * 批量写入数据 | 78 | * 批量写入数据 |
| 81 | * $collection->insertMany([['name' => 'John Doe'],['name' => 'John Doe']]); | 79 | * $collection->insertMany([['name' => 'John Doe'],['name' => 'John Doe']]); |
| 82 | * */ | 80 | * */ |
| 83 | - public function insertAll($table, $data = []) | 81 | + public function insertAll($table,$data = []) |
| 84 | { | 82 | { |
| 85 | try { | 83 | try { |
| 86 | $mongodb = $this->pool->get(); | 84 | $mongodb = $this->pool->get(); |
| @@ -99,7 +97,7 @@ class MongoCli | @@ -99,7 +97,7 @@ class MongoCli | ||
| 99 | * 查询单条数据 | 97 | * 查询单条数据 |
| 100 | * $collection->findOne(['name' => 'John Doe']); | 98 | * $collection->findOne(['name' => 'John Doe']); |
| 101 | * */ | 99 | * */ |
| 102 | - public function findOne($table, $where = []) | 100 | + public function findOne($table,$where = []) |
| 103 | { | 101 | { |
| 104 | try { | 102 | try { |
| 105 | $mongodb = $this->pool->get(); | 103 | $mongodb = $this->pool->get(); |
| @@ -126,17 +124,17 @@ class MongoCli | @@ -126,17 +124,17 @@ class MongoCli | ||
| 126 | * 'limit' => 100, | 124 | * 'limit' => 100, |
| 127 | * ]; | 125 | * ]; |
| 128 | * */ | 126 | * */ |
| 129 | - public function findAll($table, $where = [], $options = []) | 127 | + public function findAll($table,$where = [], $options=[]) |
| 130 | { | 128 | { |
| 131 | try { | 129 | try { |
| 132 | $mongodb = $this->pool->get(); | 130 | $mongodb = $this->pool->get(); |
| 133 | $database = $mongodb->selectDatabase($this->database); | 131 | $database = $mongodb->selectDatabase($this->database); |
| 134 | $collection = $database->selectCollection($table); | 132 | $collection = $database->selectCollection($table); |
| 135 | - $result = $collection->find($where, $options); | 133 | + $result = $collection->find($where,$options); |
| 136 | $this->pool->push($mongodb); | 134 | $this->pool->push($mongodb); |
| 137 | // 遍历结果集 | 135 | // 遍历结果集 |
| 138 | $data = []; | 136 | $data = []; |
| 139 | - if ($result) { | 137 | + if ($result){ |
| 140 | $arr = $result->toArray(); | 138 | $arr = $result->toArray(); |
| 141 | foreach ($arr as $item) { | 139 | foreach ($arr as $item) { |
| 142 | $data[] = $item->getArrayCopy(); | 140 | $data[] = $item->getArrayCopy(); |
| @@ -154,13 +152,13 @@ class MongoCli | @@ -154,13 +152,13 @@ class MongoCli | ||
| 154 | * 更新数据 | 152 | * 更新数据 |
| 155 | * $collection->updateOne(['name' => 'John Doe'], ['$set' => ['age' => 31]]); | 153 | * $collection->updateOne(['name' => 'John Doe'], ['$set' => ['age' => 31]]); |
| 156 | * */ | 154 | * */ |
| 157 | - public function updateOne($table, $where = [], $upData = []) | 155 | + public function updateOne($table,$where = [],$upData = []) |
| 158 | { | 156 | { |
| 159 | try { | 157 | try { |
| 160 | $mongodb = $this->pool->get(); | 158 | $mongodb = $this->pool->get(); |
| 161 | $database = $mongodb->selectDatabase($this->database); | 159 | $database = $mongodb->selectDatabase($this->database); |
| 162 | $collection = $database->selectCollection($table); | 160 | $collection = $database->selectCollection($table); |
| 163 | - $result = $collection->updateOne($where, $upData); | 161 | + $result = $collection->updateOne($where,$upData); |
| 164 | $this->pool->push($mongodb); | 162 | $this->pool->push($mongodb); |
| 165 | return $result; | 163 | return $result; |
| 166 | } catch (\Exception $e) { | 164 | } catch (\Exception $e) { |
| @@ -173,7 +171,7 @@ class MongoCli | @@ -173,7 +171,7 @@ class MongoCli | ||
| 173 | * 删除数据 | 171 | * 删除数据 |
| 174 | * $collection->deleteOne(['name' => 'John Doe']); | 172 | * $collection->deleteOne(['name' => 'John Doe']); |
| 175 | * */ | 173 | * */ |
| 176 | - public function deleteOne($table, $where = []) | 174 | + public function deleteOne($table,$where = []) |
| 177 | { | 175 | { |
| 178 | try { | 176 | try { |
| 179 | $mongodb = $this->pool->get(); | 177 | $mongodb = $this->pool->get(); |
| @@ -192,7 +190,7 @@ class MongoCli | @@ -192,7 +190,7 @@ class MongoCli | ||
| 192 | * 批量删除数据 | 190 | * 批量删除数据 |
| 193 | * $collection->deleteMany(['name' => 'John Doe']); | 191 | * $collection->deleteMany(['name' => 'John Doe']); |
| 194 | * */ | 192 | * */ |
| 195 | - public function deleteBatch($table, $where = []) | 193 | + public function deleteBatch($table,$where = []) |
| 196 | { | 194 | { |
| 197 | try { | 195 | try { |
| 198 | $mongodb = $this->pool->get(); | 196 | $mongodb = $this->pool->get(); |
| @@ -206,4 +204,5 @@ class MongoCli | @@ -206,4 +204,5 @@ class MongoCli | ||
| 206 | return []; | 204 | return []; |
| 207 | } | 205 | } |
| 208 | } | 206 | } |
| 207 | + | ||
| 209 | } | 208 | } |
| 1 | <?php | 1 | <?php |
| 2 | - | ||
| 3 | -namespace jiaoyin; | 2 | +namespace Jiaoyin; |
| 4 | 3 | ||
| 5 | use MongoDB\Client; | 4 | use MongoDB\Client; |
| 6 | use Swoole\Coroutine; | 5 | use Swoole\Coroutine; |
| @@ -14,7 +13,7 @@ class MongoPool | @@ -14,7 +13,7 @@ class MongoPool | ||
| 14 | private $mongoUri; | 13 | private $mongoUri; |
| 15 | private $options; | 14 | private $options; |
| 16 | 15 | ||
| 17 | - public function __construct($mongoUri, $maxSize = 20, $options = []) | 16 | + public function __construct($mongoUri, $maxSize=20, $options = []) |
| 18 | { | 17 | { |
| 19 | $this->mongoUri = $mongoUri; | 18 | $this->mongoUri = $mongoUri; |
| 20 | $this->maxSize = $maxSize; | 19 | $this->maxSize = $maxSize; |
| @@ -36,8 +35,8 @@ class MongoPool | @@ -36,8 +35,8 @@ class MongoPool | ||
| 36 | 35 | ||
| 37 | public function get() | 36 | public function get() |
| 38 | { | 37 | { |
| 39 | - while ($this->pool->isEmpty()) { | ||
| 40 | - // output('Mongodb连接池为空,等待释放连接'); | 38 | + while ($this->pool->isEmpty()){ |
| 39 | +// output('Mongodb连接池为空,等待释放连接'); | ||
| 41 | Coroutine::sleep(0.1); | 40 | Coroutine::sleep(0.1); |
| 42 | } | 41 | } |
| 43 | $connection = $this->pool->pop(); | 42 | $connection = $this->pool->pop(); |
| @@ -59,7 +58,7 @@ class MongoPool | @@ -59,7 +58,7 @@ class MongoPool | ||
| 59 | // 检查连接是否有效,这里仅做示例,实际应用中可能需要更复杂的逻辑 | 58 | // 检查连接是否有效,这里仅做示例,实际应用中可能需要更复杂的逻辑 |
| 60 | if (is_object($connection) && get_class($connection) === Client::class) { | 59 | if (is_object($connection) && get_class($connection) === Client::class) { |
| 61 | $this->pool->push($connection); | 60 | $this->pool->push($connection); |
| 62 | - } else { | 61 | + }else{ |
| 63 | $this->currentSize -= 1; | 62 | $this->currentSize -= 1; |
| 64 | } | 63 | } |
| 65 | } | 64 | } |
| 1 | <?php | 1 | <?php |
| 2 | 2 | ||
| 3 | -namespace jiaoyin; | 3 | +namespace Jiaoyin; |
| 4 | 4 | ||
| 5 | use Swoole\Database\MysqliConfig; | 5 | use Swoole\Database\MysqliConfig; |
| 6 | use Swoole\Database\MysqliPool; | 6 | use Swoole\Database\MysqliPool; |
| 7 | +use Swoole\Coroutine; | ||
| 8 | +use Swoole\Coroutine\Channel; | ||
| 7 | 9 | ||
| 8 | class MysqlCli | 10 | class MysqlCli |
| 9 | { | 11 | { |
| 10 | 12 | ||
| 11 | private $pool = null; | 13 | private $pool = null; |
| 12 | - private $prefix = ''; // 前缀 | 14 | + private $prefix = '';// 前缀 |
| 13 | 15 | ||
| 14 | private int $reconnectCount = 0; //period时间内重连次数 | 16 | private int $reconnectCount = 0; //period时间内重连次数 |
| 15 | private int $period = 300; | 17 | private int $period = 300; |
| @@ -45,57 +47,57 @@ class MysqlCli | @@ -45,57 +47,57 @@ class MysqlCli | ||
| 45 | ->withDbName($this->connectConfig['database']) | 47 | ->withDbName($this->connectConfig['database']) |
| 46 | ->withCharset($this->connectConfig['charset']) | 48 | ->withCharset($this->connectConfig['charset']) |
| 47 | ->withUsername($this->connectConfig['username']) | 49 | ->withUsername($this->connectConfig['username']) |
| 48 | - ->withPassword($this->connectConfig['password']), | ||
| 49 | - $this->connectConfig['connectCount'] | 50 | + ->withPassword($this->connectConfig['password']) |
| 51 | + , $this->connectConfig['connectCount'] | ||
| 50 | ); | 52 | ); |
| 51 | $this->prefix = $this->connectConfig['prefix']; | 53 | $this->prefix = $this->connectConfig['prefix']; |
| 52 | } | 54 | } |
| 53 | 55 | ||
| 54 | //执行sql | 56 | //执行sql |
| 55 | - public function execute($action, $sql) | 57 | + public function execute($action, $sql, float $timeout = 5.0, int $retry = 1) |
| 56 | { | 58 | { |
| 57 | $ret = false; | 59 | $ret = false; |
| 58 | $db = $this->pool->get(); | 60 | $db = $this->pool->get(); |
| 59 | try { | 61 | try { |
| 60 | - $stmt = $db->query($sql); | ||
| 61 | - if ($stmt == false) { | ||
| 62 | - output($db->errno, $db->error); | ||
| 63 | - } else { | ||
| 64 | - if ($action == 'SELECT') { | ||
| 65 | - $ret = $stmt->fetch_all(); | ||
| 66 | - } else { | ||
| 67 | - $ret = $stmt; | 62 | + $chan = new Channel(1); |
| 63 | + Coroutine::create(function () use ($chan, $db, $sql) { | ||
| 64 | + try { | ||
| 65 | + $result = $db->query($sql); | ||
| 66 | + $chan->push($result); | ||
| 67 | + } catch (\Throwable $e) { | ||
| 68 | + $chan->push($e); | ||
| 68 | } | 69 | } |
| 70 | + }); | ||
| 71 | + | ||
| 72 | + $result = $chan->pop($timeout); // 超时控制 | ||
| 73 | + | ||
| 74 | + if ($result === false) { | ||
| 75 | + // 超时 | ||
| 76 | + output("SQL超时: {$sql}, 超时时间 {$timeout}s"); | ||
| 77 | + if ($retry > 0) { | ||
| 78 | + output("超时后自动重试一次: {$sql}"); | ||
| 79 | + $this->pool->put($db); | ||
| 80 | + return $this->execute($action, $sql, $timeout, $retry - 1); | ||
| 69 | } | 81 | } |
| 70 | - } catch (\Exception $e) { | ||
| 71 | - $errorMsg = $e->getMessage(); | ||
| 72 | - output($sql); | ||
| 73 | - output('sql错误', $errorMsg); | ||
| 74 | - if ($errorMsg == 'MySQL server has gone away') { | ||
| 75 | - if ($this->reconnectCount >= 3 && time() - $this->lastReconnectTime < $this->period) { | ||
| 76 | - //period内重连超过3次,不继续重连但电话通知异常 | ||
| 77 | - output('数据库连接失效,并重连3次失败'); | 82 | + } elseif ($result instanceof \Throwable) { |
| 83 | + output("执行SQL异常: {$sql}"); | ||
| 84 | + output("异常信息: " . $result->getMessage()); | ||
| 78 | } else { | 85 | } else { |
| 79 | - //重连 | ||
| 80 | - output('重新创建mysql连接池', $this->reconnectCount); | ||
| 81 | - $this->pool->put($db); | ||
| 82 | - $this->poolConnect(); | ||
| 83 | - if (time() - $this->lastReconnectTime < $this->period) { | ||
| 84 | - $this->reconnectCount += 1; | 86 | + if ($action === 'SELECT') { |
| 87 | + $ret = $result->fetch_all(); | ||
| 85 | } else { | 88 | } else { |
| 86 | - $this->reconnectCount = 0; | 89 | + $ret = $result; |
| 87 | } | 90 | } |
| 88 | - $this->lastReconnectTime = time(); | ||
| 89 | - return $this->execute($action, $sql); | ||
| 90 | } | 91 | } |
| 91 | - } else { | ||
| 92 | - output('数据库操作异常'); | 92 | + |
| 93 | + } finally { | ||
| 94 | + if ($db) { | ||
| 95 | + $this->pool->put($db); | ||
| 93 | } | 96 | } |
| 94 | } | 97 | } |
| 95 | - $this->pool->put($db); | 98 | + |
| 96 | return $ret; | 99 | return $ret; |
| 97 | } | 100 | } |
| 98 | - | ||
| 99 | //插入数据 | 101 | //插入数据 |
| 100 | public function insert($table, $data) | 102 | public function insert($table, $data) |
| 101 | { | 103 | { |
| @@ -109,13 +111,36 @@ class MysqlCli | @@ -109,13 +111,36 @@ class MysqlCli | ||
| 109 | //select数据 | 111 | //select数据 |
| 110 | public function select($table, $where = [], $col = [], $orderBy = '', $limit = '') | 112 | public function select($table, $where = [], $col = [], $orderBy = '', $limit = '') |
| 111 | { | 113 | { |
| 112 | - | ||
| 113 | $sql = $this->parseSelect($table, $where, $col, $orderBy, $limit); | 114 | $sql = $this->parseSelect($table, $where, $col, $orderBy, $limit); |
| 114 | if (!$sql) { | 115 | if (!$sql) { |
| 115 | return false; | 116 | return false; |
| 116 | } | 117 | } |
| 117 | $data = $this->execute('SELECT', $sql); | 118 | $data = $this->execute('SELECT', $sql); |
| 119 | + | ||
| 120 | + // 如果没有数据 | ||
| 121 | + if (!$data) return []; | ||
| 122 | + | ||
| 118 | $newData = []; | 123 | $newData = []; |
| 124 | + | ||
| 125 | + // ✅ 判断是否包含聚合函数 | ||
| 126 | + $isAggregate = false; | ||
| 127 | + foreach ($col as $c) { | ||
| 128 | + if (stripos($c, 'sum(') !== false || stripos($c, 'count(') !== false || stripos($c, 'avg(') !== false || stripos($c, 'max(') !== false || stripos($c, 'min(') !== false) { | ||
| 129 | + $isAggregate = true; | ||
| 130 | + break; | ||
| 131 | + } | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + if ($isAggregate) { | ||
| 135 | + // ✅ 聚合查询直接使用 fetch_assoc 风格 | ||
| 136 | + $db = $this->pool->get(); | ||
| 137 | + $stmt = $db->query($sql); | ||
| 138 | + $result = $stmt->fetch_assoc(); | ||
| 139 | + $this->pool->put($db); | ||
| 140 | + return [$result]; | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + // ✅ 普通查询才查字段结构 | ||
| 119 | if (empty($col)) { | 144 | if (empty($col)) { |
| 120 | $newsql = 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = "' . $this->database . '" AND TABLE_NAME="' . $this->parseTable($table) . '" ORDER BY `ORDINAL_POSITION` ASC'; | 145 | $newsql = 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = "' . $this->database . '" AND TABLE_NAME="' . $this->parseTable($table) . '" ORDER BY `ORDINAL_POSITION` ASC'; |
| 121 | $coldata = $this->execute('SELECT', $newsql); | 146 | $coldata = $this->execute('SELECT', $newsql); |
| @@ -127,6 +152,7 @@ class MysqlCli | @@ -127,6 +152,7 @@ class MysqlCli | ||
| 127 | } | 152 | } |
| 128 | } | 153 | } |
| 129 | } | 154 | } |
| 155 | + | ||
| 130 | foreach ($data as $key => $value) { | 156 | foreach ($data as $key => $value) { |
| 131 | $tmp = []; | 157 | $tmp = []; |
| 132 | foreach ($col as $k => $v) { | 158 | foreach ($col as $k => $v) { |
| @@ -208,12 +234,16 @@ class MysqlCli | @@ -208,12 +234,16 @@ class MysqlCli | ||
| 208 | var_dump($where); | 234 | var_dump($where); |
| 209 | return false; | 235 | return false; |
| 210 | } | 236 | } |
| 211 | - $value[1] = $this->transferValue($value[1]); | ||
| 212 | - if ($value[1] === 'NULL') { | 237 | + $symbol = $value[2] ?? '='; |
| 238 | + // in + 数组专门处理 | ||
| 239 | + if (strtolower($symbol) === 'in' && is_array($value[1])) { | ||
| 240 | + $val = "('" . implode("','", array_map('addslashes', $value[1])) . "')"; | ||
| 241 | + $tmp .= $value[0] . ' in ' . $val; | ||
| 242 | + } elseif ($value[1] === 'NULL') { | ||
| 213 | $tmp .= $value[0] . ' is NULL'; | 243 | $tmp .= $value[0] . ' is NULL'; |
| 214 | } else { | 244 | } else { |
| 215 | - $symbol = $value[2] ?? '='; | ||
| 216 | - $tmp .= $value[0] . ' ' . $symbol . ' ' . $value[1]; | 245 | + $val = $this->transferValue($value[1]); |
| 246 | + $tmp .= $value[0] . ' ' . $symbol . ' ' . $val; | ||
| 217 | } | 247 | } |
| 218 | $tmp .= $key < count($where) - 1 ? ' and ' : ''; | 248 | $tmp .= $key < count($where) - 1 ? ' and ' : ''; |
| 219 | } | 249 | } |
| @@ -273,4 +303,5 @@ class MysqlCli | @@ -273,4 +303,5 @@ class MysqlCli | ||
| 273 | } | 303 | } |
| 274 | return $valueNew; | 304 | return $valueNew; |
| 275 | } | 305 | } |
| 306 | + | ||
| 276 | } | 307 | } |
| 1 | <?php | 1 | <?php |
| 2 | - | ||
| 3 | -namespace jiaoyin; | 2 | +namespace Jiaoyin; |
| 4 | 3 | ||
| 5 | /* | 4 | /* |
| 6 | 所有接口 | 5 | 所有接口 |
| 7 | */ | 6 | */ |
| 8 | - | ||
| 9 | class OkApi | 7 | class OkApi |
| 10 | { | 8 | { |
| 11 | protected string $host = 'https://www.okx.com'; | 9 | protected string $host = 'https://www.okx.com'; |
| @@ -80,9 +78,9 @@ class OkApi | @@ -80,9 +78,9 @@ class OkApi | ||
| 80 | ]); | 78 | ]); |
| 81 | } | 79 | } |
| 82 | 80 | ||
| 83 | - $headers_array = []; | ||
| 84 | - foreach ($headers as $key => $value) { | ||
| 85 | - $headers_array[] = $key . ':' . $value; | 81 | + $headers_array=[]; |
| 82 | + foreach ($headers as $key => $value){ | ||
| 83 | + $headers_array[]=$key.':'.$value; | ||
| 86 | } | 84 | } |
| 87 | $this->headers = $headers_array; | 85 | $this->headers = $headers_array; |
| 88 | return $headers; | 86 | return $headers; |
| @@ -92,14 +90,14 @@ class OkApi | @@ -92,14 +90,14 @@ class OkApi | ||
| 92 | * 公共请求接口 | 90 | * 公共请求接口 |
| 93 | * $type 1: 需鉴权 2: 不需鉴权 | 91 | * $type 1: 需鉴权 2: 不需鉴权 |
| 94 | * */ | 92 | * */ |
| 95 | - public function request($method, $path, $param, $type = 1) | 93 | + public function request($method, $path, $param,$type=1) |
| 96 | { | 94 | { |
| 97 | $this->method = strtoupper($method); | 95 | $this->method = strtoupper($method); |
| 98 | $this->path = $path; | 96 | $this->path = $path; |
| 99 | $this->param = $param; | 97 | $this->param = $param; |
| 100 | - if ($type == 2) { | 98 | + if($type==2){ |
| 101 | $this->createHeaders(); | 99 | $this->createHeaders(); |
| 102 | - } else { | 100 | + }else{ |
| 103 | if (empty($this->apikey) || empty($this->secret) || empty($this->apipwd)) { | 101 | if (empty($this->apikey) || empty($this->secret) || empty($this->apipwd)) { |
| 104 | return ['code' => -1, 'msg' => 'apikey, secret or apipwd is empty']; | 102 | return ['code' => -1, 'msg' => 'apikey, secret or apipwd is empty']; |
| 105 | } | 103 | } |
| @@ -127,7 +125,7 @@ class OkApi | @@ -127,7 +125,7 @@ class OkApi | ||
| 127 | /** | 125 | /** |
| 128 | * 合约账户余额查询 | 126 | * 合约账户余额查询 |
| 129 | */ | 127 | */ |
| 130 | - public function getBalance($param = []) | 128 | + public function getBalance($param=[]) |
| 131 | { | 129 | { |
| 132 | $path = '/api/v5/account/balance'; | 130 | $path = '/api/v5/account/balance'; |
| 133 | return $this->request('GET', $path, $param); | 131 | return $this->request('GET', $path, $param); |
| @@ -135,7 +133,7 @@ class OkApi | @@ -135,7 +133,7 @@ class OkApi | ||
| 135 | /** | 133 | /** |
| 136 | * 资金账户余额查询 | 134 | * 资金账户余额查询 |
| 137 | */ | 135 | */ |
| 138 | - public function getAssetBalance($param = []) | 136 | + public function getAssetBalance($param=[]) |
| 139 | { | 137 | { |
| 140 | $path = '/api/v5/asset/balances'; | 138 | $path = '/api/v5/asset/balances'; |
| 141 | return $this->request('GET', $path, $param); | 139 | return $this->request('GET', $path, $param); |
| @@ -160,9 +158,10 @@ class OkApi | @@ -160,9 +158,10 @@ class OkApi | ||
| 160 | /* | 158 | /* |
| 161 | * 公共请求接口 | 159 | * 公共请求接口 |
| 162 | * */ | 160 | * */ |
| 163 | - public function getTransferHistory($path, $param) | 161 | + public function getTransferHistory($path,$param) |
| 164 | { | 162 | { |
| 165 | $path = '/api/v5/asset/bills'; | 163 | $path = '/api/v5/asset/bills'; |
| 166 | return $this->request('GET', $path, $param); | 164 | return $this->request('GET', $path, $param); |
| 167 | } | 165 | } |
| 166 | + | ||
| 168 | } | 167 | } |
| 1 | <?php | 1 | <?php |
| 2 | 2 | ||
| 3 | -namespace jiaoyin; | 3 | +namespace Jiaoyin; |
| 4 | 4 | ||
| 5 | use Swoole\Database\RedisConfig; | 5 | use Swoole\Database\RedisConfig; |
| 6 | use Swoole\Database\RedisPool; | 6 | use Swoole\Database\RedisPool; |
| 7 | +use function Jiaoyin\output; | ||
| 7 | 8 | ||
| 8 | class RedisCli | 9 | class RedisCli |
| 9 | { | 10 | { |
| 10 | private $pool; | 11 | private $pool; |
| 11 | public $subRedis = null; | 12 | public $subRedis = null; |
| 12 | - public function __construct($host, $port, $password, $dbIndex, $num = 20) | 13 | + |
| 14 | + private $host; | ||
| 15 | + private $port; | ||
| 16 | + private $dbIndex; | ||
| 17 | + | ||
| 18 | + public function __construct($host, $port, $password, $dbIndex, $num = 40) | ||
| 13 | { | 19 | { |
| 14 | - $this->pool = new RedisPool((new RedisConfig) | 20 | + $this->host = $host; |
| 21 | + $this->port = $port; | ||
| 22 | + $this->dbIndex = $dbIndex; | ||
| 23 | + | ||
| 24 | + $this->pool = new RedisPool( | ||
| 25 | + (new RedisConfig) | ||
| 15 | ->withHost($host) | 26 | ->withHost($host) |
| 16 | ->withPort($port) | 27 | ->withPort($port) |
| 17 | ->withAuth($password) | 28 | ->withAuth($password) |
| 18 | ->withDbIndex($dbIndex), | 29 | ->withDbIndex($dbIndex), |
| 19 | - $num //默认64个连接池 | 30 | + $num |
| 20 | ); | 31 | ); |
| 21 | } | 32 | } |
| 22 | - //集合存数据 | ||
| 23 | - public function sAdd($key, $member) | 33 | + |
| 34 | + private function exec(callable $fn, string $method) | ||
| 24 | { | 35 | { |
| 25 | $redis = $this->pool->get(); | 36 | $redis = $this->pool->get(); |
| 26 | try { | 37 | try { |
| 27 | - $res = $redis->sAdd($key, $member); | 38 | + return $fn($redis); |
| 28 | } catch (\RedisException $e) { | 39 | } catch (\RedisException $e) { |
| 29 | - return false; | 40 | + $this->logError($method, $e); |
| 41 | + try { | ||
| 42 | + $redis->close(); // 销毁坏连接 | ||
| 43 | + } catch (\Throwable $t) { | ||
| 30 | } | 44 | } |
| 45 | + return false; | ||
| 46 | + } finally { | ||
| 47 | + if ($redis && $redis->isConnected()) { | ||
| 31 | $this->pool->put($redis); | 48 | $this->pool->put($redis); |
| 32 | - return $res; | 49 | + } |
| 50 | + } | ||
| 33 | } | 51 | } |
| 34 | 52 | ||
| 35 | - //获取集合 | ||
| 36 | - public function sMembers($key) | 53 | + // ------------------ 集合 ------------------ |
| 54 | + public function sAdd($key, $member) | ||
| 37 | { | 55 | { |
| 38 | - $redis = $this->pool->get(); | ||
| 39 | - try { | ||
| 40 | - $res = $redis->sMembers($key); | ||
| 41 | - } catch (\RedisException $e) { | ||
| 42 | - return false; | 56 | + return $this->exec(fn($r) => $r->sAdd($key, $member), "sAdd"); |
| 43 | } | 57 | } |
| 44 | - $this->pool->put($redis); | ||
| 45 | - return $res; | 58 | + |
| 59 | + public function sMembers($key) | ||
| 60 | + { | ||
| 61 | + return $this->exec(fn($r) => $r->sMembers($key), "sMembers"); | ||
| 46 | } | 62 | } |
| 47 | 63 | ||
| 48 | - //移除集合成员 | ||
| 49 | public function sRem($key, $member) | 64 | public function sRem($key, $member) |
| 50 | { | 65 | { |
| 51 | - $redis = $this->pool->get(); | ||
| 52 | - try { | ||
| 53 | - $res = $redis->sRem($key, $member); | ||
| 54 | - } catch (\RedisException $e) { | ||
| 55 | - return false; | 66 | + return $this->exec(fn($r) => $r->sRem($key, $member), "sRem"); |
| 56 | } | 67 | } |
| 57 | 68 | ||
| 58 | - $this->pool->put($redis); | ||
| 59 | - return $res; | 69 | + // ------------------ 哈希 ------------------ |
| 70 | + public function hSet($key, $field, $value) | ||
| 71 | + { | ||
| 72 | + return $this->exec(fn($r) => $r->hSet($key, $field, $value), "hSet"); | ||
| 60 | } | 73 | } |
| 61 | 74 | ||
| 62 | - //哈希 键 字段 值 设置 | ||
| 63 | - public function hSet($key, $field, $value) | 75 | + public function hMSet($key, $field) |
| 64 | { | 76 | { |
| 65 | - $redis = $this->pool->get(); | ||
| 66 | - try { | ||
| 67 | - $res = $redis->hSet($key, $field, $value); | ||
| 68 | - } catch (\RedisException $e) { | ||
| 69 | - return false; | 77 | + return $this->exec(fn($r) => $r->hMSet($key, $field), "hMSet"); |
| 70 | } | 78 | } |
| 71 | - $this->pool->put($redis); | ||
| 72 | - return $res; | 79 | + |
| 80 | + public function hMGet($key, $field) | ||
| 81 | + { | ||
| 82 | + return $this->exec(fn($r) => $r->hMGet($key, $field), "hMGet"); | ||
| 73 | } | 83 | } |
| 74 | - //获取 键对应字段的值 | 84 | + |
| 75 | public function hGet($key, $field) | 85 | public function hGet($key, $field) |
| 76 | { | 86 | { |
| 77 | - $redis = $this->pool->get(); | ||
| 78 | - try { | ||
| 79 | - $res = $redis->hGet($key, $field); | ||
| 80 | - } catch (\RedisException $e) { | ||
| 81 | - return false; | 87 | + return $this->exec(fn($r) => $r->hGet($key, $field), "hGet"); |
| 82 | } | 88 | } |
| 83 | - $this->pool->put($redis); | ||
| 84 | - return $res; | ||
| 85 | - } | ||
| 86 | - //获取键所有字段和值 | 89 | + |
| 87 | public function hGetAll($key) | 90 | public function hGetAll($key) |
| 88 | { | 91 | { |
| 89 | - $redis = $this->pool->get(); | ||
| 90 | - try { | ||
| 91 | - $res = $redis->hGetAll($key); | ||
| 92 | - } catch (\RedisException $e) { | ||
| 93 | - return false; | 92 | + return $this->exec(fn($r) => $r->hGetAll($key), "hGetAll"); |
| 94 | } | 93 | } |
| 95 | - $this->pool->put($redis); | ||
| 96 | - return $res; | ||
| 97 | - } | ||
| 98 | - //删除一个值 | 94 | + |
| 99 | public function hDel($key, $field) | 95 | public function hDel($key, $field) |
| 100 | { | 96 | { |
| 101 | - $redis = $this->pool->get(); | ||
| 102 | - try { | ||
| 103 | - $res = $redis->hDel($key, $field); | ||
| 104 | - } catch (\RedisException $e) { | ||
| 105 | - return false; | ||
| 106 | - } | ||
| 107 | - $this->pool->put($redis); | ||
| 108 | - return $res; | 97 | + return $this->exec(fn($r) => $r->hDel($key, $field), "hDel"); |
| 109 | } | 98 | } |
| 110 | 99 | ||
| 111 | - /* | ||
| 112 | - * 发布订阅等 | ||
| 113 | - */ | ||
| 114 | - //将信息发送到指定的频道 | 100 | + // ------------------ 发布 ------------------ |
| 115 | public function publish($channel, $message) | 101 | public function publish($channel, $message) |
| 116 | { | 102 | { |
| 117 | - $redis = $this->pool->get(); | ||
| 118 | - try { | ||
| 119 | - $res = $redis->publish($channel, $message); | ||
| 120 | - } catch (\RedisException $e) { | ||
| 121 | - return false; | ||
| 122 | - } | ||
| 123 | - $this->pool->put($redis); | ||
| 124 | - return $res; | 103 | + return $this->exec(fn($r) => $r->publish($channel, $message), "publish"); |
| 125 | } | 104 | } |
| 126 | 105 | ||
| 106 | + // ------------------ 订阅模式(独立连接) ------------------ | ||
| 127 | public function subscribe($channels, callable $onMessage) | 107 | public function subscribe($channels, callable $onMessage) |
| 128 | { | 108 | { |
| 129 | $redis = $this->pool->get(); | 109 | $redis = $this->pool->get(); |
| 130 | $this->subRedis = $redis; | 110 | $this->subRedis = $redis; |
| 131 | try { | 111 | try { |
| 132 | - $res = $redis->subscribe($channels, function ($redis, $channel, $message) use ($onMessage) { | ||
| 133 | - call_user_func($onMessage, $redis, $channel, $message); | 112 | + return $redis->subscribe($channels, function ($redis, $channel, $message) use ($onMessage) { |
| 113 | + $onMessage($redis, $channel, $message); | ||
| 134 | }); | 114 | }); |
| 135 | } catch (\RedisException $e) { | 115 | } catch (\RedisException $e) { |
| 116 | + $this->logError("subscribe", $e); | ||
| 136 | return false; | 117 | return false; |
| 137 | - } | 118 | + } finally { |
| 138 | $this->subRedis = null; | 119 | $this->subRedis = null; |
| 139 | - $this->pool->put($redis); | ||
| 140 | - return $res; | 120 | + // 不放回池子,订阅是阻塞连接 |
| 121 | + } | ||
| 141 | } | 122 | } |
| 142 | 123 | ||
| 143 | public function psubscribe($channels, callable $onMessage) | 124 | public function psubscribe($channels, callable $onMessage) |
| @@ -145,14 +126,44 @@ class RedisCli | @@ -145,14 +126,44 @@ class RedisCli | ||
| 145 | $redis = $this->pool->get(); | 126 | $redis = $this->pool->get(); |
| 146 | $this->subRedis = $redis; | 127 | $this->subRedis = $redis; |
| 147 | try { | 128 | try { |
| 148 | - $res = $redis->psubscribe($channels, function ($redis, $pattern, $channel, $message) use ($onMessage) { | ||
| 149 | - call_user_func($onMessage, $pattern, $redis, $channel, $message); | 129 | + return $redis->psubscribe($channels, function ($redis, $pattern, $channel, $message) use ($onMessage) { |
| 130 | + $onMessage($pattern, $redis, $channel, $message); | ||
| 150 | }); | 131 | }); |
| 151 | } catch (\RedisException $e) { | 132 | } catch (\RedisException $e) { |
| 133 | + $this->logError("psubscribe", $e); | ||
| 152 | return false; | 134 | return false; |
| 153 | - } | 135 | + } finally { |
| 154 | $this->subRedis = null; | 136 | $this->subRedis = null; |
| 155 | - $this->pool->put($redis); | ||
| 156 | - return $res; | 137 | + // 不放回池子 |
| 138 | + } | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + // ------------------ 健康检查 ------------------ | ||
| 142 | + public function ping() | ||
| 143 | + { | ||
| 144 | + return $this->exec(fn($r) => $r->ping(), "ping"); | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + // ------------------ 错误日志 ------------------ | ||
| 148 | + private function logError($method, \RedisException $e) | ||
| 149 | + { | ||
| 150 | + if (function_exists('swoole_get_worker_id')) { | ||
| 151 | + $workerId = swoole_get_worker_id(); | ||
| 152 | + $processInfo = "WorkerID:{$workerId}"; | ||
| 153 | + } else { | ||
| 154 | + $pid = getmypid(); | ||
| 155 | + $uid = function_exists('posix_getuid') ? posix_getuid() : 'N/A'; | ||
| 156 | + $processInfo = "PID:{$pid}, UID:{$uid}"; | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + output(sprintf( | ||
| 160 | + "[RedisCli][%s][%s:%d][db:%d] %s failed: %s", | ||
| 161 | + $processInfo, | ||
| 162 | + $this->host, | ||
| 163 | + $this->port, | ||
| 164 | + $this->dbIndex, | ||
| 165 | + $method, | ||
| 166 | + $e->getMessage() | ||
| 167 | + )); | ||
| 157 | } | 168 | } |
| 158 | } | 169 | } |
| 1 | <?php | 1 | <?php |
| 2 | 2 | ||
| 3 | -namespace jiaoyin; | 3 | +namespace Jiaoyin; |
| 4 | 4 | ||
| 5 | class SimpleRequest | 5 | class SimpleRequest |
| 6 | { | 6 | { |
| @@ -11,7 +11,6 @@ class SimpleRequest | @@ -11,7 +11,6 @@ class SimpleRequest | ||
| 11 | public string $method; | 11 | public string $method; |
| 12 | public array $header; | 12 | public array $header; |
| 13 | public array $cookie; | 13 | public array $cookie; |
| 14 | - public string $rawContent; | ||
| 15 | 14 | ||
| 16 | public function __construct($data) | 15 | public function __construct($data) |
| 17 | { | 16 | { |
| @@ -22,14 +21,5 @@ class SimpleRequest | @@ -22,14 +21,5 @@ class SimpleRequest | ||
| 22 | $this->method = $data['method']; | 21 | $this->method = $data['method']; |
| 23 | $this->header = $data['header']; | 22 | $this->header = $data['header']; |
| 24 | $this->cookie = $data['cookie']; | 23 | $this->cookie = $data['cookie']; |
| 25 | - $this->rawContent = isset($data['rawContent']) ? $data['rawContent'] : ''; | ||
| 26 | - if ($this->method == 'POST') { | ||
| 27 | - if (count($this->post) == 0) { | ||
| 28 | - $data = json_decode($this->rawContent, true); | ||
| 29 | - if ($data) { | ||
| 30 | - $this->post = $data; | ||
| 31 | - } | ||
| 32 | - } | ||
| 33 | - } | ||
| 34 | } | 24 | } |
| 35 | } | 25 | } |
| 1 | <?php | 1 | <?php |
| 2 | - | ||
| 3 | -namespace jiaoyin; | ||
| 4 | - | ||
| 5 | -require_once __DIR__ . '/func.php'; | ||
| 6 | - | 2 | +namespace Jiaoyin; |
| 7 | use Swoole\Http\Request; | 3 | use Swoole\Http\Request; |
| 8 | use Swoole\Http\Response; | 4 | use Swoole\Http\Response; |
| 9 | use Swoole\Http\Server; | 5 | use Swoole\Http\Server; |
| 10 | -use function jiaoyin\output; | ||
| 11 | 6 | ||
| 12 | class SimpleServer | 7 | class SimpleServer |
| 13 | { | 8 | { |
| @@ -16,9 +11,8 @@ class SimpleServer | @@ -16,9 +11,8 @@ class SimpleServer | ||
| 16 | { | 11 | { |
| 17 | $this->httpServer = new Server($host, $port); | 12 | $this->httpServer = new Server($host, $port); |
| 18 | } | 13 | } |
| 19 | - public function router($url, $callback) | ||
| 20 | - { | ||
| 21 | - $this->httpServer->on('Request', function (Request $request, Response $response) use ($url, $callback) { | 14 | + public function router($url, $callback){ |
| 15 | + $this->httpServer->on('Request', function (Request $request, Response $response) use ($url, $callback){ | ||
| 22 | $requestInfo = [ | 16 | $requestInfo = [ |
| 23 | 'path' => $request->server['path_info'], | 17 | 'path' => $request->server['path_info'], |
| 24 | 'uri' => $request->server['request_uri'], | 18 | 'uri' => $request->server['request_uri'], |
| @@ -28,17 +22,16 @@ class SimpleServer | @@ -28,17 +22,16 @@ class SimpleServer | ||
| 28 | 'header' => $request->header ?: [], | 22 | 'header' => $request->header ?: [], |
| 29 | 'cookie' => $request->cookie ?: [], | 23 | 'cookie' => $request->cookie ?: [], |
| 30 | ]; | 24 | ]; |
| 31 | - output($requestInfo); | ||
| 32 | $simpleRequest = new SimpleRequest($requestInfo); | 25 | $simpleRequest = new SimpleRequest($requestInfo); |
| 33 | if ($simpleRequest->path == '/favicon.ico' || $simpleRequest->uri == '/favicon.ico') { | 26 | if ($simpleRequest->path == '/favicon.ico' || $simpleRequest->uri == '/favicon.ico') { |
| 34 | $response->end(); | 27 | $response->end(); |
| 35 | return; | 28 | return; |
| 36 | } | 29 | } |
| 37 | - if ($url == $simpleRequest->uri) { | 30 | + if($url == $simpleRequest->uri){ |
| 38 | $res = call_user_func($callback, $simpleRequest); | 31 | $res = call_user_func($callback, $simpleRequest); |
| 39 | - if (!$res) { | 32 | + if(!$res){ |
| 40 | $response->end(); | 33 | $response->end(); |
| 41 | - } else { | 34 | + }else{ |
| 42 | $response->end($res); | 35 | $response->end($res); |
| 43 | } | 36 | } |
| 44 | return; | 37 | return; |
| @@ -47,8 +40,8 @@ class SimpleServer | @@ -47,8 +40,8 @@ class SimpleServer | ||
| 47 | }); | 40 | }); |
| 48 | } | 41 | } |
| 49 | 42 | ||
| 50 | - public function start() | ||
| 51 | - { | 43 | + public function start(){ |
| 52 | $this->httpServer->start(); | 44 | $this->httpServer->start(); |
| 53 | } | 45 | } |
| 54 | } | 46 | } |
| 47 | + |
| 1 | <?php | 1 | <?php |
| 2 | - | ||
| 3 | -namespace jiaoyin; | ||
| 4 | - | ||
| 5 | -require_once __DIR__ . '/func.php'; | ||
| 6 | - | 2 | +namespace Jiaoyin; |
| 7 | use Swoole\Http\Request; | 3 | use Swoole\Http\Request; |
| 8 | use Swoole\Http\Response; | 4 | use Swoole\Http\Response; |
| 9 | use Swoole\Coroutine\Http\Server; | 5 | use Swoole\Coroutine\Http\Server; |
| 10 | -use function jiaoyin\output; | ||
| 11 | 6 | ||
| 12 | class SimpleServerCoroutine | 7 | class SimpleServerCoroutine |
| 13 | { | 8 | { |
| @@ -16,52 +11,35 @@ class SimpleServerCoroutine | @@ -16,52 +11,35 @@ class SimpleServerCoroutine | ||
| 16 | { | 11 | { |
| 17 | $this->httpServer = new Server($host, $port, $ssl); | 12 | $this->httpServer = new Server($host, $port, $ssl); |
| 18 | } | 13 | } |
| 19 | - public function router(array $method, $path, $callback) | ||
| 20 | - { | ||
| 21 | - $this->httpServer->handle($path, function (Request $request, Response $response) use ($method, $callback) { | 14 | + public function router($method, $path, $callback){ |
| 15 | + $this->httpServer->handle($path, function (Request $request, Response $response) use ($method,$callback) { | ||
| 22 | $m = $request->getMethod(); | 16 | $m = $request->getMethod(); |
| 23 | - foreach ($method as $k => $v) { | ||
| 24 | - $method[$k] = strtolower($v); | ||
| 25 | - } | ||
| 26 | - $m = strtolower($m); | ||
| 27 | - if (!in_array($m, $method)) { | 17 | + if(!in_array($m,$method)){ |
| 28 | $response->status(405); | 18 | $response->status(405); |
| 29 | - $data = [ | ||
| 30 | - 'code' => 405, | ||
| 31 | - 'msg' => 'Method Not Allowed', | ||
| 32 | - 'currentMethod' => $m, | ||
| 33 | - 'allowMethod' => $method, | ||
| 34 | - ]; | ||
| 35 | - $response->end(json_encode($data)); | 19 | + $response->end(); |
| 36 | return; | 20 | return; |
| 37 | } | 21 | } |
| 38 | $requestInfo = [ | 22 | $requestInfo = [ |
| 39 | - 'method' => $request->getMethod(), | ||
| 40 | 'path' => $request->server['path_info'], | 23 | 'path' => $request->server['path_info'], |
| 41 | 'uri' => $request->server['request_uri'], | 24 | 'uri' => $request->server['request_uri'], |
| 42 | 'get' => $request->get ?: [], | 25 | 'get' => $request->get ?: [], |
| 43 | 'post' => $request->post ?: [], | 26 | 'post' => $request->post ?: [], |
| 27 | + 'method' => $request->getMethod(), | ||
| 44 | 'header' => $request->header ?: [], | 28 | 'header' => $request->header ?: [], |
| 45 | 'cookie' => $request->cookie ?: [], | 29 | 'cookie' => $request->cookie ?: [], |
| 46 | - 'rawContent' => $request->rawContent() ?: '' | ||
| 47 | ]; | 30 | ]; |
| 48 | - output($requestInfo['method'], $requestInfo['path'], "GET:" . json_encode($requestInfo['get']), "POST:" . json_encode($requestInfo['post']), "rawContent:" . $requestInfo['rawContent']); | ||
| 49 | $simpleRequest = new SimpleRequest($requestInfo); | 31 | $simpleRequest = new SimpleRequest($requestInfo); |
| 50 | $res = call_user_func($callback, $simpleRequest); | 32 | $res = call_user_func($callback, $simpleRequest); |
| 51 | - if (empty($res)) { | ||
| 52 | - $response->end(json_encode(['code' => -1, 'msg' => 'nothing return'])); | ||
| 53 | - } else { | 33 | + if(empty($res)){ |
| 34 | + $response->end(json_encode(['code'=>-1,'msg'=>'nothing return'])); | ||
| 35 | + }else{ | ||
| 54 | $response->end($res); | 36 | $response->end($res); |
| 55 | } | 37 | } |
| 56 | }); | 38 | }); |
| 57 | } | 39 | } |
| 58 | 40 | ||
| 59 | - public function start() | ||
| 60 | - { | 41 | + public function start(){ |
| 61 | $this->httpServer->start(); | 42 | $this->httpServer->start(); |
| 62 | } | 43 | } |
| 63 | - public function stop() | ||
| 64 | - { | ||
| 65 | - $this->httpServer->shutdown(); | ||
| 66 | - } | ||
| 67 | } | 44 | } |
| 45 | + |
| 1 | <?php | 1 | <?php |
| 2 | 2 | ||
| 3 | -namespace jiaoyin; | 3 | +namespace Jiaoyin; |
| 4 | 4 | ||
| 5 | use Swoole\Coroutine; | 5 | use Swoole\Coroutine; |
| 6 | use Swoole\Coroutine\Http\Client; | 6 | use Swoole\Coroutine\Http\Client; |
| @@ -26,7 +26,7 @@ class Websocket | @@ -26,7 +26,7 @@ class Websocket | ||
| 26 | private int $timerPing = 0; | 26 | private int $timerPing = 0; |
| 27 | private string $desc = 'websocket未命名'; | 27 | private string $desc = 'websocket未命名'; |
| 28 | private int $pingCount = 0; | 28 | private int $pingCount = 0; |
| 29 | - private int $pingDelay = 10000; //ping tick时间 10s | 29 | + private int $pingDelay = 10000;//ping tick时间 10s |
| 30 | 30 | ||
| 31 | // 例如:wss://fstream.binance.com/stream、ws://175.178.36.217:9528/spot/stream | 31 | // 例如:wss://fstream.binance.com/stream、ws://175.178.36.217:9528/spot/stream |
| 32 | public function __construct($url, $desc = null) | 32 | public function __construct($url, $desc = null) |
| @@ -64,13 +64,9 @@ class Websocket | @@ -64,13 +64,9 @@ class Websocket | ||
| 64 | $this->client?->close(); | 64 | $this->client?->close(); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | - public function connect( | ||
| 68 | - callable $onOpen = null, | ||
| 69 | - callable $onMessage = null, | ||
| 70 | - callable $onClose = null, | ||
| 71 | - $onPing = null, | ||
| 72 | - $onPong = null | ||
| 73 | - ): void { | 67 | + public function connect(callable $onOpen = null, callable $onMessage = null, |
| 68 | + callable $onClose = null, $onPing = null, $onPong = null): void | ||
| 69 | + { | ||
| 74 | $this->close(); | 70 | $this->close(); |
| 75 | $this->pingCount = 0; | 71 | $this->pingCount = 0; |
| 76 | $this->onOpen = $onOpen; | 72 | $this->onOpen = $onOpen; |
| @@ -84,8 +80,8 @@ class Websocket | @@ -84,8 +80,8 @@ class Websocket | ||
| 84 | $ret = $client->upgrade($this->path); | 80 | $ret = $client->upgrade($this->path); |
| 85 | if ($ret) { | 81 | if ($ret) { |
| 86 | output($this->desc, "连接成功"); | 82 | output($this->desc, "连接成功"); |
| 87 | - $this->client = &$client; | ||
| 88 | - swoole_timer_after(50, function () use ($onOpen, $client) { | 83 | + $this->client=&$client; |
| 84 | + swoole_timer_after(50, function () use ($onOpen,$client) { | ||
| 89 | if ($onOpen) { | 85 | if ($onOpen) { |
| 90 | call_user_func($onOpen, $client); | 86 | call_user_func($onOpen, $client); |
| 91 | } | 87 | } |
| @@ -94,7 +90,7 @@ class Websocket | @@ -94,7 +90,7 @@ class Websocket | ||
| 94 | while ($client) { | 90 | while ($client) { |
| 95 | $frame = $client->recv($this->recvTimeout); | 91 | $frame = $client->recv($this->recvTimeout); |
| 96 | if (!$frame && $client->errCode != 60) { | 92 | if (!$frame && $client->errCode != 60) { |
| 97 | - output($this->desc, '错误码:' . $client->errCode . ",错误数据:", $frame); | 93 | + output($this->desc,'错误码:' . $client->errCode.",错误数据:", $frame); |
| 98 | break; | 94 | break; |
| 99 | } | 95 | } |
| 100 | $this->lastRecvTime = time(); | 96 | $this->lastRecvTime = time(); |
jiaoyin/WebsocketOk.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace Jiaoyin; | ||
| 4 | + | ||
| 5 | +use Swoole\Coroutine; | ||
| 6 | +use Swoole\Coroutine\Http\Client; | ||
| 7 | +use Swoole\WebSocket\Frame; | ||
| 8 | + | ||
| 9 | +class WebsocketOk | ||
| 10 | +{ | ||
| 11 | + private string $host; | ||
| 12 | + private string $path; | ||
| 13 | + private int $port; | ||
| 14 | + private bool $ssl; | ||
| 15 | + private int $recvTimeout = 60; | ||
| 16 | + public ?Client $client = null; | ||
| 17 | + public string $url = ''; | ||
| 18 | + private int $lastRecvTime = 0; | ||
| 19 | + private $onOpen = null; | ||
| 20 | + private $onMessage = null; | ||
| 21 | + private $onClose = null; | ||
| 22 | + private $onPing = null; | ||
| 23 | + private $onPong = null; | ||
| 24 | + private bool $pingState = true; | ||
| 25 | + private bool $reconnecting = false; //防频繁重连 | ||
| 26 | + private ?int $timerCheck = 0; | ||
| 27 | + private string $desc = 'websocket未命名'; | ||
| 28 | + private int $pingCount = 0; | ||
| 29 | + private int $pingDelay = 25000; // ping tick时间 | ||
| 30 | + | ||
| 31 | + public function __construct($url, $desc = null) | ||
| 32 | + { | ||
| 33 | + $this->url = $url; | ||
| 34 | + $pattern = '/(ws{1,2}):\/\/([\w\.-]+):*(\d*)([\/\-\w\.\?&=]*)/'; | ||
| 35 | + preg_match($pattern, $url, $result); | ||
| 36 | + $this->ssl = $result[1] === 'wss'; | ||
| 37 | + $this->host = $result[2]; | ||
| 38 | + $this->port = empty($result[3]) ? ($this->ssl ? 443 : 80) : (int)$result[3]; | ||
| 39 | + $this->path = empty($result[4]) ? '/' : $result[4]; | ||
| 40 | + if ($desc) { | ||
| 41 | + $this->desc = 'websocket ' . $desc; | ||
| 42 | + } | ||
| 43 | + output($this->host, $this->port, $this->ssl, $this->path); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + public function push($data): void | ||
| 47 | + { | ||
| 48 | + if ($this->client && $this->client->connected) { | ||
| 49 | + $this->client->push($data); | ||
| 50 | + } else { | ||
| 51 | + output('push error, client not connected'); | ||
| 52 | + } | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + public function close(): void | ||
| 56 | + { | ||
| 57 | + swoole_timer_clear($this->timerCheck); | ||
| 58 | + if ($this->client) { | ||
| 59 | + $this->client->close(); | ||
| 60 | + } | ||
| 61 | + $this->client = null; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + public function connect( | ||
| 65 | + callable $onOpen = null, | ||
| 66 | + callable $onMessage = null, | ||
| 67 | + callable $onClose = null, | ||
| 68 | + $onPing = null, | ||
| 69 | + $onPong = null | ||
| 70 | + ): void { | ||
| 71 | + $this->close(); | ||
| 72 | + $this->pingCount = 0; | ||
| 73 | + $this->onOpen = $onOpen; | ||
| 74 | + $this->onMessage = $onMessage; | ||
| 75 | + $this->onClose = $onClose; | ||
| 76 | + $this->onPing = $onPing; | ||
| 77 | + $this->onPong = $onPong; | ||
| 78 | + | ||
| 79 | + Coroutine::create(function () use ($onOpen, $onMessage, $onClose, $onPing, $onPong) { | ||
| 80 | + $client = new Client($this->host, $this->port, $this->ssl); | ||
| 81 | + $client->set(['timeout' => 5]); | ||
| 82 | + $ret = $client->upgrade($this->path); | ||
| 83 | + | ||
| 84 | + if ($ret) { | ||
| 85 | + output($this->desc, "连接成功"); | ||
| 86 | + $this->reconnecting = false; //解除重连状态 | ||
| 87 | + $this->client = &$client; | ||
| 88 | + swoole_timer_after(50, function () use ($onOpen, $client) { | ||
| 89 | + if ($onOpen) { | ||
| 90 | + call_user_func($onOpen, $client); | ||
| 91 | + } | ||
| 92 | + $this->sendPing(); | ||
| 93 | + }); | ||
| 94 | + | ||
| 95 | + while ($client) { | ||
| 96 | + $frame = $client->recv($this->recvTimeout); | ||
| 97 | + if (!$frame && $client->errCode !== 60) { | ||
| 98 | + output($this->desc, '错误码:' . $client->errCode . ',状态码:' . $client->statusCode . ',body:' . $client->body . ";错误数据:" . $frame); | ||
| 99 | + break; | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + $this->lastRecvTime = time(); | ||
| 103 | + | ||
| 104 | + if (is_object($frame) && get_class($frame) === Frame::class) { | ||
| 105 | + if ($frame->opcode === WEBSOCKET_OPCODE_TEXT) { | ||
| 106 | + $data = json_decode($frame->data, true); | ||
| 107 | + if (isset($data['event']) && $data['event'] === 'pong') { | ||
| 108 | + $this->recvPongData($data['ts'] ?? null); | ||
| 109 | + if ($onPong) { | ||
| 110 | + call_user_func($onPong, $data); | ||
| 111 | + } | ||
| 112 | + } else { | ||
| 113 | + $this->pingState = true; // 收到任何消息都算活跃 | ||
| 114 | + call_user_func($onMessage, $frame->data); | ||
| 115 | + } | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + if ($frame->opcode === WEBSOCKET_OPCODE_CLOSE) { | ||
| 119 | + output($this->desc, "服务器主动关闭连接,连接关闭"); | ||
| 120 | + break; | ||
| 121 | + } | ||
| 122 | + } | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + Coroutine::defer(function () use ($onClose) { | ||
| 126 | + output($this->desc, "协程退出"); | ||
| 127 | + if ($this->client) { | ||
| 128 | + $this->client->close(); | ||
| 129 | + } | ||
| 130 | + $this->client = null; | ||
| 131 | + if ($onClose) { | ||
| 132 | + call_user_func($onClose); | ||
| 133 | + } | ||
| 134 | + }); | ||
| 135 | + } else { | ||
| 136 | + if ($onClose) { | ||
| 137 | + call_user_func($onClose); | ||
| 138 | + } | ||
| 139 | + output($this->desc, "升级websocket连接失败,1s后重连"); | ||
| 140 | + swoole_timer_after(3000, function () { | ||
| 141 | + $this->connect($this->onOpen, $this->onMessage, $this->onClose, $this->onPing, $this->onPong); | ||
| 142 | + }); | ||
| 143 | + } | ||
| 144 | + }); | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + private function sendPing(): void | ||
| 148 | + { | ||
| 149 | + $pingData = 'ping'; | ||
| 150 | + $this->push($pingData); | ||
| 151 | + $this->pingState = false; | ||
| 152 | + | ||
| 153 | + $this->timerCheck = swoole_timer_after(15000, function () { | ||
| 154 | + if (!$this->pingState && $this->lastRecvTime < time() - 10 && !$this->reconnecting) { | ||
| 155 | + $this->reconnecting = true; | ||
| 156 | + output($this->desc, 'ping pong 超时且未收到数据,重新连接'); | ||
| 157 | + $this->connect($this->onOpen, $this->onMessage, $this->onClose, $this->onPing, $this->onPong); | ||
| 158 | + } else { | ||
| 159 | + if (!$this->client || !$this->client->connected) { | ||
| 160 | + output($this->desc, '客户端已断开,停止Ping'); | ||
| 161 | + return; | ||
| 162 | + } else { | ||
| 163 | + $this->sendPing(); | ||
| 164 | + } | ||
| 165 | + } | ||
| 166 | + }); | ||
| 167 | + } | ||
| 168 | + private function recvPongData($ts = null): void | ||
| 169 | + { | ||
| 170 | + $this->pingState = true; | ||
| 171 | + if ($ts) { | ||
| 172 | + $now = round(microtime(true) * 1000); | ||
| 173 | + $delay = $now - $ts; | ||
| 174 | + output($this->desc, "收到pong,延迟:{$delay}ms"); | ||
| 175 | + } | ||
| 176 | + } | ||
| 177 | +} |
jiaoyin/functions.php
0 → 100644
| 1 | +<?php | ||
| 2 | +namespace Jiaoyin; | ||
| 3 | +//格式化输出 | ||
| 4 | +use Swoole\Process; | ||
| 5 | +use Swoole\Event; | ||
| 6 | + | ||
| 7 | +function output(): void | ||
| 8 | +{ | ||
| 9 | + $args = func_get_args(); | ||
| 10 | + $outStr = '['.timeFormat('ms').']:'; | ||
| 11 | + foreach($args as $key => $value){ | ||
| 12 | + $value = is_array($value) ? json_encode($value) : $value; | ||
| 13 | + if(is_bool($value)){ | ||
| 14 | + $value = $value ? 'bool:true':'bool:false'; | ||
| 15 | + } | ||
| 16 | + $outStr .= count($args) - $key > 1 ? $value.' ' : $value; | ||
| 17 | + } | ||
| 18 | + echo $outStr.PHP_EOL; | ||
| 19 | +} | ||
| 20 | +//格式化时间 | ||
| 21 | +function timeFormat($type='s',$format='Y-m-d H:i:s'): string | ||
| 22 | +{ | ||
| 23 | + date_default_timezone_set('Asia/Shanghai'); | ||
| 24 | + $microTime = microtime(); | ||
| 25 | + list($msTime,$sTime) = explode(' ',$microTime); | ||
| 26 | + $timeStr = date($format,$sTime); | ||
| 27 | + if($type == 'ms'){ | ||
| 28 | + $timeStr .= '.'.sprintf("%03d",floor($msTime*1000)); | ||
| 29 | + } | ||
| 30 | + return $timeStr; | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +// 获取当前时间的微秒数 | ||
| 34 | +function getMicrotime(): float|int | ||
| 35 | +{ | ||
| 36 | + list($uSec, $sec) = explode(' ', microtime()); | ||
| 37 | + return $sec*1000+round($uSec*1000); | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +//获取精度 | ||
| 41 | +function getPrecision($number): int | ||
| 42 | +{ | ||
| 43 | + $count = 0; | ||
| 44 | + while($number < 1){ | ||
| 45 | + $number *= 10; | ||
| 46 | + $count += 1; | ||
| 47 | + } | ||
| 48 | + return $count; | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +function getIntervalUnit($interval,$type='s'): float|int | ||
| 52 | +{ | ||
| 53 | + $unitTime = 0; | ||
| 54 | + if($interval == '1m'){ | ||
| 55 | + $unitTime = 60; | ||
| 56 | + } | ||
| 57 | + if($interval == '5m'){ | ||
| 58 | + $unitTime = 60*5; | ||
| 59 | + } | ||
| 60 | + if($interval == '15m'){ | ||
| 61 | + $unitTime = 60*15; | ||
| 62 | + } | ||
| 63 | + if($interval == '1h'){ | ||
| 64 | + $unitTime = 60*60; | ||
| 65 | + } | ||
| 66 | + if($interval == '4h'){ | ||
| 67 | + $unitTime = 60*60*4; | ||
| 68 | + } | ||
| 69 | + if($interval == '1d'){ | ||
| 70 | + $unitTime = 60*60*24; | ||
| 71 | + } | ||
| 72 | + return $type == 's' ? $unitTime : $unitTime*1000; | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +//以守护进程运行 | ||
| 76 | +function runAsDaemon($callback,$isDaemon=true): void | ||
| 77 | +{ | ||
| 78 | + if($isDaemon){ | ||
| 79 | + Process::daemon(); | ||
| 80 | + $process = new Process(function ()use ($callback){ | ||
| 81 | + call_user_func($callback); | ||
| 82 | + Event::wait(); | ||
| 83 | + }); | ||
| 84 | + $process->start(); | ||
| 85 | + }else{ | ||
| 86 | + call_user_func($callback); | ||
| 87 | + Event::wait(); | ||
| 88 | + } | ||
| 89 | +} |
-
请 注册 或 登录 后发表评论