作者 karlet

feat:合并tanli的修改

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