正在显示
11 个修改的文件
包含
1440 行增加
和
12 行删除
db/MongoCli.php
0 → 100644
| 1 | +<?php | ||
| 2 | +namespace Jiaoyin; | ||
| 3 | + | ||
| 4 | +use Jiaoyin; | ||
| 5 | +use Jiaoyin\MongoPool; | ||
| 6 | + | ||
| 7 | +class MongoCli | ||
| 8 | +{ | ||
| 9 | + private $pool; | ||
| 10 | + private $database; | ||
| 11 | + public function __construct($host,$port,$username,$password,$database, $num = 20) | ||
| 12 | + { | ||
| 13 | + $this->database = $database; | ||
| 14 | + $userInfo = $username?"{$username}:{$password}@":""; | ||
| 15 | + $dsn = "mongodb://{$userInfo}{$host}:{$port}/{$database}"; | ||
| 16 | + $this->pool = new MongoPool($dsn,$num); | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + /* | ||
| 20 | + * 创建集合 | ||
| 21 | + */ | ||
| 22 | + public function createCollection($table,$createIndex=[]){ | ||
| 23 | + try { | ||
| 24 | + $mongodb = $this->pool->get(); | ||
| 25 | + $database = $mongodb->selectDatabase($this->database); | ||
| 26 | + $collections = $database->listCollections(['filter' => ['name' => $table]]); | ||
| 27 | + $collectionExists = iterator_count($collections) > 0; | ||
| 28 | + if (empty($collectionExists)) { | ||
| 29 | + // 集合不存在,创建集合并添加索引 | ||
| 30 | + $database->createCollection($table); | ||
| 31 | + $collection = $database->selectCollection($table); | ||
| 32 | + $collection->createIndex($createIndex); | ||
| 33 | + } | ||
| 34 | + $this->pool->push($mongodb); | ||
| 35 | + } catch (\Exception $e) { | ||
| 36 | + output($e->getMessage()); | ||
| 37 | + } | ||
| 38 | + } | ||
| 39 | + /* | ||
| 40 | + * 检查集合是否存在 | ||
| 41 | + * */ | ||
| 42 | + public function checkCollection($table): bool | ||
| 43 | + { | ||
| 44 | + try { | ||
| 45 | + $mongodb = $this->pool->get(); | ||
| 46 | + $database = $mongodb->selectDatabase($this->database); | ||
| 47 | + $collections = $database->listCollections(['filter' => ['name' => $table]]); | ||
| 48 | + $collectionExists = iterator_count($collections) > 0; | ||
| 49 | + $this->pool->push($mongodb); | ||
| 50 | + if (empty($collectionExists)) { | ||
| 51 | + // 集合不存在 | ||
| 52 | + return false; | ||
| 53 | + } | ||
| 54 | + return true; | ||
| 55 | + } catch (\Exception $e) { | ||
| 56 | + output($e->getMessage()); | ||
| 57 | + return false; | ||
| 58 | + } | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + /* | ||
| 62 | + * 写入数据 | ||
| 63 | + * $collection->insertOne(['name' => 'John Doe']); | ||
| 64 | + * */ | ||
| 65 | + public function insertOne($table,$data = []) | ||
| 66 | + { | ||
| 67 | + try { | ||
| 68 | + $mongodb = $this->pool->get(); | ||
| 69 | + $database = $mongodb->selectDatabase($this->database); | ||
| 70 | + $collection = $database->selectCollection($table); | ||
| 71 | + $result = $collection->insertOne($data); | ||
| 72 | + $this->pool->push($mongodb); | ||
| 73 | + return $result; | ||
| 74 | + } catch (\Exception $e) { | ||
| 75 | + output($e->getMessage()); | ||
| 76 | + return false; | ||
| 77 | + } | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + /* | ||
| 81 | + * 批量写入数据 | ||
| 82 | + * $collection->insertMany([['name' => 'John Doe'],['name' => 'John Doe']]); | ||
| 83 | + * */ | ||
| 84 | + public function insertAll($table,$data = []) | ||
| 85 | + { | ||
| 86 | + try { | ||
| 87 | + $mongodb = $this->pool->get(); | ||
| 88 | + $database = $mongodb->selectDatabase($this->database); | ||
| 89 | + $collection = $database->selectCollection($table); | ||
| 90 | + $result = $collection->insertMany($data); | ||
| 91 | + $this->pool->push($mongodb); | ||
| 92 | + return $result; | ||
| 93 | + } catch (\Exception $e) { | ||
| 94 | + output($e->getMessage()); | ||
| 95 | + return []; | ||
| 96 | + } | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + /* | ||
| 100 | + * 查询单条数据 | ||
| 101 | + * $collection->findOne(['name' => 'John Doe']); | ||
| 102 | + * */ | ||
| 103 | + public function findOne($table,$where = []) | ||
| 104 | + { | ||
| 105 | + try { | ||
| 106 | + $mongodb = $this->pool->get(); | ||
| 107 | + $database = $mongodb->selectDatabase($this->database); | ||
| 108 | + $collection = $database->selectCollection($table); | ||
| 109 | + $result = $collection->findOne($where); | ||
| 110 | + $this->pool->push($mongodb); | ||
| 111 | + return $result; | ||
| 112 | + } catch (\Exception $e) { | ||
| 113 | + output($e->getMessage()); | ||
| 114 | + return []; | ||
| 115 | + } | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + /* | ||
| 119 | + * 查询所有数据 | ||
| 120 | + * $collection->findOne(['name' => 'John Doe']); | ||
| 121 | + * ['t' => ['$gt' => 1]]; // 查询大于1的数据 | ||
| 122 | + * ['t' => ['$lt' => 1]]; // 查询小于1的数据 | ||
| 123 | + * ['t' => ['$gte' => 1]]; // 查询大于等于1的数据 | ||
| 124 | + * ['t' => ['$lte' => 1]]; // 查询小于等于1的数据 | ||
| 125 | + * $options = [ | ||
| 126 | + * 'sort' => ['field_name' => 1], // 1 表示升序,-1 表示降序, | ||
| 127 | + * 'limit' => 100, | ||
| 128 | + * ]; | ||
| 129 | + * */ | ||
| 130 | + public function findAll($table,$where = [], $options=[]) | ||
| 131 | + { | ||
| 132 | + try { | ||
| 133 | + $mongodb = $this->pool->get(); | ||
| 134 | + $database = $mongodb->selectDatabase($this->database); | ||
| 135 | + $collection = $database->selectCollection($table); | ||
| 136 | + $result = $collection->find($where,$options); | ||
| 137 | + $this->pool->push($mongodb); | ||
| 138 | + // 遍历结果集 | ||
| 139 | + $data = []; | ||
| 140 | + if ($result){ | ||
| 141 | + $arr = $result->toArray(); | ||
| 142 | + foreach ($arr as $item) { | ||
| 143 | + $data[] = $item->getArrayCopy(); | ||
| 144 | + } | ||
| 145 | + return $data; | ||
| 146 | + } | ||
| 147 | + return []; | ||
| 148 | + } catch (\Exception $e) { | ||
| 149 | + output($e->getMessage()); | ||
| 150 | + return []; | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + /* | ||
| 155 | + * 更新数据 | ||
| 156 | + * $collection->updateOne(['name' => 'John Doe'], ['$set' => ['age' => 31]]); | ||
| 157 | + * */ | ||
| 158 | + public function updateOne($table,$where = [],$upData = []) | ||
| 159 | + { | ||
| 160 | + try { | ||
| 161 | + $mongodb = $this->pool->get(); | ||
| 162 | + $database = $mongodb->selectDatabase($this->database); | ||
| 163 | + $collection = $database->selectCollection($table); | ||
| 164 | + $result = $collection->updateOne($where,$upData); | ||
| 165 | + $this->pool->push($mongodb); | ||
| 166 | + return $result; | ||
| 167 | + } catch (\Exception $e) { | ||
| 168 | + output($e->getMessage()); | ||
| 169 | + return []; | ||
| 170 | + } | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + /* | ||
| 174 | + * 删除数据 | ||
| 175 | + * $collection->deleteOne(['name' => 'John Doe']); | ||
| 176 | + * */ | ||
| 177 | + public function deleteOne($table,$where = []) | ||
| 178 | + { | ||
| 179 | + try { | ||
| 180 | + $mongodb = $this->pool->get(); | ||
| 181 | + $database = $mongodb->selectDatabase($this->database); | ||
| 182 | + $collection = $database->selectCollection($table); | ||
| 183 | + $result = $collection->deleteOne($where); | ||
| 184 | + $this->pool->push($mongodb); | ||
| 185 | + return $result; | ||
| 186 | + } catch (\Exception $e) { | ||
| 187 | + output($e->getMessage()); | ||
| 188 | + return []; | ||
| 189 | + } | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + /* | ||
| 193 | + * 批量删除数据 | ||
| 194 | + * $collection->deleteMany(['name' => 'John Doe']); | ||
| 195 | + * */ | ||
| 196 | + public function deleteBatch($table,$where = []) | ||
| 197 | + { | ||
| 198 | + try { | ||
| 199 | + $mongodb = $this->pool->get(); | ||
| 200 | + $database = $mongodb->selectDatabase($this->database); | ||
| 201 | + $collection = $database->selectCollection($table); | ||
| 202 | + $result = $collection->deleteMany($where); | ||
| 203 | + $this->pool->push($mongodb); | ||
| 204 | + return $result; | ||
| 205 | + } catch (\Exception $e) { | ||
| 206 | + output($e->getMessage()); | ||
| 207 | + return []; | ||
| 208 | + } | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | +} |
db/MongoPool.php
0 → 100644
| 1 | +<?php | ||
| 2 | +namespace Jiaoyin; | ||
| 3 | + | ||
| 4 | +use Jiaoyin; | ||
| 5 | +use Swoole\Coroutine; | ||
| 6 | +use Swoole\Coroutine\Channel; | ||
| 7 | +use MongoDB\Client; | ||
| 8 | +class MongoPool | ||
| 9 | +{ | ||
| 10 | + private $pool; | ||
| 11 | + private $maxSize; | ||
| 12 | + private $currentSize; | ||
| 13 | + private $mongoUri; | ||
| 14 | + private $options; | ||
| 15 | + | ||
| 16 | + public function __construct($mongoUri, $maxSize=20, $options = []) | ||
| 17 | + { | ||
| 18 | + $this->mongoUri = $mongoUri; | ||
| 19 | + $this->maxSize = $maxSize; | ||
| 20 | + $this->currentSize = 0; | ||
| 21 | + $this->options = $options; | ||
| 22 | + $this->pool = new Channel($maxSize); | ||
| 23 | + | ||
| 24 | + // 初始化连接池 | ||
| 25 | + for ($i = 0; $i < $maxSize; $i++) { | ||
| 26 | + $this->pool->push($this->createConnection()); | ||
| 27 | + } | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + private function createConnection() | ||
| 31 | + { | ||
| 32 | + $manager = new Client($this->mongoUri, $this->options); | ||
| 33 | + return $manager; | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + public function get() | ||
| 37 | + { | ||
| 38 | + while ($this->pool->isEmpty()){ | ||
| 39 | +// output('Mongodb连接池为空,等待释放连接'); | ||
| 40 | + Coroutine::sleep(0.1); | ||
| 41 | + } | ||
| 42 | + $connection = $this->pool->pop(); | ||
| 43 | + if (!$connection) { | ||
| 44 | + if ($this->currentSize < $this->maxSize) { | ||
| 45 | + // 如果连接池未满,则创建新连接 | ||
| 46 | + $connection = $this->createConnection(); | ||
| 47 | + $this->currentSize++; | ||
| 48 | + } else { | ||
| 49 | + // 连接池已满,等待其他协程释放连接 | ||
| 50 | + $connection = $this->pool->pop(); | ||
| 51 | + } | ||
| 52 | + } | ||
| 53 | + return $connection; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + public function push($connection) | ||
| 57 | + { | ||
| 58 | + // 检查连接是否有效,这里仅做示例,实际应用中可能需要更复杂的逻辑 | ||
| 59 | + if (is_object($connection) && get_class($connection) === Client::class) { | ||
| 60 | + $this->pool->push($connection); | ||
| 61 | + }else{ | ||
| 62 | + $this->currentSize -= 1; | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + public function close() | ||
| 67 | + { | ||
| 68 | + while (!$this->pool->isEmpty()) { | ||
| 69 | + $connection = $this->pool->pop(); | ||
| 70 | + if (is_resource($connection)) { | ||
| 71 | + $connection->close(); | ||
| 72 | + } | ||
| 73 | + } | ||
| 74 | + $this->currentSize = 0; | ||
| 75 | + } | ||
| 76 | +} |
| 1 | <?php | 1 | <?php |
| 2 | namespace Jiaoyin; | 2 | namespace Jiaoyin; |
| 3 | 3 | ||
| 4 | -class MysqlCli{ | ||
| 5 | - public function start(){ | ||
| 6 | - echo 'mysqlCli test'; | 4 | +use Jiaoyin; |
| 5 | +use Swoole\Database\MysqliConfig; | ||
| 6 | +use Swoole\Database\MysqliPool; | ||
| 7 | + | ||
| 8 | +class MysqlCli | ||
| 9 | +{ | ||
| 10 | + | ||
| 11 | + private $pool = null; | ||
| 12 | + private $prefix = '';// 前缀 | ||
| 13 | + | ||
| 14 | + private int $reconnectCount = 0; //period时间内重连次数 | ||
| 15 | + private int $period = 300; | ||
| 16 | + private int $lastReconnectTime = 0; | ||
| 17 | + private $connectConfig = null; | ||
| 18 | + | ||
| 19 | + public function __construct($host,$port,$database,$username,$password,$charset='utf8',$prefix='', $connectCount = 20) | ||
| 20 | + { | ||
| 21 | + $this->prefix = $prefix; | ||
| 22 | + $this->connectConfig = [ | ||
| 23 | + 'host' => $host, | ||
| 24 | + 'port' => $port, | ||
| 25 | + 'database' => $database, | ||
| 26 | + 'username' => $username, | ||
| 27 | + 'password' => $password, | ||
| 28 | + 'charset' => $charset, | ||
| 29 | + 'prefix' => $prefix, | ||
| 30 | + 'connectCount' => $connectCount, | ||
| 31 | + ]; | ||
| 32 | + $this->poolConnect(); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + private function poolConnect() | ||
| 36 | + { | ||
| 37 | + if($this->pool){ | ||
| 38 | + $this->pool->close(); | ||
| 39 | + } | ||
| 40 | + $this->pool = new MysqliPool((new MysqliConfig) | ||
| 41 | + ->withHost($this->connectConfig['host']) | ||
| 42 | + ->withPort($this->connectConfig['port']) | ||
| 43 | + ->withDbName($this->connectConfig['database']) | ||
| 44 | + ->withCharset($this->connectConfig['charset']) | ||
| 45 | + ->withUsername($this->connectConfig['username']) | ||
| 46 | + ->withPassword($this->connectConfig['password']) | ||
| 47 | + ,$this->connectConfig['connectCount'] | ||
| 48 | + ); | ||
| 49 | + $this->prefix = $this->connectConfig['prefix']; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + //执行sql | ||
| 53 | + public function execute($action, $sql) | ||
| 54 | + { | ||
| 55 | + $ret = false; | ||
| 56 | + $db = $this->pool->get(); | ||
| 57 | + try { | ||
| 58 | + $stmt = $db->query($sql); | ||
| 59 | + if ($stmt == false) { | ||
| 60 | + output($db->errno, $db->error); | ||
| 61 | + } else { | ||
| 62 | + if ($action == 'SELECT') { | ||
| 63 | + $ret = $stmt->fetch_all(); | ||
| 64 | + } else { | ||
| 65 | + $ret = $stmt; | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + } catch (\Exception $e) { | ||
| 69 | + $errorMsg = $e->getMessage(); | ||
| 70 | + output($sql); | ||
| 71 | + output('sql错误', $errorMsg); | ||
| 72 | + if ($errorMsg == 'MySQL server has gone away') { | ||
| 73 | + if ($this->reconnectCount >= 3 && time() - $this->lastReconnectTime < $this->period) { | ||
| 74 | + //period内重连超过3次,不继续重连但电话通知异常 | ||
| 75 | + output('数据库连接失效,并重连3次失败'); | ||
| 76 | + } else { | ||
| 77 | + //重连 | ||
| 78 | + output('重新创建mysql连接池', $this->reconnectCount); | ||
| 79 | + $this->pool->put($db); | ||
| 80 | + $this->poolConnect(); | ||
| 81 | + if (time() - $this->lastReconnectTime < $this->period) { | ||
| 82 | + $this->reconnectCount += 1; | ||
| 83 | + } else { | ||
| 84 | + $this->reconnectCount = 0; | ||
| 85 | + } | ||
| 86 | + $this->lastReconnectTime = time(); | ||
| 87 | + return $this->execute($action, $sql); | ||
| 88 | + } | ||
| 89 | + } else { | ||
| 90 | + output('数据库操作异常'); | ||
| 91 | + } | ||
| 92 | + } | ||
| 93 | + $this->pool->put($db); | ||
| 94 | + return $ret; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + //插入数据 | ||
| 98 | + public function insert($table, $data) | ||
| 99 | + { | ||
| 100 | + $sql = $this->parseInsert($table, $data); | ||
| 101 | + if (!$sql) { | ||
| 102 | + return false; | ||
| 103 | + } | ||
| 104 | + return $this->execute('INSERT', $sql); | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + //select数据 | ||
| 108 | + public function select($table, $where = [], $col = [], $orderBy = '', $limit = '') | ||
| 109 | + { | ||
| 110 | + | ||
| 111 | + $sql = $this->parseSelect($table, $where, $col, $orderBy, $limit); | ||
| 112 | + if (!$sql) { | ||
| 113 | + return false; | ||
| 114 | + } | ||
| 115 | + $data = $this->execute('SELECT', $sql); | ||
| 116 | + $newData = []; | ||
| 117 | + if (empty($col)) { | ||
| 118 | + $newsql = 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME="' . $this->parseTable($table) . '" ORDER BY `ORDINAL_POSITION` ASC'; | ||
| 119 | + $coldata = $this->execute('SELECT', $newsql); | ||
| 120 | + if (!$coldata) { | ||
| 121 | + return false; | ||
| 122 | + } else { | ||
| 123 | + foreach ($coldata as $key => $value) { | ||
| 124 | + $col[] = $value[0]; | ||
| 125 | + } | ||
| 126 | + } | ||
| 127 | + } | ||
| 128 | + foreach ($data as $key => $value) { | ||
| 129 | + $tmp = []; | ||
| 130 | + foreach ($col as $k => $v) { | ||
| 131 | + $tmp[$v] = $value[$k]; | ||
| 132 | + } | ||
| 133 | + $data[$key] = $tmp; | ||
| 134 | + } | ||
| 135 | + return $data; | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + public function update($table, $data, $where = []) | ||
| 139 | + { | ||
| 140 | + $sql = $this->parseUpdate($table, $where, $data); | ||
| 141 | + if (!$sql) { | ||
| 142 | + return false; | ||
| 143 | + } | ||
| 144 | + return $this->execute('UPDATE', $sql); | ||
| 7 | } | 145 | } |
| 146 | + | ||
| 147 | + //查找符合条件的一条记录 | ||
| 148 | + public function find($table, $where, $col = [], $orderBy = '') | ||
| 149 | + { | ||
| 150 | + $data = $this->select($table, $where, $col, $orderBy, 1); | ||
| 151 | + if ($data && isset($data[0])) { | ||
| 152 | + return $data['0']; | ||
| 153 | + } | ||
| 154 | + return $data; | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + //解析insert | ||
| 158 | + private function parseInsert($table, $data) | ||
| 159 | + { | ||
| 160 | + $table = $this->parseTable($table); | ||
| 161 | + if (!is_array($data)) { | ||
| 162 | + output('insert data 不合法'); | ||
| 163 | + return false; | ||
| 164 | + } | ||
| 165 | + $keys = array_keys($data); | ||
| 166 | + $values = array_values($data); | ||
| 167 | + foreach ($values as $key => $value) { | ||
| 168 | + $values[$key] = $this->transferValue($value); | ||
| 169 | + } | ||
| 170 | + $keysFormat = implode(',', $keys); | ||
| 171 | + $valuesFormat = implode(',', $values); | ||
| 172 | + $sql = "insert into {$table}({$keysFormat}) values({$valuesFormat})"; | ||
| 173 | + return $sql; | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + //解析select | ||
| 177 | + private function parseSelect($table, $where, $col, $orderBy, $limit) | ||
| 178 | + { | ||
| 179 | + $table = $this->parseTable($table); | ||
| 180 | + if (!empty($col) && !is_array($col)) { | ||
| 181 | + output('select where col 不合法'); | ||
| 182 | + return false; | ||
| 183 | + } | ||
| 184 | + $colTxt = empty($col) ? '*' : implode(',', $col); | ||
| 185 | + $whereTxt = $this->parseWhere($where); | ||
| 186 | + $orderByTxt = !empty($orderBy) && isset($orderBy[0]) && isset($orderBy[1]) ? 'order by ' . $orderBy[0] . ' ' . $orderBy[1] : ''; | ||
| 187 | + $limitTxt = !empty($limit) ? 'limit ' . $limit : ''; | ||
| 188 | + $sql = "select {$colTxt} from {$table} {$whereTxt} {$orderByTxt} {$limitTxt}"; | ||
| 189 | + return $sql; | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + //解析where | ||
| 193 | + private function parseWhere($where) | ||
| 194 | + { | ||
| 195 | + if (empty($where)) { | ||
| 196 | + return ''; | ||
| 197 | + } | ||
| 198 | + $whereTxt = 'where '; | ||
| 199 | + if (is_string($where)) { | ||
| 200 | + $whereTxt .= $where; | ||
| 201 | + } else if (is_array($where)) { | ||
| 202 | + $tmp = ''; | ||
| 203 | + foreach ($where as $key => $value) { | ||
| 204 | + if (!is_array($value) && !isset($value[1])) { | ||
| 205 | + output('where 不合法'); | ||
| 206 | + var_dump($where); | ||
| 207 | + return false; | ||
| 208 | + } | ||
| 209 | + $value[1] = $this->transferValue($value[1]); | ||
| 210 | + $symbol = isset($value[2]) ? $value[2] : '='; | ||
| 211 | + $tmp .= $value[0] . ' ' . $symbol . ' ' . $value[1]; | ||
| 212 | + $tmp .= $key < count($where) - 1 ? ' and ' : ''; | ||
| 213 | + } | ||
| 214 | + $whereTxt .= $tmp; | ||
| 215 | + } else { | ||
| 216 | + output('where 不合法'); | ||
| 217 | + var_dump($where); | ||
| 218 | + return false; | ||
| 219 | + } | ||
| 220 | + return $whereTxt; | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + //解析udpate | ||
| 224 | + private function parseUpdate($table, $where, $data) | ||
| 225 | + { | ||
| 226 | + $table = $this->parseTable($table); | ||
| 227 | + if (!is_array($data) || empty($data)) { | ||
| 228 | + output('更新数据不能为空'); | ||
| 229 | + return false; | ||
| 230 | + } | ||
| 231 | + $setTxt = 'set '; | ||
| 232 | + $count = 0; | ||
| 233 | + foreach ($data as $key => $value) { | ||
| 234 | + $setTxt .= $key . '='; | ||
| 235 | + $setTxt .= $this->transferValue($value); | ||
| 236 | + if ($count < count($data) - 1) { | ||
| 237 | + $setTxt .= ','; | ||
| 238 | + } | ||
| 239 | + $count += 1; | ||
| 240 | + } | ||
| 241 | + $whereTxt = $this->parseWhere($where); | ||
| 242 | + $sql = 'update ' . $table . ' ' . $setTxt . ' ' . $whereTxt; | ||
| 243 | + return $sql; | ||
| 244 | + } | ||
| 245 | + | ||
| 246 | + private function parseTable($table) | ||
| 247 | + { | ||
| 248 | + return $this->prefix . $table; | ||
| 249 | + } | ||
| 250 | + | ||
| 251 | + //值转义 | ||
| 252 | + private function transferValue($value) | ||
| 253 | + { | ||
| 254 | + $valueNew = null; | ||
| 255 | + if (is_string($value)) { | ||
| 256 | + $valueNew = '"' . $value . '"'; | ||
| 257 | + } else if ($value === null) { | ||
| 258 | + $valueNew = 'NULL'; | ||
| 259 | + } else { | ||
| 260 | + if (is_numeric($value) && strpos($value . '', 'E') !== false) { | ||
| 261 | + $value = number_format($value, 8); | ||
| 262 | + while (substr($value, -1) === '0') { | ||
| 263 | + $value = substr($value, 0, -1); | ||
| 264 | + } | ||
| 265 | + } | ||
| 266 | + $valueNew = $value; | ||
| 267 | + } | ||
| 268 | + return $valueNew; | ||
| 269 | + } | ||
| 270 | + | ||
| 8 | } | 271 | } |
db/RedisCli.php
0 → 100644
| 1 | +<?php | ||
| 2 | +namespace Jiaoyin; | ||
| 3 | + | ||
| 4 | +use Jiaoyin; | ||
| 5 | +use Swoole\Database\RedisConfig; | ||
| 6 | +use Swoole\Database\RedisPool; | ||
| 7 | + | ||
| 8 | +class RedisCli | ||
| 9 | +{ | ||
| 10 | + private $pool; | ||
| 11 | + public function __construct($host,$port,$password,$dbIndex, $num = 20) | ||
| 12 | + { | ||
| 13 | + $this->pool = new RedisPool((new RedisConfig) | ||
| 14 | + ->withHost($host) | ||
| 15 | + ->withPort($port) | ||
| 16 | + ->withAuth($password) | ||
| 17 | + ->withDbIndex($dbIndex) | ||
| 18 | + ->withTimeout(5) | ||
| 19 | + ,$num //默认64个连接池 | ||
| 20 | + ); | ||
| 21 | + } | ||
| 22 | + //集合存数据 | ||
| 23 | + public function sAdd($key,$member){ | ||
| 24 | + $redis = $this->pool->get(); | ||
| 25 | + try { | ||
| 26 | + $res = $redis->sAdd($key,$member); | ||
| 27 | + } catch (\RedisException $e) { | ||
| 28 | + return false; | ||
| 29 | + } | ||
| 30 | + $this->pool->put($redis); | ||
| 31 | + return $res; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + //获取集合 | ||
| 35 | + public function sMembers($key){ | ||
| 36 | + $redis = $this->pool->get(); | ||
| 37 | + try { | ||
| 38 | + $res = $redis->sMembers($key); | ||
| 39 | + } catch (\RedisException $e) { | ||
| 40 | + return false; | ||
| 41 | + } | ||
| 42 | + $this->pool->put($redis); | ||
| 43 | + return $res; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + //移除集合成员 | ||
| 47 | + public function sRem($key,$member){ | ||
| 48 | + $redis = $this->pool->get(); | ||
| 49 | + try { | ||
| 50 | + $res = $redis->sRem($key,$member); | ||
| 51 | + }catch (\RedisException $e){ | ||
| 52 | + return false; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + $this->pool->put($redis); | ||
| 56 | + return $res; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + //哈希 键 字段 值 设置 | ||
| 60 | + public function hSet($key,$field,$value){ | ||
| 61 | + $redis = $this->pool->get(); | ||
| 62 | + try { | ||
| 63 | + $res = $redis->hSet($key, $field, $value); | ||
| 64 | + } catch (\RedisException $e) { | ||
| 65 | + return false; | ||
| 66 | + } | ||
| 67 | + $this->pool->put($redis); | ||
| 68 | + return $res; | ||
| 69 | + } | ||
| 70 | + //获取 键对应字段的值 | ||
| 71 | + public function hGet($key,$field){ | ||
| 72 | + $redis = $this->pool->get(); | ||
| 73 | + try { | ||
| 74 | + $res = $redis->hGet($key, $field); | ||
| 75 | + } catch (\RedisException $e) { | ||
| 76 | + return false; | ||
| 77 | + } | ||
| 78 | + $this->pool->put($redis); | ||
| 79 | + return $res; | ||
| 80 | + } | ||
| 81 | + //获取键所有字段和值 | ||
| 82 | + public function hGetAll($key){ | ||
| 83 | + $redis = $this->pool->get(); | ||
| 84 | + try { | ||
| 85 | + $res = $redis->hGetAll($key); | ||
| 86 | + } catch (\RedisException $e) { | ||
| 87 | + return false; | ||
| 88 | + } | ||
| 89 | + $this->pool->put($redis); | ||
| 90 | + return $res; | ||
| 91 | + } | ||
| 92 | + //删除一个值 | ||
| 93 | + public function hDel($key,$field){ | ||
| 94 | + $redis = $this->pool->get(); | ||
| 95 | + try { | ||
| 96 | + $res = $redis->hDel($key, $field); | ||
| 97 | + } catch (\RedisException $e) { | ||
| 98 | + return false; | ||
| 99 | + } | ||
| 100 | + $this->pool->put($redis); | ||
| 101 | + return $res; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + /* | ||
| 105 | + * 发布订阅等 | ||
| 106 | + */ | ||
| 107 | + //将信息发送到指定的频道 | ||
| 108 | + public function publish($channel,$message){ | ||
| 109 | + $redis = $this->pool->get(); | ||
| 110 | + try { | ||
| 111 | + $res = $redis->publish($channel, $message); | ||
| 112 | + } catch (\RedisException $e) { | ||
| 113 | + return false; | ||
| 114 | + } | ||
| 115 | + $this->pool->put($redis); | ||
| 116 | + return $res; | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + public function subscribe($channels,callable $onMessage) | ||
| 120 | + { | ||
| 121 | + $redis = $this->pool->get(); | ||
| 122 | + try { | ||
| 123 | + $res = $redis->subscribe($channels,function($redis, $channel, $message)use($onMessage){ | ||
| 124 | + call_user_func($onMessage,$redis, $channel, $message); | ||
| 125 | + }); | ||
| 126 | + } catch (\RedisException $e) { | ||
| 127 | + return false; | ||
| 128 | + } | ||
| 129 | + $this->pool->put($redis); | ||
| 130 | + return $res; | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | +} |
exchange/BinanceFutures.php
0 → 100644
| 1 | +<?php | ||
| 2 | +namespace Jiaoyin; | ||
| 3 | + | ||
| 4 | +use Jiaoyin; | ||
| 5 | +use Jiaoyin\Curl; | ||
| 6 | + | ||
| 7 | +/* | ||
| 8 | + binance合约接口 | ||
| 9 | +*/ | ||
| 10 | +class BinanceFutures | ||
| 11 | +{ | ||
| 12 | + static private string $host = 'https://fapi.binance.com'; | ||
| 13 | + private string $apikey = ''; | ||
| 14 | + private string $secret = ''; | ||
| 15 | + | ||
| 16 | + public function __construct($apikey='', $secret='',$host='') | ||
| 17 | + { | ||
| 18 | + $this->apikey = $apikey; | ||
| 19 | + $this->secret = $secret; | ||
| 20 | + if(!empty($host)){ | ||
| 21 | + self::$host = $host; | ||
| 22 | + } | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + static private function createUrl($path): string | ||
| 26 | + { | ||
| 27 | + return self::$host . $path; | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + static private function createHeader($apikey): array | ||
| 31 | + { | ||
| 32 | + return ["X-MBX-APIKEY:" . $apikey]; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + static private function createSign($secret, $param) | ||
| 36 | + { | ||
| 37 | + $len = count($param); | ||
| 38 | + if ($len == 0) { | ||
| 39 | + output('param 为空,签名失败'); | ||
| 40 | + return $param; | ||
| 41 | + } | ||
| 42 | + $paramStr = http_build_query($param); | ||
| 43 | + return hash_hmac('sha256', $paramStr, $secret); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + private function requestAccount($method, $path, $param) | ||
| 47 | + { | ||
| 48 | + if(empty($this->apikey) || empty($this->secret)){ | ||
| 49 | + output('api 或 secret 为空'); | ||
| 50 | + return ['code'=>-1,'msg'=>'apikey or secret is empty']; | ||
| 51 | + } | ||
| 52 | + $url = $this->createUrl($path); | ||
| 53 | + $param['timestamp'] = getMicrotime(); | ||
| 54 | + $param['signature'] = $this->createSign($this->secret, $param); | ||
| 55 | + $header = $this->createHeader($this->apikey); | ||
| 56 | + if (!in_array(strtoupper($method), ['GET', 'POST', 'DELETE'])) { | ||
| 57 | + output('请求方法错误', $method, $path, $param); | ||
| 58 | + return 0; | ||
| 59 | + } | ||
| 60 | + $data = json_encode([]); | ||
| 61 | + if (strtoupper($method) == 'POST') { | ||
| 62 | + $data = Curl::httpPost($url, $param, $header); | ||
| 63 | + } | ||
| 64 | + if (strtoupper($method) == 'GET') { | ||
| 65 | + $data = Curl::httpGet($url, $param, $header); | ||
| 66 | + } | ||
| 67 | + if (strtoupper($method) == 'DELETE') { | ||
| 68 | + $data = Curl::httpDelete($url, $param, $header); | ||
| 69 | + } | ||
| 70 | + return json_decode($data, true); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + static private function requestMarket($method, $path, $param) | ||
| 74 | + { | ||
| 75 | + $url = self::createUrl($path); | ||
| 76 | + if (!in_array(strtoupper($method), ['GET', 'POST', 'DELETE'])) { | ||
| 77 | + output('请求方法错误', $method, $path, $param); | ||
| 78 | + return 0; | ||
| 79 | + } | ||
| 80 | + $data = json_encode([]); | ||
| 81 | + if (strtoupper($method) == 'POST') { | ||
| 82 | + $data = Curl::httpPost($url, $param); | ||
| 83 | + } | ||
| 84 | + if (strtoupper($method) == 'GET') { | ||
| 85 | + $data = Curl::httpGet($url, $param); | ||
| 86 | + } | ||
| 87 | + if (strtoupper($method) == 'DELETE') { | ||
| 88 | + $data = Curl::httpDelete($url, $param); | ||
| 89 | + } | ||
| 90 | + return json_decode($data, true); | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + public function cancelOrderById($param) | ||
| 94 | + { | ||
| 95 | + $path = '/fapi/v1/order'; | ||
| 96 | + $method = 'DELETE'; | ||
| 97 | + return $this->requestAccount($method, $path, $param); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + //下单 | ||
| 101 | + public function order($param) | ||
| 102 | + { | ||
| 103 | + $path = '/fapi/v1/order'; | ||
| 104 | + $method = 'POST'; | ||
| 105 | + return $this->requestAccount($method, $path, $param); | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + //获取或者延长listenKey | ||
| 109 | + public function listenKey($param = []) | ||
| 110 | + { | ||
| 111 | + $path = '/fapi/v1/listenKey'; | ||
| 112 | + $method = 'POST'; | ||
| 113 | + return $this->requestAccount($method, $path, $param); | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + //获取仓位 | ||
| 117 | + public function account($param = []) | ||
| 118 | + { | ||
| 119 | + $path = '/fapi/v2/account'; | ||
| 120 | + $method = 'GET'; | ||
| 121 | + return $this->requestAccount($method, $path, $param); | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + //查询历史订单 | ||
| 125 | + public function allOrders($param) | ||
| 126 | + { | ||
| 127 | + $path = '/fapi/v1/allOrders'; | ||
| 128 | + $method = 'GET'; | ||
| 129 | + return $this->requestAccount($method, $path, $param); | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + //查询持仓模式 | ||
| 133 | + public function positionSideDual($param = []) | ||
| 134 | + { | ||
| 135 | + $path = '/fapi/v1/positionSide/dual'; | ||
| 136 | + $method = 'GET'; | ||
| 137 | + return $this->requestAccount($method, $path, $param); | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + //设置持仓模式 | ||
| 141 | + public function setPositionSideDual($param) | ||
| 142 | + { | ||
| 143 | + $path = '/fapi/v1/positionSide/dual'; | ||
| 144 | + $method = 'POST'; | ||
| 145 | + return $this->requestAccount($method, $path, $param); | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + //查询资产 | ||
| 149 | + public function balance($param = []) | ||
| 150 | + { | ||
| 151 | + $path = '/fapi/v2/balance'; | ||
| 152 | + $method = 'GET'; | ||
| 153 | + return $this->requestAccount($method, $path, $param); | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + //查询杠杆层级 | ||
| 157 | + public function leverageBracket($param = []) | ||
| 158 | + { | ||
| 159 | + $path = '/fapi/v1/leverageBracket'; | ||
| 160 | + $method = 'GET'; | ||
| 161 | + return $this->requestAccount($method, $path, $param); | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + //查询用户持仓风险,杠杆倍数 | ||
| 165 | + public function positionRisk($param = []) | ||
| 166 | + { | ||
| 167 | + $path = '/fapi/v2/positionRisk'; | ||
| 168 | + $method = 'GET'; | ||
| 169 | + return $this->requestAccount($method, $path, $param); | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + //设置杠杆 | ||
| 173 | + public function leverage($param) | ||
| 174 | + { | ||
| 175 | + $path = '/fapi/v1/leverage'; | ||
| 176 | + $method = 'POST'; | ||
| 177 | + return $this->requestAccount($method, $path, $param); | ||
| 178 | + } | ||
| 179 | + //查询资金流水 | ||
| 180 | + public function income($param) | ||
| 181 | + { | ||
| 182 | + $path = '/fapi/v1/income'; | ||
| 183 | + $method = 'GET'; | ||
| 184 | + return $this->requestAccount($method, $path, $param); | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + /* | ||
| 188 | + * ================================== | ||
| 189 | + * 以下公共接口,无限apikey | ||
| 190 | + * ================================== | ||
| 191 | + */ | ||
| 192 | + //24小时成交数据 | ||
| 193 | + static public function ticker24hr($param = []) | ||
| 194 | + { | ||
| 195 | + $path = '/fapi/v1/ticker/24hr'; | ||
| 196 | + $method = 'GET'; | ||
| 197 | + return self::requestMarket($method, $path, $param); | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + //获取交易对和交易规则 | ||
| 201 | + static public function exchangeInfo($param = []) | ||
| 202 | + { | ||
| 203 | + $path = '/fapi/v1/exchangeInfo'; | ||
| 204 | + $method = 'GET'; | ||
| 205 | + return self::requestMarket($method, $path, $param); | ||
| 206 | + } | ||
| 207 | + //获取k线数据 | ||
| 208 | + static public function klines($param) | ||
| 209 | + { | ||
| 210 | + $path = '/fapi/v1/klines'; | ||
| 211 | + $method = 'GET'; | ||
| 212 | + return self::requestMarket($method, $path, $param); | ||
| 213 | + } | ||
| 214 | +} | ||
| 215 | + | ||
| 216 | +?> |
exchange/BinanceSpot.php
0 → 100644
| 1 | +<?php | ||
| 2 | +namespace Jiaoyin; | ||
| 3 | + | ||
| 4 | +use Jiaoyin; | ||
| 5 | +use Jiaoyin\Curl; | ||
| 6 | + | ||
| 7 | +/* | ||
| 8 | + binance现货接口 | ||
| 9 | +*/ | ||
| 10 | +class BinanceSpot | ||
| 11 | +{ | ||
| 12 | + static private string $host = 'https://api.binance.com'; | ||
| 13 | + private string $apikey = ''; | ||
| 14 | + private string $secret = ''; | ||
| 15 | + | ||
| 16 | + public function __construct($apikey='', $secret='',$host='') | ||
| 17 | + { | ||
| 18 | + $this->apikey = $apikey; | ||
| 19 | + $this->secret = $secret; | ||
| 20 | + if(!empty($host)){ | ||
| 21 | + self::$host = $host; | ||
| 22 | + } | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + static private function createUrl($path): string | ||
| 26 | + { | ||
| 27 | + return self::$host . $path; | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + static private function createHeader($apikey): array | ||
| 31 | + { | ||
| 32 | + return ["X-MBX-APIKEY:" . $apikey]; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + static private function createSign($secret, $param) | ||
| 36 | + { | ||
| 37 | + $len = count($param); | ||
| 38 | + if ($len == 0) { | ||
| 39 | + output('param 为空,签名失败'); | ||
| 40 | + return $param; | ||
| 41 | + } | ||
| 42 | + $paramStr = http_build_query($param); | ||
| 43 | + return hash_hmac('sha256', $paramStr, $secret); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + private function requestAccount($method, $path, $param) | ||
| 47 | + { | ||
| 48 | + $url = $this->createUrl($path); | ||
| 49 | + $param['timestamp'] = getMicrotime(); | ||
| 50 | + $param['signature'] = $this->createSign($this->secret, $param); | ||
| 51 | + $header = $this->createHeader($this->apikey); | ||
| 52 | + if (!in_array(strtoupper($method), ['GET', 'POST', 'DELETE'])) { | ||
| 53 | + output('请求方法错误', $method, $path, $param); | ||
| 54 | + return 0; | ||
| 55 | + } | ||
| 56 | + $data = json_encode([]); | ||
| 57 | + if (strtoupper($method) == 'POST') { | ||
| 58 | + $data = Curl::httpPost($url, $param, $header); | ||
| 59 | + } | ||
| 60 | + if (strtoupper($method) == 'GET') { | ||
| 61 | + $data = Curl::httpGet($url, $param, $header); | ||
| 62 | + } | ||
| 63 | + if (strtoupper($method) == 'DELETE') { | ||
| 64 | + $data = Curl::httpDelete($url, $param, $header); | ||
| 65 | + } | ||
| 66 | + return json_decode($data, true); | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + static private function requestMarket($method, $path, $param) | ||
| 70 | + { | ||
| 71 | + $url = self::createUrl($path); | ||
| 72 | + if (!in_array(strtoupper($method), ['GET', 'POST', 'DELETE'])) { | ||
| 73 | + output('请求方法错误', $method, $path, $param); | ||
| 74 | + return 0; | ||
| 75 | + } | ||
| 76 | + $data = json_encode([]); | ||
| 77 | + if (strtoupper($method) == 'POST') { | ||
| 78 | + $data = network\Curl::httpPost($url, $param); | ||
| 79 | + } | ||
| 80 | + if (strtoupper($method) == 'GET') { | ||
| 81 | + $data = network\Curl::httpGet($url, $param); | ||
| 82 | + } | ||
| 83 | + if (strtoupper($method) == 'DELETE') { | ||
| 84 | + $data = network\Curl::httpDelete($url, $param); | ||
| 85 | + } | ||
| 86 | + return json_decode($data, true); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + //查询用户万向划转历史 | ||
| 90 | + public function getTransfer($param) | ||
| 91 | + { | ||
| 92 | + $path = '/sapi/v1/asset/transfer'; | ||
| 93 | + $method = 'GET'; | ||
| 94 | + return $this->requestAccount($method, $path, $param); | ||
| 95 | + } | ||
| 96 | + //查询子账户划转历史 (仅适用子账户) | ||
| 97 | + public function subUserHistory($param) | ||
| 98 | + { | ||
| 99 | + $path = '/sapi/v1/sub-account/transfer/subUserHistory'; | ||
| 100 | + $method = 'GET'; | ||
| 101 | + return $this->requestAccount($method, $path, $param); | ||
| 102 | + } | ||
| 103 | + //合约资金划转 | ||
| 104 | + public function futuresTransfer($param) | ||
| 105 | + { | ||
| 106 | + $path = '/sapi/v1/futures/transfer'; | ||
| 107 | + $method = 'POST'; | ||
| 108 | + return $this->requestAccount($method, $path, $param); | ||
| 109 | + } | ||
| 110 | + //子账户向子账户划转 | ||
| 111 | + public function subToSub($param) | ||
| 112 | + { | ||
| 113 | + $path = '/sapi/v1/sub-account/transfer/subToSub'; | ||
| 114 | + $method = 'POST'; | ||
| 115 | + return $this->requestAccount($method, $path, $param); | ||
| 116 | + } | ||
| 117 | + //获取用户资产 | ||
| 118 | + public function getUserAsset($param) | ||
| 119 | + { | ||
| 120 | + $path = '/sapi/v3/asset/getUserAsset'; | ||
| 121 | + $method = 'POST'; | ||
| 122 | + return $this->requestAccount($method, $path, $param); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + | ||
| 126 | + /* | ||
| 127 | + * ================================== | ||
| 128 | + * 以下公共接口,无限apikey | ||
| 129 | + * ================================== | ||
| 130 | + */ | ||
| 131 | + //24小时成交数据 | ||
| 132 | +// static public function ticker24hr($param = []) | ||
| 133 | +// { | ||
| 134 | +// $path = '/fapi/v1/ticker/24hr'; | ||
| 135 | +// $method = 'GET'; | ||
| 136 | +// return self::requestMarket($method, $path, $param); | ||
| 137 | +// } | ||
| 138 | + | ||
| 139 | +} | ||
| 140 | + | ||
| 141 | +?> |
exchange/Test.php
已删除
100644 → 0
functions.php
0 → 100644
| 1 | +<?php | ||
| 2 | +namespace Jiaoyin; | ||
| 3 | +//格式化输出 | ||
| 4 | +function output(){ | ||
| 5 | + $args = func_get_args(); | ||
| 6 | + $outStr = '['.timeFormat('ms').']:'; | ||
| 7 | + foreach($args as $key => $value){ | ||
| 8 | + $value = is_array($value) ? json_encode($value) : $value; | ||
| 9 | + if(is_bool($value)){ | ||
| 10 | + $value = $value ? 'bool:true':'bool:false'; | ||
| 11 | + } | ||
| 12 | + $outStr .= count($args) - $key > 1 ? $value.' ' : $value; | ||
| 13 | + } | ||
| 14 | + echo $outStr.PHP_EOL; | ||
| 15 | +} | ||
| 16 | +//格式化时间 | ||
| 17 | +function timeFormat($type='s',$format='Y-m-d H:i:s'){ | ||
| 18 | + date_default_timezone_set('Asia/Shanghai'); | ||
| 19 | + $microTime = microtime(); | ||
| 20 | + list($msTime,$sTime) = explode(' ',$microTime); | ||
| 21 | + $timeStr = date($format,$sTime); | ||
| 22 | + if($type == 'ms'){ | ||
| 23 | + $timeStr .= '.'.sprintf("%03d",floor($msTime*1000)); | ||
| 24 | + } | ||
| 25 | + return $timeStr; | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +// 获取当前时间的微秒数 | ||
| 29 | +function getMicrotime() | ||
| 30 | +{ | ||
| 31 | + list($uSec, $sec) = explode(' ', microtime()); | ||
| 32 | + return $sec*1000+round($uSec*1000); | ||
| 33 | +} | ||
| 34 | + | ||
| 35 | +//获取精度 | ||
| 36 | +function getPrecision($number): int | ||
| 37 | +{ | ||
| 38 | + $count = 0; | ||
| 39 | + while($number < 1){ | ||
| 40 | + $number *= 10; | ||
| 41 | + $count += 1; | ||
| 42 | + } | ||
| 43 | + return $count; | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +function getIntervalUnit($interval,$type='s'){ | ||
| 47 | + $unitTime = 0; | ||
| 48 | + if($interval == '1m'){ | ||
| 49 | + $unitTime = 60; | ||
| 50 | + } | ||
| 51 | + if($interval == '5m'){ | ||
| 52 | + $unitTime = 60*5; | ||
| 53 | + } | ||
| 54 | + if($interval == '15m'){ | ||
| 55 | + $unitTime = 60*15; | ||
| 56 | + } | ||
| 57 | + if($interval == '1h'){ | ||
| 58 | + $unitTime = 60*60; | ||
| 59 | + } | ||
| 60 | + if($interval == '4h'){ | ||
| 61 | + $unitTime = 60*60*4; | ||
| 62 | + } | ||
| 63 | + if($interval == '1d'){ | ||
| 64 | + $unitTime = 60*60*24; | ||
| 65 | + } | ||
| 66 | + return $type == 's' ? $unitTime : $unitTime*1000; | ||
| 67 | +} |
network/Curl.php
0 → 100644
| 1 | +<?php | ||
| 2 | +namespace Jiaoyin; | ||
| 3 | + | ||
| 4 | +use Jiaoyin; | ||
| 5 | +class Curl{ | ||
| 6 | + | ||
| 7 | + // get 获取数据 | ||
| 8 | + static public function httpGet($url, $param=[], $header=[]){ | ||
| 9 | + if(empty($url)){ | ||
| 10 | + return false; | ||
| 11 | + } | ||
| 12 | + if(count($param) > 0){ | ||
| 13 | + $url = $url.'?'.http_build_query($param); | ||
| 14 | + } | ||
| 15 | + $ch = curl_init(); | ||
| 16 | + $ch = self::curlSet($ch,$url, $header); | ||
| 17 | + $output = curl_exec($ch); | ||
| 18 | + if($output === false){ | ||
| 19 | + echo 'Curl error: ' . curl_error($ch); | ||
| 20 | + } | ||
| 21 | + curl_close($ch); | ||
| 22 | + return $output; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + //post 获取数据 | ||
| 26 | + static public function httpPost($url, $param=[], $header=[]){ | ||
| 27 | + if(empty($url)){ | ||
| 28 | + return false; | ||
| 29 | + } | ||
| 30 | + if(count($param) > 0){ | ||
| 31 | + $url = $url.'?'.http_build_query($param); | ||
| 32 | + } | ||
| 33 | + $ch = curl_init(); | ||
| 34 | + $ch = self::curlSet($ch, $url, $header); | ||
| 35 | + curl_setopt($ch, CURLOPT_POST, 1); | ||
| 36 | + $output = curl_exec($ch); | ||
| 37 | + if($output === false){ | ||
| 38 | + echo 'Curl error: ' . curl_error($ch); | ||
| 39 | + } | ||
| 40 | + curl_close($ch); | ||
| 41 | + return $output; | ||
| 42 | + } | ||
| 43 | + //delete 请求 | ||
| 44 | + static public function httpDelete($url, $param=[], $header=[]){ | ||
| 45 | + if(empty($url)){ | ||
| 46 | + return false; | ||
| 47 | + } | ||
| 48 | + if(count($param) > 0){ | ||
| 49 | + $url = $url.'?'.http_build_query($param); | ||
| 50 | + } | ||
| 51 | + $ch = curl_init(); | ||
| 52 | + $ch = self::curlSet($ch, $url, $header); | ||
| 53 | + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); | ||
| 54 | + $output = curl_exec($ch); | ||
| 55 | + if($output === false){ | ||
| 56 | + echo 'Curl error: ' . curl_error($ch); | ||
| 57 | + } | ||
| 58 | + curl_close($ch); | ||
| 59 | + return $output; | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + //curl 参数设置 | ||
| 63 | + static private function curlSet($ch,$url, $header){ | ||
| 64 | + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true) ; | ||
| 65 | + curl_setopt($ch, CURLOPT_URL, $url); | ||
| 66 | + //参数为1表示传输数据,为0表示直接输出显示。 | ||
| 67 | + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | ||
| 68 | + //参数为0表示不带头文件,为1表示带头文件 | ||
| 69 | + curl_setopt($ch, CURLOPT_HEADER,0); | ||
| 70 | + if(!empty($header)){ | ||
| 71 | + curl_setopt($ch, CURLOPT_HTTPHEADER,$header); | ||
| 72 | + } | ||
| 73 | + // 关闭SSL验证 | ||
| 74 | + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); | ||
| 75 | + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); | ||
| 76 | + return $ch; | ||
| 77 | + } | ||
| 78 | +} |
network/Websocket.php
0 → 100644
| 1 | +<?php | ||
| 2 | +namespace Jiaoyin; | ||
| 3 | + | ||
| 4 | +use Jiaoyin; | ||
| 5 | +use Swoole\Coroutine; | ||
| 6 | +use Swoole\Coroutine\Http\Client; | ||
| 7 | +use Swoole\WebSocket\Frame; | ||
| 8 | + | ||
| 9 | +class Websocket | ||
| 10 | +{ | ||
| 11 | + private string $host = ''; | ||
| 12 | + private string $path = ''; | ||
| 13 | + private int $port = 443; | ||
| 14 | + private bool $ssl = true; | ||
| 15 | + private int $recvTimeout = 60; | ||
| 16 | + public ?Client $client = null; | ||
| 17 | + public string $url = ''; | ||
| 18 | + private $lastRecvTime = 0; | ||
| 19 | + private $onOpen = null; | ||
| 20 | + private $onMessage = null; | ||
| 21 | + private $onClose = null; | ||
| 22 | + private $onPing = null; | ||
| 23 | + private $onPong = null; | ||
| 24 | + private $pingState = true; | ||
| 25 | + private $timerCheck = 0; | ||
| 26 | + private $timerPing = 0; | ||
| 27 | + private $desc = 'websocket未命名'; | ||
| 28 | + private $pingCount = 0; | ||
| 29 | + private $pingDelay = 10000;//ping tick时间 10s | ||
| 30 | + | ||
| 31 | + // 例如:wss://fstream.binance.com/stream、ws://175.178.36.217:9528/spot/stream | ||
| 32 | + public function __construct($url, $desc = null) | ||
| 33 | + { | ||
| 34 | + $this->url = $url; | ||
| 35 | + $pattern = '/(ws{1,2}):\/\/([\w.a-zA-Z]+):*(\d*)([\/\-\wa-zA-Z?=.]*)/'; | ||
| 36 | + preg_match($pattern, $url, $result); | ||
| 37 | + $this->ssl = $result[1] == 'wss'; | ||
| 38 | + $this->host = $result[2]; | ||
| 39 | + if (empty($result[3])) { | ||
| 40 | + $this->port = $this->ssl ? 443 : 80; | ||
| 41 | + } else { | ||
| 42 | + $this->port = $result[3]; | ||
| 43 | + } | ||
| 44 | + $this->path = empty($result[4]) ? '/' : $result[4]; | ||
| 45 | + if ($desc) { | ||
| 46 | + $this->desc = 'websocket ' . $desc; | ||
| 47 | + } | ||
| 48 | + output($this->host,$this->port,$this->ssl,$this->path); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + public function push($data) | ||
| 52 | + { | ||
| 53 | + if ($this->client) { | ||
| 54 | + $this->client->push($data); | ||
| 55 | + } else { | ||
| 56 | + output('push error, client is null'); | ||
| 57 | + } | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + public function close() | ||
| 61 | + { | ||
| 62 | + swoole_timer_clear($this->timerPing); | ||
| 63 | + swoole_timer_clear($this->timerCheck); | ||
| 64 | + if ($this->client) { | ||
| 65 | + $this->client->close(); | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + public function connect(callable $onOpen = null, callable $onMessage = null, | ||
| 70 | + callable $onClose = null, $onPing = null, $onPong = null) | ||
| 71 | + { | ||
| 72 | + $this->close(); | ||
| 73 | + $this->pingCount = 0; | ||
| 74 | + $this->onOpen = $onOpen; | ||
| 75 | + $this->onMessage = $onMessage; | ||
| 76 | + $this->onClose = $onClose; | ||
| 77 | + $this->onPing = $onPing; | ||
| 78 | + $this->onPong = $onPong; | ||
| 79 | + Coroutine::create(function () use ($onOpen, $onMessage, $onClose, $onPing, $onPong) { | ||
| 80 | + $this->client = new Client($this->host, $this->port, $this->ssl); | ||
| 81 | + $this->client->set(['timeout' => 5]); | ||
| 82 | + $ret = $this->client->upgrade($this->path); | ||
| 83 | + if ($ret) { | ||
| 84 | + output($this->desc, "连接成功"); | ||
| 85 | + swoole_timer_after(50, function () use ($onOpen) { | ||
| 86 | + if ($onOpen) { | ||
| 87 | + call_user_func($onOpen, $this->client); | ||
| 88 | + } | ||
| 89 | + $this->sendPing(); | ||
| 90 | + }); | ||
| 91 | + while ($this->client) { | ||
| 92 | + $frame = $this->client->recv(60); | ||
| 93 | + if (!$frame && $this->client->errCode!=60) { | ||
| 94 | + output($this->desc, "错误数据", $frame); | ||
| 95 | + break; | ||
| 96 | + } | ||
| 97 | + $this->lastRecvTime = time(); | ||
| 98 | + if (is_object($frame) && get_class($frame) === Frame::class) { | ||
| 99 | + if ($frame->opcode == WEBSOCKET_OPCODE_PING) { | ||
| 100 | + $this->push(self::createPongData($frame->data)); | ||
| 101 | + if ($onPing) { | ||
| 102 | + call_user_func($onPing, $frame->data); | ||
| 103 | + } | ||
| 104 | + } | ||
| 105 | + if ($frame->opcode == WEBSOCKET_OPCODE_PONG) { | ||
| 106 | + $this->recvPongData($frame->data); | ||
| 107 | + if($onPong){ | ||
| 108 | + call_user_func($onPong, $frame->data); | ||
| 109 | + } | ||
| 110 | + } | ||
| 111 | + if ($frame->opcode == WEBSOCKET_OPCODE_TEXT) { | ||
| 112 | + call_user_func($onMessage, $frame->data); | ||
| 113 | + } | ||
| 114 | + if ($frame->opcode == WEBSOCKET_OPCODE_CLOSE) { | ||
| 115 | + output($this->desc, "服务器主动关闭连接,连接关闭"); | ||
| 116 | + break; | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | + \Swoole\Coroutine::defer(function () use ($onClose) { | ||
| 120 | + output($this->desc, "协程退出"); | ||
| 121 | + $this->client = null; | ||
| 122 | + if ($onClose) { | ||
| 123 | + call_user_func($onClose); | ||
| 124 | + } | ||
| 125 | + }); | ||
| 126 | + } | ||
| 127 | + } else { | ||
| 128 | + if($onClose){ | ||
| 129 | + call_user_func($onClose); | ||
| 130 | + } | ||
| 131 | + output($this->desc, "升级websocket连接失败,1s后重连"); | ||
| 132 | + swoole_timer_after(1000,function (){ | ||
| 133 | + $this->connect($this->onOpen, $this->onMessage, $this->onClose, $this->onPing, $this->onPong); | ||
| 134 | + }); | ||
| 135 | + } | ||
| 136 | + }); | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + static public function createPongData($data = null) | ||
| 140 | + { | ||
| 141 | + $frame = new Frame(); | ||
| 142 | + if ($data) { | ||
| 143 | + $frame->data = $data; | ||
| 144 | + } | ||
| 145 | + $frame->opcode = WEBSOCKET_OPCODE_PONG; | ||
| 146 | + return $frame; | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + static public function createPingData() | ||
| 150 | + { | ||
| 151 | + $frame = new Frame(); | ||
| 152 | + $frame->opcode = WEBSOCKET_OPCODE_PING; | ||
| 153 | + $frame->data = getMicrotime(); | ||
| 154 | + return $frame; | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + private function sendPing() | ||
| 158 | + { | ||
| 159 | + $this->push(self::createPingData()); | ||
| 160 | + $this->pingState = false; | ||
| 161 | + $this->timerCheck = swoole_timer_after(5000, function () { | ||
| 162 | + if (!$this->pingState) { | ||
| 163 | + output($this->desc, 'ping pong 超时,重新连接'); | ||
| 164 | + $this->connect($this->onOpen, $this->onMessage, $this->onClose, $this->onPing, $this->onPong); | ||
| 165 | + } | ||
| 166 | + }); | ||
| 167 | + } | ||
| 168 | + | ||
| 169 | + private function recvPongData($data) | ||
| 170 | + { | ||
| 171 | + $this->pingState = true; | ||
| 172 | + $this->pingCount += 1; | ||
| 173 | + if ($this->pingCount % 100 == 0) { | ||
| 174 | + output($this->desc, '连接稳定,已连接时间:', $this->pingCount * $this->pingDelay / 1000, '秒'); | ||
| 175 | + } | ||
| 176 | + swoole_timer_clear($this->timerCheck); | ||
| 177 | + $this->timerPing = swoole_timer_after($this->pingDelay, function () { | ||
| 178 | + $this->sendPing(); | ||
| 179 | + }); | ||
| 180 | + } | ||
| 181 | +} |
notice/Feishu.php
0 → 100644
| 1 | +<?php | ||
| 2 | +namespace Jiaoyin; | ||
| 3 | + | ||
| 4 | +use Jiaoyin; | ||
| 5 | +class Feishu{ | ||
| 6 | + //发送预警通知 | ||
| 7 | + static public function send($content) | ||
| 8 | + { | ||
| 9 | + $url = 'https://open.feishu.cn/open-apis/bot/v2/hook/f30bbb72-091c-499d-a577-43c45dceb158'; | ||
| 10 | + $content = [ | ||
| 11 | + 'msg_type'=>"text", | ||
| 12 | + 'content'=>[ | ||
| 13 | + 'text'=>$content | ||
| 14 | + ] | ||
| 15 | + ]; | ||
| 16 | + $con = json_encode($content); | ||
| 17 | + return self::send_post_json($url,$con); | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + static public function sendNoError($content) | ||
| 21 | + { | ||
| 22 | + $url = 'https://open.feishu.cn/open-apis/bot/v2/hook/a344e313-4d5d-4aa4-912b-dda37b2e3ee8'; | ||
| 23 | + $content = [ | ||
| 24 | + 'msg_type'=>"text", | ||
| 25 | + 'content'=>[ | ||
| 26 | + 'text'=>$content | ||
| 27 | + ] | ||
| 28 | + ]; | ||
| 29 | + $con = json_encode($content); | ||
| 30 | + return self::send_post_json($url,$con); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + static public function notice($content,$channel="warning") | ||
| 34 | + { | ||
| 35 | + $urls = [ | ||
| 36 | + 'follow' => 'https://open.feishu.cn/open-apis/bot/v2/hook/f30bbb72-091c-499d-a577-43c45dceb158', | ||
| 37 | + 'warning' => 'https://open.feishu.cn/open-apis/bot/v2/hook/d48c2e4a-0ef9-4684-88d4-e62d878fdaca', | ||
| 38 | + 'error' => 'https://open.feishu.cn/open-apis/bot/v2/hook/a344e313-4d5d-4aa4-912b-dda37b2e3ee8', | ||
| 39 | + ]; | ||
| 40 | + $url = $urls[$channel] ?? $urls['warning']; | ||
| 41 | + $content = [ | ||
| 42 | + 'msg_type'=>"text", | ||
| 43 | + 'content'=>[ | ||
| 44 | + 'text'=>$content | ||
| 45 | + ] | ||
| 46 | + ]; | ||
| 47 | + $con = json_encode($content); | ||
| 48 | + return self::send_post_json($url,$con); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + | ||
| 52 | + static public function send_post_json($url, $jsonStr) | ||
| 53 | + { | ||
| 54 | + $ch = curl_init(); | ||
| 55 | + curl_setopt($ch, CURLOPT_POST, 1); | ||
| 56 | + curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); | ||
| 57 | + curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); | ||
| 58 | + curl_setopt($ch, CURLOPT_URL, $url); | ||
| 59 | + curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonStr); | ||
| 60 | + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | ||
| 61 | + curl_setopt($ch, CURLOPT_HTTPHEADER, array( | ||
| 62 | + 'Content-Type: application/json; charset=utf-8', | ||
| 63 | + 'Content-Length: ' . strlen($jsonStr) | ||
| 64 | + ) | ||
| 65 | + ); | ||
| 66 | + $response = curl_exec($ch); | ||
| 67 | + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); | ||
| 68 | + curl_close($ch); | ||
| 69 | + return array($httpCode, $response); | ||
| 70 | + } | ||
| 71 | +} |
-
请 注册 或 登录 后发表评论