├── .idea ├── .name ├── encodings.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml ├── swoole-game.iml ├── vcs.xml └── workspace.xml ├── README.md ├── applications ├── Game │ ├── Chat.php │ ├── Config │ │ ├── Db.php │ │ ├── Server.php │ │ └── Store.php │ ├── Game.php │ ├── Lib │ │ ├── Autoloader.php │ │ ├── Context.php │ │ ├── Db.php │ │ ├── DbConnection.php │ │ ├── Store.php │ │ └── StoreDriver │ │ │ └── File.php │ ├── Login.php │ ├── Pay.php │ ├── Protocols │ │ ├── JsonProtocol.php │ │ ├── TextProtocol.php │ │ └── WebSocket.php │ ├── README.md │ └── Server │ │ ├── Member.php │ │ └── Statistic.php ├── Http │ ├── Config │ │ ├── Db.php │ │ ├── Server.php │ │ └── Store.php │ ├── Lib │ │ ├── Autoloader.php │ │ ├── Context.php │ │ ├── Db.php │ │ ├── DbConnection.php │ │ ├── Store.php │ │ └── StoreDriver │ │ │ └── File.php │ ├── Protocols │ │ └── JsonProtocol.php │ ├── Services │ │ ├── Online.php │ │ └── Zone.php │ └── index.php └── swoole-yaf │ ├── README.md │ ├── application │ ├── Bootstrap.php │ ├── controllers │ │ ├── Error.php │ │ ├── Form.php │ │ └── Index.php │ ├── favicon.ico.png │ ├── library │ │ └── readme.txt │ ├── models │ │ └── Sample.php │ ├── plugins │ │ └── Sample.php │ └── views │ │ ├── error │ │ └── error.phtml │ │ ├── form │ │ ├── form.phtml │ │ └── index.phtml │ │ └── index │ │ └── index.phtml │ ├── conf │ ├── application.ini │ └── routes.ini │ ├── server │ └── server.php │ └── yaf_classes.php ├── example ├── JsonProtocol.php ├── echo │ ├── client.php │ ├── eofClient.php │ ├── eofServer.php │ ├── event.php │ └── server.php ├── http │ └── index.php ├── member.php ├── mysql │ ├── async_mysql.php │ └── mysqlPool.php ├── process │ └── server.php ├── table │ ├── get.php │ └── set.php ├── task │ ├── client.php │ └── server.php ├── timer │ ├── basic.php │ └── server.php └── websocket │ └── server.php └── swoole-auto-complete-master ├── README.md ├── Swoole.php └── phpstorm └── php.jar /.idea/.name: -------------------------------------------------------------------------------- 1 | swoole-game -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/swoole-game.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | swoole-framework 2 | ================ 3 | 4 | swoole基础框架 5 | -------------------------------------------------------------------------------- /applications/Game/Chat.php: -------------------------------------------------------------------------------- 1 | set(['worker_num' => 4, 'user'=>'www', 'group'=>'www', 'daemonize'=>0]); 14 | 15 | //handshake成功之后回调, 和js的onopen对应 16 | $http->on('open', function($response) { 17 | echo "handshake success" . PHP_EOL; 18 | //print_r($response); 19 | }); 20 | 21 | //自定定握手规则,没有设置则用系统内置的(只支持version:13的) 22 | $http->on('handshake', function($request, $response) { 23 | print_r($request); 24 | 25 | if (!isset($request->header['sec-websocket-key'])) { 26 | //'Bad protocol implementation: it is not RFC6455.' 27 | $response->end(); 28 | return false; 29 | } 30 | 31 | if (0 === preg_match('#^[+/0-9A-Za-z]{21}[AQgw]==$#', $request->header['sec-websocket-key']) || 16 !== strlen(base64_decode($request->header['sec-websocket-key']))) { 32 | //Header Sec-WebSocket-Key is illegal; 33 | $response->end(); 34 | return false; 35 | } 36 | 37 | $headers = array( 38 | 'Upgrade' => 'websocket', 39 | 'Connection' => 'Upgrade', 40 | 'Sec-WebSocket-Accept' => ''. base64_encode(sha1($request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)), 41 | 'Sec-WebSocket-Version' => '13', 42 | 'KeedpAlive' => 'off', 43 | ); 44 | 45 | foreach($headers as $key => $val) { 46 | $response->header($key, $val); 47 | } 48 | $response->status(101); 49 | $response->end(); 50 | }); 51 | 52 | $http->on('message', function($response){ 53 | var_dump($response); 54 | 55 | $response->message($response->data); 56 | }); 57 | 58 | $http->on('request', function ($request, $response) { 59 | var_dump($request); 60 | $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); 61 | }); 62 | 63 | $http->on('close', function(){ 64 | echo "on close" . PHP_EOL; 65 | }); 66 | 67 | $http->start(); -------------------------------------------------------------------------------- /applications/Game/Config/Db.php: -------------------------------------------------------------------------------- 1 | select('name,age')->from('user')->where('age>12')->query(); 13 | * 等价于 14 | * $user_array = Db::instance('one_demo')->query('SELECT `name`,`age` FROM `one_demo` WHERE `age`>12'); 15 | * @var array 16 | */ 17 | public static $passport = array( 18 | 'host' => '127.0.0.1', 19 | 'port' => 3306, 20 | 'user' => 'root', 21 | 'password' => '622124', 22 | 'dbname' => 'passport', 23 | 'charset' => 'utf8', 24 | ); 25 | } -------------------------------------------------------------------------------- /applications/Game/Config/Server.php: -------------------------------------------------------------------------------- 1 | 4, 17 | //task worker进程数 18 | 'task_worker_num' => 8, 19 | //设置程序进入后台作为守护进程运行 20 | 'daemonize' => false, 21 | //每个worker进程允许处理的最大任务数 22 | 'max_request' => 10000, 23 | //'heartbeat_check_interval' => 60, 24 | 'dispatch_mode' => 2, 25 | 'debug_mode'=> 1 26 | ); 27 | 28 | public static $chat = array( 29 | //Worker进程数 30 | 'worker_num' => 4, 31 | //设置程序进入后台作为守护进程运行 32 | 'daemonize' => false, 33 | //每个worker进程允许处理的最大任务数 34 | 'max_request' => 10000, 35 | //'heartbeat_check_interval' => 60, 36 | 'dispatch_mode' => 2, 37 | 'debug_mode'=> 1 38 | ); 39 | } -------------------------------------------------------------------------------- /applications/Game/Config/Store.php: -------------------------------------------------------------------------------- 1 | serv = new swoole_server("0.0.0.0", 9500); 22 | 23 | //通过配置获取 24 | $this->serv->set(Server::$game); 25 | 26 | //注册Server的事件回调函数 27 | $this->serv->on('Start', array($this, 'onStart')); 28 | $this->serv->on('WorkerStart', array($this, 'onWorkerStart')); 29 | $this->serv->on('Connect', array($this, 'onConnect')); 30 | $this->serv->on('Receive', array($this, 'onReceive')); 31 | $this->serv->on('Close', array($this, 'onClose')); 32 | 33 | //绑定任务 34 | $this->serv->on('Task', array($this, 'onTask')); 35 | $this->serv->on('Finish', array($this, 'onFinish')); 36 | $this->serv->start(); 37 | } 38 | 39 | //主进程启动 40 | public function onStart($serv) { 41 | echo "Server is Running" . PHP_EOL; 42 | 43 | //管理进程的PID,通过向管理进程发送SIGUSR1信号可实现柔性重启 44 | echo $serv->manager_pid . PHP_EOL; 45 | 46 | //主进程的PID,通过向主进程发送SIGTERM信号可安全关闭服务器 47 | echo $serv->master_pid . PHP_EOL; 48 | 49 | //print_r($serv->stats()); 50 | 51 | } 52 | 53 | public function onWorkerStart($serv, $worker_id) { 54 | 55 | } 56 | 57 | public function onConnect($serv, $fd, $from_id ) {} 58 | 59 | /** 60 | * 服务端接收数据 61 | * 62 | * @param $serv swoole_server对象 63 | * @param $fd 连接的描述符 64 | * @param $from_id reactor的id,无用 65 | * @param $data 接收数据 66 | */ 67 | public function onReceive(swoole_server $serv, $fd, $from_id, $data) { 68 | //检测数据完整性 69 | if(JsonProtocol::check($data) != 0) { 70 | return; 71 | } 72 | 73 | $data = JsonProtocol::decode($data); 74 | 75 | //接收参数 76 | $class = $data['class']; 77 | $method = $data['method']; 78 | $params = $data['params']; 79 | $startTime = $this->microtimeFloat(); 80 | 81 | // 判断类对应文件是否载入 82 | if(!class_exists($class)) 83 | { 84 | $include_file = ROOT_DIR . "Server/$class.php"; 85 | if(is_file($include_file)) { 86 | require_once $include_file; 87 | } 88 | 89 | if(!class_exists($class)) { 90 | $code = 404; 91 | $msg = "class $class not found"; 92 | 93 | $result = array('code'=>$code, 'msg'=>$msg, 'data'=>null); 94 | 95 | $serv->send($fd, JsonProtocol::encode($result)); 96 | } 97 | } 98 | 99 | // 调用类的方法 100 | try { 101 | $ret = call_user_func_array(array(new $class, $method), $params); 102 | $code = $ret['code']; 103 | $msg = $ret['msg']; 104 | 105 | // 发送数据给客户端,调用成功,data下标对应的元素即为调用结果 106 | $serv->send($fd, JsonProtocol::encode($ret)); 107 | } catch(Exception $e) { 108 | // 发送数据给客户端,发生异常,调用失败 109 | $code = $e->getCode() ? $e->getCode() : 500; 110 | $msg = $e->getMessage(); 111 | $result = array('code'=>$code, 'msg'=>$msg, 'data'=>$e); 112 | 113 | $serv->send($fd, JsonProtocol::encode($result)); 114 | } 115 | 116 | //请求数据统计,放在task执行 117 | $executionTime = $this->microtimeFloat() - $startTime; 118 | $report = array( 119 | 'class' => $class, 120 | 'method' => $method, 121 | 'params' => json_encode($params), 122 | 'code' => $code, 123 | 'msg' => $msg, 124 | 'execution' => $executionTime, 125 | 'time' => time() 126 | ); 127 | $serv->task(json_encode($report)); 128 | } 129 | 130 | /** 131 | * @param $serv swoole_server对象 132 | * @param $task_id 任务ID 133 | * @param $from_id 来自于哪个worker进程 134 | * @param $data 任务内容 135 | * @return int 136 | */ 137 | public function onTask($serv, $task_id, $from_id, $data) { 138 | $data = json_decode($data, true); 139 | Statistic::report($data); 140 | return 1; 141 | } 142 | 143 | /** 144 | * @param $serv swoole_server对象 145 | * @param $task_id 任务ID 146 | * @param $data 任务结果 147 | */ 148 | public function onFinish($serv, $task_id, $data) { 149 | echo "Task {$task_id} finish" . PHP_EOL; 150 | } 151 | 152 | public function onClose($serv, $fd, $from_id ) { 153 | echo "Client {$fd} close connection" . PHP_EOL; 154 | } 155 | 156 | private function microtimeFloat() { 157 | list($usec, $sec) = explode(" ", microtime()); 158 | return ((float)$usec + (float)$sec); 159 | } 160 | } 161 | 162 | new Game(); -------------------------------------------------------------------------------- /applications/Game/Lib/Autoloader.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | class Db 8 | { 9 | /** 10 | * 实例数组 11 | * @var array 12 | */ 13 | protected static $instance = array(); 14 | 15 | /** 16 | * 获取实例 17 | * @param string $config_name 18 | * @throws \Exception 19 | */ 20 | public static function instance($config_name) 21 | { 22 | if(!isset(\Config\Db::$$config_name)) 23 | { 24 | echo "\\Config\\Db::$config_name not set\n"; 25 | throw new \Exception("\\Config\\Db::$config_name not set\n"); 26 | } 27 | 28 | if(empty(self::$instance[$config_name])) 29 | { 30 | $config = \Config\Db::$$config_name; 31 | self::$instance[$config_name] = new \Lib\DbConnection($config['host'], $config['port'], $config['user'], $config['password'], $config['dbname']); 32 | } 33 | return self::$instance[$config_name]; 34 | } 35 | 36 | /** 37 | * 关闭数据库实例 38 | * @param string $config_name 39 | */ 40 | public static function close($config_name) 41 | { 42 | if(isset(self::$instance[$config_name])) 43 | { 44 | self::$instance[$config_name]->closeConnection(); 45 | self::$instance[$config_name] = null; 46 | } 47 | } 48 | 49 | /** 50 | * 关闭所有数据库实例 51 | */ 52 | public static function closeAll() 53 | { 54 | foreach(self::$instance as $connection) 55 | { 56 | $connection->closeConnection(); 57 | self::$instance = array(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /applications/Game/Lib/DbConnection.php: -------------------------------------------------------------------------------- 1 | type = 'SELECT'; 173 | if(!is_array($cols)) 174 | { 175 | $cols = array($cols); 176 | } 177 | $this->cols($cols); 178 | return $this; 179 | } 180 | 181 | /** 182 | * 从哪个表删除 183 | * @param string $table 184 | * @return self 185 | */ 186 | public function delete($table) 187 | { 188 | $this->type = 'DELETE'; 189 | $this->table = $this->quoteName($table); 190 | $this->fromRaw($this->quoteName($table)); 191 | return $this; 192 | } 193 | 194 | /** 195 | * 更新哪个表 196 | * @param string $table 197 | */ 198 | public function update($table) 199 | { 200 | $this->type = 'UPDATE'; 201 | $this->table = $this->quoteName($table); 202 | return $this; 203 | } 204 | 205 | /** 206 | * 向哪个表插入 207 | * @param string $table 208 | */ 209 | public function insert($table) 210 | { 211 | $this->type = 'INSERT'; 212 | $this->table = $this->quoteName($table); 213 | return $this; 214 | } 215 | 216 | /** 217 | * 218 | * 设置 SQL_CALC_FOUND_ROWS 标记. 219 | * @param bool 220 | * @return self 221 | */ 222 | public function calcFoundRows($enable = true) 223 | { 224 | $this->setFlag('SQL_CALC_FOUND_ROWS', $enable); 225 | return $this; 226 | } 227 | 228 | /** 229 | * 设置 SQL_CACHE 标记 230 | * @param bool 231 | * @return self 232 | */ 233 | public function cache($enable = true) 234 | { 235 | $this->setFlag('SQL_CACHE', $enable); 236 | return $this; 237 | } 238 | 239 | /** 240 | * 设置 SQL_NO_CACHE 标记 241 | * @param bool 242 | * @return self 243 | */ 244 | public function noCache($enable = true) 245 | { 246 | $this->setFlag('SQL_NO_CACHE', $enable); 247 | return $this; 248 | } 249 | 250 | /** 251 | * 设置 STRAIGHT_JOIN 标记. 252 | * @param bool 253 | * @return self 254 | */ 255 | public function straightJoin($enable = true) 256 | { 257 | $this->setFlag('STRAIGHT_JOIN', $enable); 258 | return $this; 259 | } 260 | 261 | /** 262 | * 设置 HIGH_PRIORITY 标记 263 | * @param bool 264 | * @return self 265 | */ 266 | public function highPriority($enable = true) 267 | { 268 | $this->setFlag('HIGH_PRIORITY', $enable); 269 | return $this; 270 | } 271 | 272 | /** 273 | * 设置 SQL_SMALL_RESULT 标记 274 | * @param bool 275 | * @return self 276 | */ 277 | public function smallResult($enable = true) 278 | { 279 | $this->setFlag('SQL_SMALL_RESULT', $enable); 280 | return $this; 281 | } 282 | 283 | /** 284 | * 设置 SQL_BIG_RESULT 标记 285 | * @param bool 286 | * @return self 287 | */ 288 | public function bigResult($enable = true) 289 | { 290 | $this->setFlag('SQL_BIG_RESULT', $enable); 291 | return $this; 292 | } 293 | 294 | /** 295 | * 设置 SQL_BUFFER_RESULT 标记 296 | * @param bool 297 | * @return self 298 | */ 299 | public function bufferResult($enable = true) 300 | { 301 | $this->setFlag('SQL_BUFFER_RESULT', $enable); 302 | return $this; 303 | } 304 | 305 | /** 306 | * 设置 FOR UPDATE 标记 307 | * @param bool 308 | * @return self 309 | */ 310 | public function forUpdate($enable = true) 311 | { 312 | $this->for_update = (bool) $enable; 313 | return $this; 314 | } 315 | 316 | /** 317 | * 设置 DISTINCT 标记 318 | * @param bool 319 | * @return self 320 | */ 321 | public function distinct($enable = true) 322 | { 323 | $this->setFlag('DISTINCT', $enable); 324 | return $this; 325 | } 326 | 327 | /** 328 | * 设置 LOW_PRIORITY 标记 329 | * @param bool $enable 330 | * @return self 331 | */ 332 | public function lowPriority($enable = true) 333 | { 334 | $this->setFlag('LOW_PRIORITY', $enable); 335 | return $this; 336 | } 337 | 338 | /** 339 | * 设置 IGNORE 标记 340 | * @param bool $enable 341 | * @return self 342 | */ 343 | public function ignore($enable = true) 344 | { 345 | $this->setFlag('IGNORE', $enable); 346 | return $this; 347 | } 348 | 349 | /** 350 | * 设置 QUICK 标记 351 | * @param bool $enable 352 | * @return self 353 | */ 354 | public function quick($enable = true) 355 | { 356 | $this->setFlag('QUICK', $enable); 357 | return $this; 358 | } 359 | 360 | /** 361 | * 设置 DELAYED 标记 362 | * @param bool $enable 363 | * @return self 364 | */ 365 | public function delayed($enable = true) 366 | { 367 | $this->setFlag('DELAYED', $enable); 368 | return $this; 369 | } 370 | 371 | /** 372 | * 序列化 373 | * @return string 374 | */ 375 | public function __toString() 376 | { 377 | $union = ''; 378 | if ($this->union) { 379 | $union = implode(' ', $this->union) . ' '; 380 | } 381 | return $union . $this->build(); 382 | } 383 | 384 | /** 385 | * 设置每页多少条记录 386 | * @param int 387 | * @return self 388 | */ 389 | public function setPaging($paging) 390 | { 391 | $this->paging = (int) $paging; 392 | return $this; 393 | } 394 | 395 | /** 396 | * 获取每页多少条记录 397 | * @return int 398 | */ 399 | public function getPaging() 400 | { 401 | return $this->paging; 402 | } 403 | 404 | /** 405 | * 获取绑定在占位符上的值 406 | */ 407 | public function getBindValues() 408 | { 409 | switch($this->type) 410 | { 411 | case 'SELECT': 412 | return $this->getBindValuesSELECT(); 413 | case 'DELETE': 414 | case 'UPDATE': 415 | case 'INSERT': 416 | return $this->getBindValuesCOMMON(); 417 | default : 418 | throw new \Exception("type err"); 419 | } 420 | } 421 | 422 | /** 423 | * 获取绑定在占位符上的值 424 | * @return array 425 | */ 426 | public function getBindValuesSELECT() 427 | { 428 | $bind_values = $this->bind_values; 429 | $i = 1; 430 | foreach ($this->bind_where as $val) { 431 | $bind_values[$i] = $val; 432 | $i ++; 433 | } 434 | foreach ($this->bind_having as $val) { 435 | $bind_values[$i] = $val; 436 | $i ++; 437 | } 438 | return $bind_values; 439 | } 440 | 441 | /** 442 | * 443 | * SELECT选择哪些列 444 | * @param mixed 445 | * @return null 446 | */ 447 | protected function addColSELECT($key, $val) 448 | { 449 | if (is_string($key)) { 450 | $this->cols[$val] = $key; 451 | } else { 452 | $this->addColWithAlias($val); 453 | } 454 | } 455 | 456 | /** 457 | * SELECT增加选择的列 458 | * @param string 459 | * @return null 460 | */ 461 | protected function addColWithAlias($spec) 462 | { 463 | $parts = explode(' ', $spec); 464 | $count = count($parts); 465 | if ($count == 2) { 466 | $this->cols[$parts[1]] = $parts[0]; 467 | } elseif ($count == 3 && strtoupper($parts[1]) == 'AS') { 468 | $this->cols[$parts[2]] = $parts[0]; 469 | } else { 470 | $this->cols[] = $spec; 471 | } 472 | } 473 | 474 | /** 475 | * from 哪个表 476 | * @param string $table 477 | * @return self 478 | */ 479 | public function from($table) 480 | { 481 | return $this->fromRaw($this->quoteName($table)); 482 | } 483 | 484 | /** 485 | * from的表 486 | * @param string $table 487 | * @return self 488 | */ 489 | public function fromRaw($table) 490 | { 491 | $this->from[] = array($table); 492 | $this->from_key ++; 493 | return $this; 494 | } 495 | /** 496 | * 497 | * 子查询 498 | * @param string $table 499 | * @param string $name The alias name for the sub-select. 500 | * @return self 501 | */ 502 | public function fromSubSelect($table, $name) 503 | { 504 | $this->from[] = array( "($table) AS " . $this->quoteName($name)); 505 | $this->from_key ++; 506 | return $this; 507 | } 508 | 509 | 510 | /** 511 | * 增加join语句 512 | * @param string $join inner, left, natural 513 | * @param string $table 514 | * @param string $cond 515 | * @return self 516 | * @throws Exception 517 | */ 518 | public function join($table, $cond = null, $type = '') 519 | { 520 | return $this->joinInternal($type, $table, $cond); 521 | } 522 | 523 | /** 524 | * 增加join语句 525 | * @param string $join inner, left, natural 526 | * @param string $table 527 | * @param string $cond 528 | * @return self 529 | * @throws Exception 530 | */ 531 | protected function joinInternal($join, $table, $cond = null) 532 | { 533 | if (! $this->from) { 534 | throw new Exception('Cannot join() without from()'); 535 | } 536 | 537 | $join = strtoupper(ltrim("$join JOIN")); 538 | $table = $this->quoteName($table); 539 | $cond = $this->fixJoinCondition($cond); 540 | $this->from[$this->from_key][] = rtrim("$join $table $cond"); 541 | return $this; 542 | } 543 | 544 | /** 545 | * quote 546 | * @param string $cond 547 | * @return string 548 | * 549 | */ 550 | protected function fixJoinCondition($cond) 551 | { 552 | if (! $cond) { 553 | return; 554 | } 555 | 556 | $cond = $this->quoteNamesIn($cond); 557 | 558 | if (strtoupper(substr(ltrim($cond), 0, 3)) == 'ON ') { 559 | return $cond; 560 | } 561 | 562 | if (strtoupper(substr(ltrim($cond), 0, 6)) == 'USING ') { 563 | return $cond; 564 | } 565 | 566 | return 'ON ' . $cond; 567 | } 568 | 569 | /** 570 | * inner join 571 | * @param string $spec 572 | * @param string $cond 573 | * @return self 574 | * @throws Exception 575 | */ 576 | public function innerJoin($table, $cond = null) 577 | { 578 | return $this->joinInternal('INNER', $table, $cond); 579 | } 580 | 581 | /** 582 | * left join 583 | * @param string $table 584 | * @param string $cond 585 | * @return self 586 | * @throws Exception 587 | */ 588 | public function leftJoin($table, $cond = null) 589 | { 590 | return $this->joinInternal('LEFT', $table, $cond); 591 | } 592 | 593 | /** 594 | * right join 595 | * @param string $table 596 | * @param string $cond 597 | * @return self 598 | * @throws Exception 599 | */ 600 | public function rightJoin($table, $cond = null) 601 | { 602 | return $this->joinInternal('RIGHT', $table, $cond); 603 | } 604 | 605 | /** 606 | * joinSubSelect 607 | * @param string $join inner, left, natural 608 | * @param string $spec 609 | * @param string $name sub-select 的别名 610 | * @param string $cond 611 | * @return self 612 | * @throws Exception 613 | */ 614 | public function joinSubSelect($join, $spec, $name, $cond = null) 615 | { 616 | if (! $this->from) { 617 | throw new Exception('Cannot join() without from() first.'); 618 | } 619 | 620 | $join = strtoupper(ltrim("$join JOIN")); 621 | $name = $this->quoteName($name); 622 | $cond = $this->fixJoinCondition($cond); 623 | $this->from[$this->from_key][] = rtrim("$join ($spec) AS $name $cond"); 624 | return $this; 625 | } 626 | 627 | /** 628 | * group by 语句 629 | * @param array $cols 630 | * @return self 631 | */ 632 | public function groupBy(array $cols) 633 | { 634 | foreach ($cols as $col) { 635 | $this->group_by[] = $this->quoteNamesIn($col); 636 | } 637 | return $this; 638 | } 639 | 640 | /** 641 | * having 语句 642 | * @param string $cond 643 | * @return self 644 | */ 645 | public function having($cond) 646 | { 647 | $this->addClauseCondWithBind('having', 'AND', func_get_args()); 648 | return $this; 649 | } 650 | 651 | /** 652 | * or having 语句 653 | * @param string $cond The HAVING condition. 654 | * @return self 655 | */ 656 | public function orHaving($cond) 657 | { 658 | $this->addClauseCondWithBind('having', 'OR', func_get_args()); 659 | return $this; 660 | } 661 | 662 | /** 663 | * 设置每页的记录数量 664 | * @param int $page 665 | * @return self 666 | */ 667 | public function page($page) 668 | { 669 | $this->limit = 0; 670 | $this->offset = 0; 671 | 672 | $page = (int) $page; 673 | if ($page > 0) { 674 | $this->limit = $this->paging; 675 | $this->offset = $this->paging * ($page - 1); 676 | } 677 | return $this; 678 | } 679 | 680 | /** 681 | * union 682 | * @return self 683 | */ 684 | public function union() 685 | { 686 | $this->union[] = $this->build() . ' UNION'; 687 | $this->reset(); 688 | return $this; 689 | } 690 | 691 | /** 692 | * unionAll 693 | * @return self 694 | */ 695 | public function unionAll() 696 | { 697 | $this->union[] = $this->build() . ' UNION ALL'; 698 | $this->reset(); 699 | return $this; 700 | } 701 | 702 | /** 703 | * 重置 704 | * @return null 705 | */ 706 | protected function reset() 707 | { 708 | $this->resetFlags(); 709 | $this->cols = array(); 710 | $this->from = array(); 711 | $this->from_key = -1; 712 | $this->where = array(); 713 | $this->group_by = array(); 714 | $this->having = array(); 715 | $this->order_by = array(); 716 | $this->limit = 0; 717 | $this->offset = 0; 718 | $this->for_update = false; 719 | } 720 | 721 | /** 722 | * 清除所有数据 723 | * @return void 724 | */ 725 | protected function resetAll() 726 | { 727 | $this->union = array(); 728 | $this->for_update = false; 729 | $this->cols = array(); 730 | $this->from = array(); 731 | $this->from_key = -1; 732 | $this->group_by = array(); 733 | $this->having = array(); 734 | $this->bind_having = array(); 735 | $this->paging = 10; 736 | $this->bind_values = array(); 737 | $this->where = array(); 738 | $this->bind_where = array(); 739 | $this->order_by = array(); 740 | $this->limit = 0; 741 | $this->offset = 0; 742 | $this->flags = array(); 743 | $this->table = ''; 744 | $this->last_insert_id_names = array(); 745 | $this->col_values = array(); 746 | $this->returning = array(); 747 | $this->parameters = array(); 748 | } 749 | 750 | /** 751 | * 创建 SELECT SQL 752 | * @return string 753 | */ 754 | protected function buildSELECT() 755 | { 756 | return 'SELECT' 757 | . $this->buildFlags() 758 | . $this->buildCols() 759 | . $this->buildFrom() 760 | . $this->buildWhere() 761 | . $this->buildGroupBy() 762 | . $this->buildHaving() 763 | . $this->buildOrderBy() 764 | . $this->buildLimit() 765 | . $this->buildForUpdate(); 766 | } 767 | 768 | /** 769 | * 创建DELETE SQL 770 | */ 771 | protected function buildDELETE() 772 | { 773 | return 'DELETE' 774 | . $this->buildFlags() 775 | . $this->buildFrom() 776 | . $this->buildWhere() 777 | . $this->buildOrderBy() 778 | . $this->buildLimit() 779 | . $this->buildReturning(); 780 | } 781 | 782 | /** 783 | * 生成SELECT列语句 784 | * @return string 785 | * @throws Exception 786 | */ 787 | protected function buildCols() 788 | { 789 | if (! $this->cols) { 790 | throw new Exception('No columns in the SELECT.'); 791 | } 792 | 793 | $cols = array(); 794 | foreach ($this->cols as $key => $val) { 795 | if (is_int($key)) { 796 | $cols[] = $this->quoteNamesIn($val); 797 | } else { 798 | $cols[] = $this->quoteNamesIn("$val AS $key"); 799 | } 800 | } 801 | 802 | return $this->indentCsv($cols); 803 | } 804 | 805 | /** 806 | * 生成 FROM 语句. 807 | * @return string 808 | */ 809 | protected function buildFrom() 810 | { 811 | if (! $this->from) { 812 | return ''; 813 | } 814 | 815 | $refs = array(); 816 | foreach ($this->from as $from) { 817 | $refs[] = implode(' ', $from); 818 | } 819 | return ' FROM' . $this->indentCsv($refs); 820 | } 821 | 822 | /** 823 | * 生成 GROUP BY 语句. 824 | * @return string 825 | */ 826 | protected function buildGroupBy() 827 | { 828 | if (! $this->group_by) { 829 | return ''; 830 | } 831 | return ' GROUP BY' . $this->indentCsv($this->group_by); 832 | } 833 | 834 | /** 835 | * 生成 HAVING 语句. 836 | * @return string 837 | */ 838 | protected function buildHaving() 839 | { 840 | if (! $this->having) { 841 | return ''; 842 | } 843 | return ' HAVING' . $this->indent($this->having); 844 | } 845 | 846 | /** 847 | * 生成 FOR UPDATE 语句 848 | * @return string 849 | */ 850 | protected function buildForUpdate() 851 | { 852 | if (! $this->for_update) { 853 | return ''; 854 | } 855 | return ' FOR UPDATE'; 856 | } 857 | 858 | /** 859 | * where 860 | * @param string $cond 861 | * @param mixed ...$bind 862 | * @return self 863 | */ 864 | public function where($cond) 865 | { 866 | if(is_array($cond)) 867 | { 868 | foreach($cond as $key=>$val) 869 | { 870 | if(is_string($key)) 871 | { 872 | $this->addWhere('AND', array($key, $val)); 873 | } 874 | else 875 | { 876 | $this->addWhere('AND', array($val)); 877 | } 878 | } 879 | } 880 | else 881 | { 882 | $this->addWhere('AND', func_get_args()); 883 | } 884 | return $this; 885 | } 886 | 887 | /** 888 | * or where 889 | * @param string $cond 890 | * @param mixed ...$bind 891 | * @return self 892 | */ 893 | public function orWhere($cond) 894 | { 895 | if(is_array($con)) 896 | { 897 | foreach($con as $key=>$val) 898 | { 899 | if(is_string($key)) 900 | { 901 | $this->addWhere('OR', array($key, $val)); 902 | } 903 | else 904 | { 905 | $this->addWhere('OR', array($val)); 906 | } 907 | } 908 | } 909 | else 910 | { 911 | $this->addWhere('OR', func_get_args()); 912 | } 913 | return $this; 914 | } 915 | 916 | /** 917 | * limit 918 | * @param int $limit 919 | * @return self 920 | */ 921 | public function limit($limit) 922 | { 923 | $this->limit = (int) $limit; 924 | return $this; 925 | } 926 | 927 | /** 928 | * limit offset 929 | * @param int $offset 930 | * @return self 931 | */ 932 | public function offset($offset) 933 | { 934 | $this->offset = (int) $offset; 935 | return $this; 936 | } 937 | 938 | /** 939 | * orderby. 940 | * @param array $cols 941 | * @return self 942 | */ 943 | public function orderBy(array $cols) 944 | { 945 | return $this->addOrderBy($cols); 946 | } 947 | 948 | // -------------abstractquery---------- 949 | /** 950 | * 返回逗号分隔的字符串 951 | * @param array $list 952 | * @return string 953 | */ 954 | protected function indentCsv(array $list) 955 | { 956 | return ' ' . implode(',', $list); 957 | } 958 | 959 | /** 960 | * 返回空格分隔的字符串 961 | * @param array $list 962 | * @return string 963 | */ 964 | protected function indent(array $list) 965 | { 966 | return ' ' . implode(' ', $list); 967 | } 968 | 969 | /** 970 | * 批量为占位符绑定值 971 | * @param array $bind_values 972 | * @return self 973 | * 974 | */ 975 | public function bindValues(array $bind_values) 976 | { 977 | foreach ($bind_values as $key => $val) { 978 | $this->bindValue($key, $val); 979 | } 980 | return $this; 981 | } 982 | 983 | /** 984 | * 单个为占位符绑定值 985 | * @param string $name 986 | * @param mixed $value 987 | * @return self 988 | */ 989 | public function bindValue($name, $value) 990 | { 991 | $this->bind_values[$name] = $value; 992 | return $this; 993 | } 994 | 995 | /** 996 | * 生成flag 997 | * @return string 998 | */ 999 | protected function buildFlags() 1000 | { 1001 | if (! $this->flags) { 1002 | return ''; 1003 | } 1004 | return ' ' . implode(' ', array_keys($this->flags)); 1005 | } 1006 | 1007 | /** 1008 | * 设置 flag. 1009 | * @param string $flag 1010 | * @param bool $enable 1011 | * @return null 1012 | */ 1013 | protected function setFlag($flag, $enable = true) 1014 | { 1015 | if ($enable) { 1016 | $this->flags[$flag] = true; 1017 | } else { 1018 | unset($this->flags[$flag]); 1019 | } 1020 | } 1021 | 1022 | /** 1023 | * 重置flag 1024 | * @return null 1025 | */ 1026 | protected function resetFlags() 1027 | { 1028 | $this->flags = array(); 1029 | } 1030 | 1031 | /** 1032 | * 1033 | * 添加where语句 1034 | * @param string $andor 'AND' or 'OR 1035 | * @param array $conditions 1036 | * @return self 1037 | * 1038 | */ 1039 | protected function addWhere($andor, $conditions) 1040 | { 1041 | $this->addClauseCondWithBind('where', $andor, $conditions); 1042 | return $this; 1043 | } 1044 | 1045 | /** 1046 | * 添加条件和绑定值 1047 | * @param string $clause where 、having等 1048 | * @param string $andor AND、OR等 1049 | * @param array $conditions 1050 | * @return null 1051 | */ 1052 | protected function addClauseCondWithBind($clause, $andor, $conditions) 1053 | { 1054 | $cond = array_shift($conditions); 1055 | $cond = $this->quoteNamesIn($cond); 1056 | 1057 | $bind =& $this->{"bind_{$clause}"}; 1058 | foreach ($conditions as $value) { 1059 | $bind[] = $value; 1060 | } 1061 | 1062 | $clause =& $this->$clause; 1063 | if ($clause) { 1064 | $clause[] = "$andor $cond"; 1065 | } else { 1066 | $clause[] = $cond; 1067 | } 1068 | } 1069 | 1070 | /** 1071 | * 生成where语句 1072 | * @return string 1073 | */ 1074 | protected function buildWhere() 1075 | { 1076 | if (! $this->where) { 1077 | return ''; 1078 | } 1079 | return ' WHERE' . $this->indent($this->where); 1080 | } 1081 | 1082 | /** 1083 | * 增加order by 1084 | * @param array $spec The columns and direction to order by. 1085 | * @return self 1086 | */ 1087 | protected function addOrderBy(array $spec) 1088 | { 1089 | foreach ($spec as $col) { 1090 | $this->order_by[] = $this->quoteNamesIn($col); 1091 | } 1092 | return $this; 1093 | } 1094 | 1095 | /** 1096 | * 生成order by 语句 1097 | * @return string 1098 | */ 1099 | protected function buildOrderBy() 1100 | { 1101 | if (! $this->order_by) { 1102 | return ''; 1103 | } 1104 | return ' ORDER BY' . $this->indentCsv($this->order_by); 1105 | } 1106 | 1107 | /** 1108 | * 生成limit语句 1109 | * @return string 1110 | */ 1111 | protected function buildLimit() 1112 | { 1113 | $has_limit = $this->type == 'DELETE' || $this->type == 'UPDATE'; 1114 | $has_offset = $this->type == 'SELECT'; 1115 | 1116 | if ($has_offset && $this->limit) { 1117 | $clause = " LIMIT {$this->limit}"; 1118 | if ($this->offset) { 1119 | $clause .= " OFFSET {$this->offset}"; 1120 | } 1121 | return $clause; 1122 | } elseif ($has_limit && $this->limit) { 1123 | return " LIMIT {$this->limit}"; 1124 | } 1125 | return ''; 1126 | } 1127 | 1128 | /** 1129 | * Quotes 1130 | * @param string $spec 1131 | * @return string|array 1132 | */ 1133 | public function quoteName($spec) 1134 | { 1135 | $spec = trim($spec); 1136 | $seps = array(' AS ', ' ', '.'); 1137 | foreach ($seps as $sep) { 1138 | $pos = strripos($spec, $sep); 1139 | if ($pos) { 1140 | return $this->quoteNameWithSeparator($spec, $sep, $pos); 1141 | } 1142 | } 1143 | return $this->replaceName($spec); 1144 | } 1145 | 1146 | /** 1147 | * 指定分隔符的Quotes 1148 | * @param string $spec 1149 | * @param string $sep 1150 | * @param string $pos 1151 | * @return string 1152 | */ 1153 | protected function quoteNameWithSeparator($spec, $sep, $pos) 1154 | { 1155 | $len = strlen($sep); 1156 | $part1 = $this->quoteName(substr($spec, 0, $pos)); 1157 | $part2 = $this->replaceName(substr($spec, $pos + $len)); 1158 | return "{$part1}{$sep}{$part2}"; 1159 | } 1160 | 1161 | /** 1162 | * Quotes "table.col" 格式的字符串 1163 | * @param string $text 1164 | * @return string|array 1165 | */ 1166 | public function quoteNamesIn($text) 1167 | { 1168 | $list = $this->getListForQuoteNamesIn($text); 1169 | $last = count($list) - 1; 1170 | $text = null; 1171 | foreach ($list as $key => $val) { 1172 | if (($key+1) % 3) { 1173 | $text .= $this->quoteNamesInLoop($val, $key == $last); 1174 | } 1175 | } 1176 | return $text; 1177 | } 1178 | 1179 | /** 1180 | * 返回quote元素列表 1181 | * @param string $text 1182 | * @return array 1183 | */ 1184 | protected function getListForQuoteNamesIn($text) 1185 | { 1186 | $apos = "'"; 1187 | $quot = '"'; 1188 | return preg_split( 1189 | "/(($apos+|$quot+|\\$apos+|\\$quot+).*?\\2)/", 1190 | $text, 1191 | -1, 1192 | PREG_SPLIT_DELIM_CAPTURE 1193 | ); 1194 | } 1195 | 1196 | /** 1197 | * 循环quote 1198 | * @param string $val 1199 | * @param bool $is_last 1200 | * @return string 1201 | */ 1202 | protected function quoteNamesInLoop($val, $is_last) 1203 | { 1204 | if ($is_last) { 1205 | return $this->replaceNamesAndAliasIn($val); 1206 | } 1207 | return $this->replaceNamesIn($val); 1208 | } 1209 | 1210 | /** 1211 | * 1212 | * 替换成别名 1213 | * @param string $val 1214 | * @return string 1215 | */ 1216 | protected function replaceNamesAndAliasIn($val) 1217 | { 1218 | $quoted = $this->replaceNamesIn($val); 1219 | $pos = strripos($quoted, ' AS '); 1220 | if ($pos) { 1221 | $alias = $this->replaceName(substr($quoted, $pos + 4)); 1222 | $quoted = substr($quoted, 0, $pos) . " AS $alias"; 1223 | } 1224 | return $quoted; 1225 | } 1226 | 1227 | /** 1228 | * Quotes name 1229 | * @param string $name 1230 | * @return string 1231 | */ 1232 | protected function replaceName($name) 1233 | { 1234 | $name = trim($name); 1235 | if ($name == '*') { 1236 | return $name; 1237 | } 1238 | return '`'. $name.'`'; 1239 | } 1240 | 1241 | /** 1242 | * Quotes 1243 | * @param string $text 1244 | * @return string|array 1245 | */ 1246 | protected function replaceNamesIn($text) 1247 | { 1248 | $is_string_literal = strpos($text, "'") !== false 1249 | || strpos($text, '"') !== false; 1250 | if ($is_string_literal) { 1251 | return $text; 1252 | } 1253 | 1254 | $word = "[a-z_][a-z0-9_]+"; 1255 | 1256 | $find = "/(\\b)($word)\\.($word)(\\b)/i"; 1257 | 1258 | $repl = '$1`$2`.`$3`$4'; 1259 | 1260 | $text = preg_replace($find, $repl, $text); 1261 | 1262 | return $text; 1263 | } 1264 | 1265 | // ---------- insert -------------- 1266 | /** 1267 | * 设置 `table.column` 与 last-insert-id 的映射 1268 | * @param array $insert_id_names 1269 | */ 1270 | public function setLastInsertIdNames(array $last_insert_id_names) 1271 | { 1272 | $this->last_insert_id_names = $last_insert_id_names; 1273 | } 1274 | 1275 | /** 1276 | * insert into. 1277 | * @param string $into 1278 | * @return self 1279 | */ 1280 | public function into($table) 1281 | { 1282 | $this->table = $this->quoteName($table); 1283 | return $this; 1284 | } 1285 | 1286 | /** 1287 | * 生成INSERT 语句 1288 | * @return string 1289 | */ 1290 | protected function buildINSERT() 1291 | { 1292 | return 'INSERT' 1293 | . $this->buildFlags() 1294 | . $this->buildInto() 1295 | . $this->buildValuesForInsert() 1296 | . $this->buildReturning(); 1297 | } 1298 | 1299 | /** 1300 | * 生成 INTO 语句 1301 | * @return string 1302 | */ 1303 | protected function buildInto() 1304 | { 1305 | return " INTO " . $this->table; 1306 | } 1307 | 1308 | /** 1309 | * PDO::lastInsertId() 1310 | * @param string $col 1311 | * @return mixed 1312 | */ 1313 | public function getLastInsertIdName($col) 1314 | { 1315 | $key = str_replace('`', '', $this->table) . '.' . $col; 1316 | if (isset($this->last_insert_id_names[$key])) { 1317 | return $this->last_insert_id_names[$key]; 1318 | } 1319 | } 1320 | 1321 | /** 1322 | * 1323 | * 设置一列,如果有第二各参数,则把第二个参数绑定在占位符上 1324 | * @param string $col 1325 | * @param mixed $val 1326 | * @return self 1327 | */ 1328 | public function col($col) 1329 | { 1330 | return call_user_func_array(array($this, 'addCol'), func_get_args()); 1331 | } 1332 | 1333 | /** 1334 | * 设置多列 1335 | * @param array $cols 1336 | * @return self 1337 | */ 1338 | public function cols(array $cols) 1339 | { 1340 | if($this->type == 'SELECT') 1341 | { 1342 | foreach ($cols as $key => $val) 1343 | { 1344 | $this->addColSELECT($key, $val); 1345 | } 1346 | return $this; 1347 | } 1348 | return $this->addCols($cols); 1349 | } 1350 | 1351 | /** 1352 | * 直接设置列的值 1353 | * @param string $col 1354 | * @param string $value 1355 | * @return self 1356 | */ 1357 | public function set($col, $value) 1358 | { 1359 | return $this->setCol($col, $value); 1360 | } 1361 | 1362 | /** 1363 | * 为INSERT语句绑定值 1364 | * @return string 1365 | */ 1366 | protected function buildValuesForInsert() 1367 | { 1368 | return ' ('.$this->indentCsv(array_keys($this->col_values)).') VALUES (' . $this->indentCsv(array_values($this->col_values)) . ')'; 1369 | } 1370 | 1371 | // ------update------- 1372 | /** 1373 | * 更新哪个表 1374 | * @param string $table 1375 | * @return self 1376 | */ 1377 | public function table($table) 1378 | { 1379 | $this->table = $this->quoteName($table); 1380 | return $this; 1381 | } 1382 | 1383 | /** 1384 | * 生成完整SQL语句 1385 | * @return string 1386 | */ 1387 | protected function build() 1388 | { 1389 | switch($this->type) 1390 | { 1391 | case 'DELETE': 1392 | return $this->buildDELETE(); 1393 | case 'INSERT': 1394 | return $this->buildINSERT(); 1395 | case 'UPDATE': 1396 | return $this->buildUPDATE(); 1397 | case 'SELECT': 1398 | return $this->buildSELECT(); 1399 | } 1400 | throw new \Exception("type empty"); 1401 | } 1402 | 1403 | /** 1404 | * 生成更新的SQL语句 1405 | */ 1406 | protected function buildUPDATE() 1407 | { 1408 | return 'UPDATE' 1409 | . $this->buildFlags() 1410 | . $this->buildTable() 1411 | . $this->buildValuesForUpdate() 1412 | . $this->buildWhere() 1413 | . $this->buildOrderBy() 1414 | . $this->buildLimit() 1415 | . $this->buildReturning(); 1416 | 1417 | } 1418 | 1419 | /** 1420 | * 哪个表 1421 | * @return null 1422 | */ 1423 | protected function buildTable() 1424 | { 1425 | return " {$this->table}"; 1426 | } 1427 | 1428 | /** 1429 | * 为更新语句绑定值 1430 | * @return string 1431 | */ 1432 | protected function buildValuesForUpdate() 1433 | { 1434 | $values = array(); 1435 | foreach ($this->col_values as $col => $value) { 1436 | $values[] = "{$col} = {$value}"; 1437 | } 1438 | return ' SET' . $this->indentCsv($values); 1439 | } 1440 | 1441 | // ----------Dml--------------- 1442 | /** 1443 | * 获取绑定的值 1444 | * @return array 1445 | */ 1446 | public function getBindValuesCOMMON() 1447 | { 1448 | $bind_values = $this->bind_values; 1449 | $i = 1; 1450 | foreach ($this->bind_where as $val) { 1451 | $bind_values[$i] = $val; 1452 | $i ++; 1453 | } 1454 | return $bind_values; 1455 | } 1456 | 1457 | /** 1458 | * 设置列 1459 | * @param string $col 1460 | * @param mixed $val 1461 | * @return self 1462 | */ 1463 | protected function addCol($col) 1464 | { 1465 | $key = $this->quoteName($col); 1466 | $this->col_values[$key] = ":$col"; 1467 | $args = func_get_args(); 1468 | if (count($args) > 1) { 1469 | $this->bindValue($col, $args[1]); 1470 | } 1471 | return $this; 1472 | } 1473 | 1474 | /** 1475 | * 设置多个列 1476 | * @param array $cols 1477 | * @return self 1478 | */ 1479 | protected function addCols(array $cols) 1480 | { 1481 | foreach ($cols as $key => $val) { 1482 | if (is_int($key)) { 1483 | $this->addCol($val); 1484 | } else { 1485 | $this->addCol($key, $val); 1486 | } 1487 | } 1488 | return $this; 1489 | } 1490 | 1491 | /** 1492 | * 设置单列的值 1493 | * @param string $col . 1494 | * @param string $value 1495 | * @return self 1496 | */ 1497 | protected function setCol($col, $value) 1498 | { 1499 | if ($value === null) { 1500 | $value = 'NULL'; 1501 | } 1502 | 1503 | $key = $this->quoteName($col); 1504 | $value = $this->quoteNamesIn($value); 1505 | $this->col_values[$key] = $value; 1506 | return $this; 1507 | } 1508 | 1509 | /** 1510 | * 增加返回的列 1511 | * @param array $cols 1512 | * @return self 1513 | * 1514 | */ 1515 | protected function addReturning(array $cols) 1516 | { 1517 | foreach ($cols as $col) { 1518 | $this->returning[] = $this->quoteNamesIn($col); 1519 | } 1520 | return $this; 1521 | } 1522 | 1523 | /** 1524 | * 生成 RETURNING 语句 1525 | * @return string 1526 | */ 1527 | protected function buildReturning() 1528 | { 1529 | if (! $this->returning) { 1530 | return ''; 1531 | } 1532 | return ' RETURNING' . $this->indentCsv($this->returning); 1533 | } 1534 | 1535 | /** 1536 | * 构造函数 1537 | */ 1538 | public function __construct($host, $port, $user, $password, $db_name, $charset = 'utf8') 1539 | { 1540 | $this->settings = array( 1541 | 'host' => $host, 1542 | 'port' => $port, 1543 | 'user' => $user, 1544 | 'password' => $password, 1545 | 'dbname' => $db_name, 1546 | 'charset' => $charset, 1547 | ); 1548 | $this->connect(); 1549 | } 1550 | 1551 | /** 1552 | * 创建pdo实例 1553 | */ 1554 | protected function connect() 1555 | { 1556 | $dsn = 'mysql:dbname='.$this->settings["dbname"].';host='.$this->settings["host"].';port='.$this->settings['port']; 1557 | $this->pdo = new \PDO($dsn, $this->settings["user"], $this->settings["password"], array(\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . (!empty($this->settings['charset']) ? $this->settings['charset'] : 'utf8'))); 1558 | $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); 1559 | $this->pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false); 1560 | } 1561 | /* 1562 | * 关闭连接 1563 | */ 1564 | public function closeConnection() 1565 | { 1566 | $this->pdo = null; 1567 | } 1568 | 1569 | /** 1570 | * 执行 1571 | * @param string $query 1572 | * @param string $parameters 1573 | */ 1574 | protected function execute($query,$parameters = "") 1575 | { 1576 | try { 1577 | $this->sQuery = $this->pdo->prepare($query); 1578 | $this->bindMore($parameters); 1579 | if(!empty($this->parameters)) { 1580 | foreach($this->parameters as $param) 1581 | { 1582 | $parameters = explode("\x7F",$param); 1583 | $this->sQuery->bindParam($parameters[0],$parameters[1]); 1584 | } 1585 | } 1586 | $this->succes = $this->sQuery->execute(); 1587 | } 1588 | catch(\PDOException $e) 1589 | { 1590 | // 服务端断开时重连一次 1591 | if($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) 1592 | { 1593 | $this->closeConnection(); 1594 | $this->connect(); 1595 | $this->sQuery = $this->pdo->prepare($query); 1596 | $this->bindMore($parameters); 1597 | if(!empty($this->parameters)) { 1598 | foreach($this->parameters as $param) 1599 | { 1600 | $parameters = explode("\x7F",$param); 1601 | $this->sQuery->bindParam($parameters[0],$parameters[1]); 1602 | } 1603 | } 1604 | $this->succes = $this->sQuery->execute(); 1605 | } 1606 | else 1607 | { 1608 | throw $e; 1609 | } 1610 | } 1611 | $this->parameters = array(); 1612 | } 1613 | 1614 | /** 1615 | * 绑定 1616 | * @param string $para 1617 | * @param string $value 1618 | */ 1619 | public function bind($para, $value) 1620 | { 1621 | if(is_string($para)) 1622 | { 1623 | $this->parameters[sizeof($this->parameters)] = ":" . $para . "\x7F" . $value; 1624 | } 1625 | else 1626 | { 1627 | $this->parameters[sizeof($this->parameters)] = $para . "\x7F" . $value; 1628 | } 1629 | } 1630 | 1631 | /** 1632 | * 绑定多个 1633 | * @param array $parray 1634 | */ 1635 | public function bindMore($parray) 1636 | { 1637 | if(empty($this->parameters) && is_array($parray)) { 1638 | $columns = array_keys($parray); 1639 | foreach($columns as $i => &$column) { 1640 | $this->bind($column, $parray[$column]); 1641 | } 1642 | } 1643 | } 1644 | 1645 | /** 1646 | * 执行SQL 1647 | * @param string $query 1648 | * @param array $params 1649 | * @param int $fetchmode 1650 | * @return mixed 1651 | */ 1652 | public function query($query = '',$params = null, $fetchmode = \PDO::FETCH_ASSOC) 1653 | { 1654 | $query = trim($query); 1655 | if(empty($query)) 1656 | { 1657 | $query = $this->build(); 1658 | if(!$params) 1659 | { 1660 | $params = $this->getBindValues(); 1661 | } 1662 | } 1663 | 1664 | $this->resetAll(); 1665 | $this->lastSql = $query; 1666 | 1667 | $this->execute($query,$params); 1668 | 1669 | $rawStatement = explode(" ", $query); 1670 | 1671 | $statement = strtolower(trim($rawStatement[0])); 1672 | if ($statement === 'select' || $statement === 'show') { 1673 | return $this->sQuery->fetchAll($fetchmode); 1674 | } 1675 | elseif ( $statement === 'insert' || $statement === 'update' || $statement === 'delete' ) { 1676 | return $this->sQuery->rowCount(); 1677 | } 1678 | else { 1679 | return NULL; 1680 | } 1681 | } 1682 | 1683 | /** 1684 | * 返回一列 1685 | * @param string $query 1686 | * @param array $params 1687 | * @return array 1688 | */ 1689 | public function column($query = '',$params = null) 1690 | { 1691 | $query = trim($query); 1692 | if(empty($query)) 1693 | { 1694 | $query = $this->build(); 1695 | if(!$params) 1696 | { 1697 | $params = $this->getBindValues(); 1698 | } 1699 | } 1700 | 1701 | $this->resetAll(); 1702 | $this->lastSql = $query; 1703 | 1704 | $this->execute($query,$params); 1705 | $columns = $this->sQuery->fetchAll(\PDO::FETCH_NUM); 1706 | $column = null; 1707 | foreach($columns as $cells) { 1708 | $column[] = $cells[0]; 1709 | } 1710 | return $column; 1711 | } 1712 | 1713 | /** 1714 | * 返回一行 1715 | * @param string $query 1716 | * @param array $params 1717 | * @param int $fetchmode 1718 | * @return array 1719 | */ 1720 | public function row($query = '',$params = null, $fetchmode = \PDO::FETCH_ASSOC) 1721 | { 1722 | $query = trim($query); 1723 | if(empty($query)) 1724 | { 1725 | $query = $this->build(); 1726 | if(!$params) 1727 | { 1728 | $params = $this->getBindValues(); 1729 | } 1730 | } 1731 | 1732 | $this->resetAll(); 1733 | $this->lastSql = $query; 1734 | 1735 | $this->execute($query,$params); 1736 | return $this->sQuery->fetch($fetchmode); 1737 | } 1738 | 1739 | /** 1740 | * 返回单个值 1741 | * @param string $query 1742 | * @param array $params 1743 | * @return string 1744 | */ 1745 | public function single($query = '',$params = null) 1746 | { 1747 | $query = trim($query); 1748 | if(empty($query)) 1749 | { 1750 | $query = $this->build(); 1751 | if(!$params) 1752 | { 1753 | $params = $this->getBindValues(); 1754 | } 1755 | } 1756 | 1757 | $this->resetAll(); 1758 | $this->lastSql = $query; 1759 | 1760 | $this->execute($query,$params); 1761 | return $this->sQuery->fetchColumn(); 1762 | } 1763 | 1764 | /** 1765 | * 返回lastInsertId 1766 | * @return string 1767 | */ 1768 | public function lastInsertId() { 1769 | return $this->pdo->lastInsertId(); 1770 | } 1771 | 1772 | /** 1773 | * 返回最后一条直行的sql 1774 | * @return string 1775 | */ 1776 | public function lastSQL() 1777 | { 1778 | return $this->lastSql; 1779 | } 1780 | } 1781 | -------------------------------------------------------------------------------- /applications/Game/Lib/Store.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class Store 9 | { 10 | /** 11 | * 实例数组 12 | * @var array 13 | */ 14 | protected static $instance = array(); 15 | 16 | /** 17 | * 获取实例 18 | * @param string $config_name 19 | * @throws \Exception 20 | */ 21 | public static function instance($config_name) 22 | { 23 | // memcache 驱动 24 | if(\Config\Store::$driver == \Config\Store::DRIVER_MC) 25 | { 26 | if(!isset(\Config\Store::$$config_name)) 27 | { 28 | echo "\\Config\\Store::$config_name not set\n"; 29 | throw new \Exception("\\Config\\Store::$config_name not set\n"); 30 | } 31 | 32 | if(!isset(self::$instance[$config_name])) 33 | { 34 | if(extension_loaded('Memcached')) 35 | { 36 | self::$instance[$config_name] = new \Memcached; 37 | } 38 | elseif(extension_loaded('Memcache')) 39 | { 40 | self::$instance[$config_name] = new \Memcache; 41 | } 42 | else 43 | { 44 | sleep(2); 45 | exit("extension memcached is not installed\n"); 46 | } 47 | foreach(\Config\Store::$$config_name as $address) 48 | { 49 | list($ip, $port) = explode(':', $address); 50 | self::$instance[$config_name] ->addServer($ip, $port); 51 | } 52 | } 53 | return self::$instance[$config_name]; 54 | } 55 | // 文件驱动 56 | else 57 | { 58 | if(!isset(self::$instance[$config_name])) 59 | { 60 | self::$instance[$config_name] = new \Lib\StoreDriver\File($config_name); 61 | } 62 | return self::$instance[$config_name]; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /applications/Game/Lib/StoreDriver/File.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | */ 11 | 12 | class File 13 | { 14 | // 为了避免频繁读取磁盘,增加了缓存机制 15 | protected $dataCache = array(); 16 | // 上次缓存时间 17 | protected $lastCacheTime = 0; 18 | // 保存数据的文件 19 | protected $dataFile = ''; 20 | // 打开文件的句柄 21 | protected $dataFileHandle = null; 22 | 23 | // 缓存过期时间 24 | const CACHE_EXP_TIME = 1; 25 | 26 | /** 27 | * 构造函数 28 | * @param 配置名 $config_name 29 | */ 30 | public function __construct($config_name) 31 | { 32 | $this->dataFile = \Config\Store::$storePath . "/$config_name.store.cache.php"; 33 | if(!is_dir(\Config\Store::$storePath) && !@mkdir(\Config\Store::$storePath, 0777, true)) 34 | { 35 | // 可能目录已经被其它进程创建 36 | clearstatcache(); 37 | if(!is_dir(\Config\Store::$storePath)) 38 | { 39 | // 避免狂刷日志 40 | sleep(1); 41 | throw new \Exception('cant not mkdir('.\Config\Store::$storePath.')'); 42 | } 43 | } 44 | if(!is_file($this->dataFile)) 45 | { 46 | touch($this->dataFile); 47 | } 48 | $this->dataFileHandle = fopen(__FILE__, 'r'); 49 | if(!$this->dataFileHandle) 50 | { 51 | throw new \Exception("can not fopen($this->dataFile, 'r')"); 52 | } 53 | } 54 | 55 | /** 56 | * 设置 57 | * @param string $key 58 | * @param mixed $value 59 | * @param int $ttl 60 | * @return number 61 | */ 62 | public function set($key, $value, $ttl = 0) 63 | { 64 | flock($this->dataFileHandle, LOCK_EX); 65 | $this->readDataFromDisk(); 66 | $this->dataCache[$key] = $value; 67 | $ret = $this->writeToDisk(); 68 | flock($this->dataFileHandle, LOCK_UN); 69 | return $ret; 70 | } 71 | 72 | /** 73 | * 读取 74 | * @param string $key 75 | * @param bool $use_cache 76 | * @return Ambigous 77 | */ 78 | public function get($key, $use_cache = true) 79 | { 80 | if(!$use_cache || time() - $this->lastCacheTime > self::CACHE_EXP_TIME) 81 | { 82 | flock($this->dataFileHandle, LOCK_EX); 83 | $this->readDataFromDisk(); 84 | flock($this->dataFileHandle, LOCK_UN); 85 | } 86 | return isset($this->dataCache[$key]) ? $this->dataCache[$key] : null; 87 | } 88 | 89 | /** 90 | * 删除 91 | * @param string $key 92 | * @return number 93 | */ 94 | public function delete($key) 95 | { 96 | flock($this->dataFileHandle, LOCK_EX); 97 | $this->readDataFromDisk(); 98 | unset($this->dataCache[$key]); 99 | $ret = $this->writeToDisk(); 100 | flock($this->dataFileHandle, LOCK_UN); 101 | return $ret; 102 | } 103 | 104 | /** 105 | * 自增 106 | * @param string $key 107 | * @return boolean|multitype: 108 | */ 109 | public function increment($key) 110 | { 111 | flock($this->dataFileHandle, LOCK_EX); 112 | $this->readDataFromDisk(); 113 | if(!isset($this->dataCache[$key])) 114 | { 115 | flock($this->dataFileHandle, LOCK_UN); 116 | return false; 117 | } 118 | $this->dataCache[$key] ++; 119 | $this->writeToDisk(); 120 | flock($this->dataFileHandle, LOCK_UN); 121 | return $this->dataCache[$key]; 122 | } 123 | 124 | /** 125 | * 清零销毁存储数据 126 | */ 127 | public function destroy() 128 | { 129 | @unlink($this->dataFile); 130 | } 131 | 132 | /** 133 | * 写入磁盘 134 | * @return number 135 | */ 136 | protected function writeToDisk() 137 | { 138 | return file_put_contents($this->dataFile, "dataCache, true). ';'); 139 | } 140 | 141 | /** 142 | * 从磁盘读 143 | */ 144 | protected function readDataFromDisk() 145 | { 146 | $cache = include $this->dataFile; 147 | if(is_array($cache)) 148 | { 149 | $this->dataCache = $cache; 150 | } 151 | $this->lastCacheTime = time(); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /applications/Game/Login.php: -------------------------------------------------------------------------------- 1 | serv = new swoole_server("0.0.0.0", 9500); 17 | 18 | //通过配置获取 19 | $this->serv->set(array( 20 | //Worker进程数 21 | 'worker_num' => 4, 22 | //设置程序进入后台作为守护进程运行 23 | 'daemonize' => false, 24 | //每个worker进程允许处理的最大任务数 25 | 'max_request' => 10000, 26 | 'heartbeat_check_interval' => 60, 27 | 'dispatch_mode' => 2, 28 | 'debug_mode'=> 1 29 | )); 30 | 31 | //注册Server的事件回调函数 32 | $this->serv->on('Start', array($this, 'onStart')); 33 | $this->serv->on('WorkerStart', array($this, 'onWorkerStart')); 34 | $this->serv->on('Connect', array($this, 'onConnect')); 35 | $this->serv->on('Receive', array($this, 'onReceive')); 36 | $this->serv->on('Close', array($this, 'onClose')); 37 | 38 | $this->serv->start(); 39 | } 40 | 41 | //主进程启动 42 | public function onStart($serv) { 43 | echo "Server is Running" . PHP_EOL; 44 | } 45 | 46 | //进程组启动 47 | public function onWorkerStart( $serv , $worker_id) { 48 | //cli_set_process_title('GroupWorker'); 49 | // 在Worker进程开启时绑定定时器 50 | //echo "进程组:$worker_id" . PHP_EOL; 51 | } 52 | 53 | public function onConnect($serv, $fd, $from_id ) { 54 | 55 | } 56 | 57 | /** 58 | * 服务端接收数据 59 | * 60 | * @param $serv swoole_server对象 61 | * @param $fd 连接的描述符 62 | * @param $from_id reactor的id,无用 63 | * @param $data 接收数据 64 | */ 65 | public function onReceive(swoole_server $serv, $fd, $from_id, $data) { 66 | //检测数据完整性 67 | if(JsonProtocol::check($data) != 0) { 68 | return; 69 | } 70 | 71 | $data = JsonProtocol::decode($data); 72 | 73 | //接收参数 74 | $class = $data['class']; 75 | $method = $data['method']; 76 | $params = $data['params']; 77 | 78 | // 判断类对应文件是否载入 79 | if(!class_exists($class)) 80 | { 81 | $include_file = ROOT_DIR . "Server/$class.php"; 82 | if(is_file($include_file)) { 83 | require_once $include_file; 84 | } 85 | 86 | if(!class_exists($class)) { 87 | $code = 404; 88 | $msg = "class $class not found"; 89 | $result = array('code'=>$code, 'msg'=>$msg, 'data'=>null); 90 | 91 | $serv->send($fd, JsonProtocol::encode($result)); 92 | } 93 | } 94 | 95 | // 调用类的方法 96 | try { 97 | $ret = call_user_func_array(array(new $class, $method), $params); 98 | 99 | // 发送数据给客户端,调用成功,data下标对应的元素即为调用结果 100 | $serv->send($fd, JsonProtocol::encode($ret)); 101 | } catch(Exception $e) { 102 | // 发送数据给客户端,发生异常,调用失败 103 | $code = $e->getCode() ? $e->getCode() : 500; 104 | $result = array('code'=>$code, 'msg'=>$e->getMessage(), 'data'=>$e); 105 | 106 | $serv->send($fd, JsonProtocol::encode($result)); 107 | } 108 | } 109 | 110 | public function onClose($serv, $fd, $from_id) { 111 | 112 | } 113 | } -------------------------------------------------------------------------------- /applications/Game/Pay.php: -------------------------------------------------------------------------------- 1 | $recv_length) 25 | { 26 | // 还有这么多字节要接收 27 | return $total_length - $recv_length; 28 | } 29 | // 接收完毕 30 | return 0; 31 | } 32 | 33 | // 打包 34 | public static function encode($data) 35 | { 36 | // 选用json格式化数据 37 | $buffer = json_encode($data); 38 | // 包的整体长度为json长度加首部四个字节(首部数据包长度存储占用空间) 39 | $total_length = 4 + strlen($buffer); 40 | return pack('N', $total_length) . $buffer; 41 | } 42 | 43 | // 解包 44 | public static function decode($buffer) 45 | { 46 | $buffer_data = unpack('Ntotal_length', $buffer); 47 | // 得到这次数据的整体长度(字节) 48 | $total_length = $buffer_data['total_length']; 49 | // json的数据 50 | $json_string = substr($buffer, 4); 51 | return json_decode($json_string, true); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /applications/Game/Protocols/TextProtocol.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | class WebSocket 9 | { 10 | /** 11 | * 检查包的完整性 12 | * @param unknown_type $buffer 13 | */ 14 | public static function check($buffer) 15 | { 16 | // 数据长度 17 | $recv_len = strlen($buffer); 18 | // 长度不够 19 | if($recv_len < 6) 20 | { 21 | return 6-$recv_len; 22 | } 23 | 24 | // 握手阶段客户端发送HTTP协议 25 | if(0 === strpos($buffer, 'GET')) 26 | { 27 | // 判断\r\n\r\n边界 28 | if(strlen($buffer) - 4 === strpos($buffer, "\r\n\r\n")) 29 | { 30 | return 0; 31 | } 32 | return 1; 33 | } 34 | // 如果是flash的policy-file-request 35 | elseif(0 === strpos($buffer,'' != $buffer[strlen($buffer) - 1]) 38 | { 39 | return 1; 40 | } 41 | return 0; 42 | } 43 | 44 | // websocket二进制数据 45 | $data_len = ord($buffer[1]) & 127; 46 | $head_len = 6; 47 | if ($data_len === 126) { 48 | $pack = unpack('ntotal_len', substr($buffer, 2, 2)); 49 | $data_len = $pack['total_len']; 50 | $head_len = 8; 51 | } else if ($data_len === 127) { 52 | $arr = unpack('N2', substr($buffer, 2, 8)); 53 | $data_len = $arr[1]*4294967296 + $arr[2]; 54 | $head_len = 14; 55 | } 56 | $remain_len = $head_len + $data_len - $recv_len; 57 | if($remain_len < 0) 58 | { 59 | return false; 60 | } 61 | return $remain_len; 62 | } 63 | 64 | /** 65 | * 打包 66 | * @param string $buffer 67 | */ 68 | public static function encode($buffer) 69 | { 70 | $len = strlen($buffer); 71 | if($len<=125) 72 | { 73 | return "\x81".chr($len).$buffer; 74 | } 75 | else if($len<=65535) 76 | { 77 | return "\x81".chr(126).pack("n", $len).$buffer; 78 | } 79 | else 80 | { 81 | return "\x81".chr(127).pack("xxxxN", $len).$buffer; 82 | } 83 | } 84 | 85 | /** 86 | * 解包 87 | * @param string $buffer 88 | * @return string 89 | */ 90 | public static function decode($buffer) 91 | { 92 | $len = $masks = $data = $decoded = null; 93 | $len = ord($buffer[1]) & 127; 94 | if ($len === 126) { 95 | $masks = substr($buffer, 4, 4); 96 | $data = substr($buffer, 8); 97 | } else if ($len === 127) { 98 | $masks = substr($buffer, 10, 4); 99 | $data = substr($buffer, 14); 100 | } else { 101 | $masks = substr($buffer, 2, 4); 102 | $data = substr($buffer, 6); 103 | } 104 | for ($index = 0; $index < strlen($data); $index++) { 105 | $decoded .= $data[$index] ^ $masks[$index % 4]; 106 | } 107 | return $decoded; 108 | } 109 | 110 | /** 111 | * 是否是websocket断开的数据包 112 | * @param string $buffer 113 | */ 114 | public static function isClosePacket($buffer) 115 | { 116 | $opcode = self::getOpcode($buffer); 117 | return $opcode == 8; 118 | } 119 | 120 | /** 121 | * 是否是websocket ping的数据包 122 | * @param string $buffer 123 | */ 124 | public static function isPingPacket($buffer) 125 | { 126 | $opcode = self::getOpcode($buffer); 127 | return $opcode == 9; 128 | } 129 | 130 | /** 131 | * 是否是websocket pong的数据包 132 | * @param string $buffer 133 | */ 134 | public static function isPongPacket($buffer) 135 | { 136 | $opcode = self::getOpcode($buffer); 137 | return $opcode == 0xa; 138 | } 139 | 140 | /** 141 | * 获取wbsocket opcode 142 | * @param string $buffer 143 | */ 144 | public static function getOpcode($buffer) 145 | { 146 | return ord($buffer[0]) & 0xf; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /applications/Game/README.md: -------------------------------------------------------------------------------- 1 | 游戏服务器 2 | =================== 3 | 4 | ## 项目介绍 ## 5 | 6 | 1. 框架模型:Gateway/Worker进程 7 | 2. 通讯协议: google protobuf 8 | 9 | 10 | ## 目录介绍 ## 11 | 1. Game.php 入口文件 12 | 2. Task.php 定时任务 13 | 3. Server目录下具体业务逻辑数据模型 14 | 4. Structure目录存放结构体数据 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /applications/Game/Server/Member.php: -------------------------------------------------------------------------------- 1 | array('code'=>1, 'msg' => 'successful', 'data' => null), 21 | 'NO_DATA_EXIST' => array('code'=>100, 'msg' => 'No data exist', 'data' => null), 22 | 'ERROR_USERNAME' => array('code'=>400, 'msg' => '用户名为空', 'data' => null), 23 | 'ERROR_PASSWORD' => array('code'=>401, 'msg' => '用户密码为空', 'data' => null), 24 | 'ERROR_USERNAME_PASSWORD' => array('code'=>402, 'msg' => '用户或者密码错误', 'data' => null), 25 | ); 26 | 27 | public function __Construct() { 28 | $this->_store = Store::instance('game'); 29 | $this->_db = Db::instance('passport'); 30 | } 31 | 32 | /** 33 | * 验证用户登录 34 | * 35 | * @param array $params 36 | * @return mixed 37 | */ 38 | public function authLogin($username, $password) { 39 | if(!isset($username)) { 40 | return $this->_result['ERROR_USERNAME']; 41 | } 42 | 43 | if(!isset($password)) { 44 | return $this->_result['ERROR_PASSWORD']; 45 | } 46 | 47 | $password = md5(md5($password)); 48 | 49 | $sql = "SELECT m_id,m_nickname,m_email,m_regtime FROM `t_member` 50 | WHERE m_nickname='{$username}' and m_password = '{$password}' 51 | limit 1"; 52 | $result = $this->_db->row($sql); 53 | if($result) { 54 | $this->_result['SUCCESS']['data'] = $result; 55 | return $this->_result['SUCCESS']; 56 | } else { 57 | return $this->_result['ERROR_USERNAME_PASSWORD']; 58 | } 59 | } 60 | 61 | /** 62 | * 注册用户 63 | * @param $username 64 | * @param $password 65 | * @return array 66 | */ 67 | public function authReg($username, $password) { 68 | $password = md5(md5($password)); 69 | 70 | $data = array( 71 | 'm_nickname' => $username, 72 | 'm_password' => $password, 73 | 'm_email' => 'tt@qq.com', 74 | 'm_ip' => '2147483647', 75 | 'm_regtime' => time() 76 | ); 77 | $result = $this->_db->insert('t_member')->cols($data)->query(); 78 | return $result; 79 | } 80 | 81 | /** 82 | * 获取用户信息 83 | * 84 | * @param $mid 85 | * @return mixed 86 | */ 87 | public function userInfoByMid($mid) { 88 | $key = "GAME_USER_INFO_{$mid}"; 89 | 90 | $userInfo = $this->_store->get($key); 91 | if($userInfo) { 92 | $this->_result['SUCCESS']['data'] = $userInfo; 93 | return $this->_result['SUCCESS']; 94 | } 95 | 96 | $sql = "SELECT m_id,m_nickname,m_email,m_regtime FROM `t_member` 97 | WHERE m_id='{$mid}' 98 | limit 1"; 99 | $result = $this->_db->row($sql); 100 | if($result) { 101 | $this->_store->set($key, $result); 102 | } 103 | 104 | if($result) { 105 | $this->_result['SUCCESS']['data'] = $result; 106 | return $this->_result['SUCCESS']; 107 | } else { 108 | return $this->_result['NO_DATA_EXIST']; 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /applications/Game/Server/Statistic.php: -------------------------------------------------------------------------------- 1 | insert('t_statistic')->cols($data)->query(); 20 | return $result; 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /applications/Http/Config/Db.php: -------------------------------------------------------------------------------- 1 | select('name,age')->from('user')->where('age>12')->query(); 13 | * 等价于 14 | * $user_array = Db::instance('one_demo')->query('SELECT `name`,`age` FROM `one_demo` WHERE `age`>12'); 15 | * @var array 16 | */ 17 | public static $passport = array( 18 | 'host' => '127.0.0.1', 19 | 'port' => 3306, 20 | 'user' => 'root', 21 | 'password' => '622124', 22 | 'dbname' => 'passport', 23 | 'charset' => 'utf8', 24 | ); 25 | } -------------------------------------------------------------------------------- /applications/Http/Config/Server.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | class Db 8 | { 9 | /** 10 | * 实例数组 11 | * @var array 12 | */ 13 | protected static $instance = array(); 14 | 15 | /** 16 | * 获取实例 17 | * @param string $config_name 18 | * @throws \Exception 19 | */ 20 | public static function instance($config_name) 21 | { 22 | if(!isset(\Config\Db::$$config_name)) 23 | { 24 | echo "\\Config\\Db::$config_name not set\n"; 25 | throw new \Exception("\\Config\\Db::$config_name not set\n"); 26 | } 27 | 28 | if(empty(self::$instance[$config_name])) 29 | { 30 | $config = \Config\Db::$$config_name; 31 | self::$instance[$config_name] = new \Lib\DbConnection($config['host'], $config['port'], $config['user'], $config['password'], $config['dbname']); 32 | } 33 | return self::$instance[$config_name]; 34 | } 35 | 36 | /** 37 | * 关闭数据库实例 38 | * @param string $config_name 39 | */ 40 | public static function close($config_name) 41 | { 42 | if(isset(self::$instance[$config_name])) 43 | { 44 | self::$instance[$config_name]->closeConnection(); 45 | self::$instance[$config_name] = null; 46 | } 47 | } 48 | 49 | /** 50 | * 关闭所有数据库实例 51 | */ 52 | public static function closeAll() 53 | { 54 | foreach(self::$instance as $connection) 55 | { 56 | $connection->closeConnection(); 57 | self::$instance = array(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /applications/Http/Lib/Store.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class Store 9 | { 10 | /** 11 | * 实例数组 12 | * @var array 13 | */ 14 | protected static $instance = array(); 15 | 16 | /** 17 | * 获取实例 18 | * @param string $config_name 19 | * @throws \Exception 20 | */ 21 | public static function instance($config_name) 22 | { 23 | // memcache 驱动 24 | if(\Config\Store::$driver == \Config\Store::DRIVER_MC) 25 | { 26 | if(!isset(\Config\Store::$$config_name)) 27 | { 28 | echo "\\Config\\Store::$config_name not set\n"; 29 | throw new \Exception("\\Config\\Store::$config_name not set\n"); 30 | } 31 | 32 | if(!isset(self::$instance[$config_name])) 33 | { 34 | if(extension_loaded('Memcached')) 35 | { 36 | self::$instance[$config_name] = new \Memcached; 37 | } 38 | elseif(extension_loaded('Memcache')) 39 | { 40 | self::$instance[$config_name] = new \Memcache; 41 | } 42 | else 43 | { 44 | sleep(2); 45 | exit("extension memcached is not installed\n"); 46 | } 47 | foreach(\Config\Store::$$config_name as $address) 48 | { 49 | list($ip, $port) = explode(':', $address); 50 | self::$instance[$config_name] ->addServer($ip, $port); 51 | } 52 | } 53 | return self::$instance[$config_name]; 54 | } 55 | // 文件驱动 56 | else 57 | { 58 | if(!isset(self::$instance[$config_name])) 59 | { 60 | self::$instance[$config_name] = new \Lib\StoreDriver\File($config_name); 61 | } 62 | return self::$instance[$config_name]; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /applications/Http/Lib/StoreDriver/File.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | */ 11 | 12 | class File 13 | { 14 | // 为了避免频繁读取磁盘,增加了缓存机制 15 | protected $dataCache = array(); 16 | // 上次缓存时间 17 | protected $lastCacheTime = 0; 18 | // 保存数据的文件 19 | protected $dataFile = ''; 20 | // 打开文件的句柄 21 | protected $dataFileHandle = null; 22 | 23 | // 缓存过期时间 24 | const CACHE_EXP_TIME = 1; 25 | 26 | /** 27 | * 构造函数 28 | * @param 配置名 $config_name 29 | */ 30 | public function __construct($config_name) 31 | { 32 | $this->dataFile = \Config\Store::$storePath . "/$config_name.store.cache.php"; 33 | if(!is_dir(\Config\Store::$storePath) && !@mkdir(\Config\Store::$storePath, 0777, true)) 34 | { 35 | // 可能目录已经被其它进程创建 36 | clearstatcache(); 37 | if(!is_dir(\Config\Store::$storePath)) 38 | { 39 | // 避免狂刷日志 40 | sleep(1); 41 | throw new \Exception('cant not mkdir('.\Config\Store::$storePath.')'); 42 | } 43 | } 44 | if(!is_file($this->dataFile)) 45 | { 46 | touch($this->dataFile); 47 | } 48 | $this->dataFileHandle = fopen(__FILE__, 'r'); 49 | if(!$this->dataFileHandle) 50 | { 51 | throw new \Exception("can not fopen($this->dataFile, 'r')"); 52 | } 53 | } 54 | 55 | /** 56 | * 设置 57 | * @param string $key 58 | * @param mixed $value 59 | * @param int $ttl 60 | * @return number 61 | */ 62 | public function set($key, $value, $ttl = 0) 63 | { 64 | flock($this->dataFileHandle, LOCK_EX); 65 | $this->readDataFromDisk(); 66 | $this->dataCache[$key] = $value; 67 | $ret = $this->writeToDisk(); 68 | flock($this->dataFileHandle, LOCK_UN); 69 | return $ret; 70 | } 71 | 72 | /** 73 | * 读取 74 | * @param string $key 75 | * @param bool $use_cache 76 | * @return Ambigous 77 | */ 78 | public function get($key, $use_cache = true) 79 | { 80 | if(!$use_cache || time() - $this->lastCacheTime > self::CACHE_EXP_TIME) 81 | { 82 | flock($this->dataFileHandle, LOCK_EX); 83 | $this->readDataFromDisk(); 84 | flock($this->dataFileHandle, LOCK_UN); 85 | } 86 | return isset($this->dataCache[$key]) ? $this->dataCache[$key] : null; 87 | } 88 | 89 | /** 90 | * 删除 91 | * @param string $key 92 | * @return number 93 | */ 94 | public function delete($key) 95 | { 96 | flock($this->dataFileHandle, LOCK_EX); 97 | $this->readDataFromDisk(); 98 | unset($this->dataCache[$key]); 99 | $ret = $this->writeToDisk(); 100 | flock($this->dataFileHandle, LOCK_UN); 101 | return $ret; 102 | } 103 | 104 | /** 105 | * 自增 106 | * @param string $key 107 | * @return boolean|multitype: 108 | */ 109 | public function increment($key) 110 | { 111 | flock($this->dataFileHandle, LOCK_EX); 112 | $this->readDataFromDisk(); 113 | if(!isset($this->dataCache[$key])) 114 | { 115 | flock($this->dataFileHandle, LOCK_UN); 116 | return false; 117 | } 118 | $this->dataCache[$key] ++; 119 | $this->writeToDisk(); 120 | flock($this->dataFileHandle, LOCK_UN); 121 | return $this->dataCache[$key]; 122 | } 123 | 124 | /** 125 | * 清零销毁存储数据 126 | */ 127 | public function destroy() 128 | { 129 | @unlink($this->dataFile); 130 | } 131 | 132 | /** 133 | * 写入磁盘 134 | * @return number 135 | */ 136 | protected function writeToDisk() 137 | { 138 | return file_put_contents($this->dataFile, "dataCache, true). ';'); 139 | } 140 | 141 | /** 142 | * 从磁盘读 143 | */ 144 | protected function readDataFromDisk() 145 | { 146 | $cache = include $this->dataFile; 147 | if(is_array($cache)) 148 | { 149 | $this->dataCache = $cache; 150 | } 151 | $this->lastCacheTime = time(); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /applications/Http/Protocols/JsonProtocol.php: -------------------------------------------------------------------------------- 1 | 7 | * */ 8 | class JsonProtocol 9 | { 10 | /** 11 | * 从socket缓冲区中预读长度 12 | * @var integer 13 | */ 14 | const PRREAD_LENGTH = 87380; 15 | 16 | /** 17 | * 判断数据包是否接收完整 18 | * @param string $bin_data 19 | * @param mixed $data 20 | * @return integer 0代表接收完毕,大于0代表还要接收数据 21 | */ 22 | public static function dealInput($bin_data) 23 | { 24 | $bin_data_length = strlen($bin_data); 25 | // 判断最后一个字符是否为\n,\n代表一个数据包的结束 26 | if($bin_data[$bin_data_length-1] !="\n") 27 | { 28 | // 再读 29 | return self::PRREAD_LENGTH; 30 | } 31 | return 0; 32 | } 33 | 34 | /** 35 | * 将数据打包成Rpc协议数据 36 | * @param mixed $data 37 | * @return string 38 | */ 39 | public static function encode($data) 40 | { 41 | return json_encode($data)."\n"; 42 | } 43 | 44 | /** 45 | * 解析Rpc协议数据 46 | * @param string $bin_data 47 | * @return mixed 48 | */ 49 | public static function decode($bin_data) 50 | { 51 | return json_decode(trim($bin_data), true); 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /applications/Http/Services/Online.php: -------------------------------------------------------------------------------- 1 | single($sql); 29 | return $result; 30 | } 31 | } -------------------------------------------------------------------------------- /applications/Http/Services/Zone.php: -------------------------------------------------------------------------------- 1 | get($key); 27 | if($serverList) { 28 | return $serverList; 29 | } 30 | 31 | $sql = "SELECT s_id,s_name,s_type,s_ip FROM `t_server` 32 | WHERE s_status=1 33 | ORDER BY s_id ASC"; 34 | $result = $db->query($sql); 35 | if($result) { 36 | $store->set($key, $result); 37 | } 38 | 39 | return $result; 40 | } 41 | 42 | public static function getData($a, $b) { 43 | return $a+$b; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /applications/Http/index.php: -------------------------------------------------------------------------------- 1 | http = new swoole_http_server("0.0.0.0", 8080); 14 | $this->http->set( 15 | array( 16 | 'worker_num' => 16, 17 | 'daemonize' => false, 18 | 'max_request' => 10000, 19 | 'dispatch_mode' => 1 20 | ) 21 | ); 22 | $this->http->on('Start', array($this, 'onStart')); 23 | $this->http->on('request' , array( $this , 'onRequest')); 24 | $this->http->on('message' , array( $this , 'onMessage')); 25 | $this->http->start(); 26 | } 27 | 28 | public function onStart( $serv ) { 29 | echo "Start\n"; 30 | } 31 | 32 | public function onRequest($request, $response) { 33 | if( isset($request->server) ) { 34 | $server = $request->server; 35 | 36 | echo $server['request_uri'] . PHP_EOL; 37 | } 38 | 39 | if( isset($request->header) ) { 40 | $header = $request->header; 41 | } 42 | 43 | if( isset($request->get) ) { 44 | $get = $request->get; 45 | print_r($get); 46 | } 47 | 48 | if( isset($request->post) ) { 49 | $post = $request->post; 50 | } 51 | 52 | $response->end(1); 53 | } 54 | 55 | public function onMessage($request, $response) { 56 | //echo $request->message; 57 | //$response->message(json_encode(array("data1", "data2"))); 58 | } 59 | } 60 | new Server(); -------------------------------------------------------------------------------- /applications/swoole-yaf/README.md: -------------------------------------------------------------------------------- 1 | ##**swoole-yaf** 2 | 结合PHP的Yaf框架和Swoole扩展的高性能PHP Web框架 3 | 4 | ##**描述** 5 | 底层使用Swoole内置的swoole_http_server提供服务 6 | 上层应用使用Yaf框架搭建 7 | 8 | ##**使用说明** 9 | 打开终端 10 | cd swoole-yaf 11 | php server/server.php 12 | 13 | 打开浏览器,输入http://ip:9501 14 | 1、路由:http://192.168.1.248:9501/test Index:模块 控制器:Index 方法:test 15 | 2、获取参数:http://192.168.1.248:9501/index/index/vote/mid/13392/status/1 16 | 参数:mid=13392 status=1 17 | 18 | ##**swoole版本** 19 | swoole-1.7.8+版本 20 | 21 | ##**yaf版本** 22 | 任意stable版本 23 | -------------------------------------------------------------------------------- /applications/swoole-yaf/application/Bootstrap.php: -------------------------------------------------------------------------------- 1 | getConfig(); 15 | Yaf_Registry::set('config', $arrConfig); 16 | } 17 | 18 | public function _initPlugin(Yaf_Dispatcher $dispatcher) { 19 | //注册一个插件 20 | $objSamplePlugin = new SamplePlugin(); 21 | $dispatcher->registerPlugin($objSamplePlugin); 22 | } 23 | 24 | public function _initRoute(Yaf_Dispatcher $dispatcher) { 25 | //在这里注册自己的路由协议,默认使用简单路由 26 | $fileName = APPLICATION_PATH . "/conf/routes.ini"; 27 | if ( file_exists($fileName) ) { 28 | $config = new Yaf_Config_Ini($fileName); 29 | $dispatcher->getRouter()->addConfig($config->routes); 30 | } 31 | } 32 | 33 | public function _initView(Yaf_Dispatcher $dispatcher){ 34 | //在这里注册自己的view控制器,例如smarty,firekylin 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /applications/swoole-yaf/application/controllers/Error.php: -------------------------------------------------------------------------------- 1 | getView()->assign("exception", $exception); 14 | //5. render by Yaf 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /applications/swoole-yaf/application/controllers/Form.php: -------------------------------------------------------------------------------- 1 | getRequest()->getPost ("fname"); 15 | //var_dump( $post ); 16 | $content['fname'] = $post["fname"]; 17 | $content['lname'] = $post["lname"]; 18 | 19 | //2. fetch model 20 | $model = new SampleModel(); 21 | 22 | //3. assign 23 | $this->getView()->assign("content", $content); 24 | //$this->getView()->assign("name", $name); 25 | $this->display('index'); 26 | //4. render by Yaf, 如果这里返回FALSE, Yaf将不会调用自动视图引擎Render模板 27 | return FALSE; 28 | } 29 | 30 | public function FormAction() { 31 | $get = HttpServer::$get; 32 | $this->getView()->assign("name", $get['name']); 33 | $this->display('form'); 34 | return FALSE; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /applications/swoole-yaf/application/controllers/Index.php: -------------------------------------------------------------------------------- 1 | getRequest()->getQuery("get", "default value"); 18 | // $get = HttpServer::$get; 19 | 20 | //2. fetch model 21 | //$model = new SampleModel(); 22 | //$model = new SampleModel(); 23 | echo 'Hello Word'; 24 | return FALSE; 25 | 26 | //3. assign 27 | //$this->getView()->assign("name", "test"); 28 | 29 | //4. render by Yaf, 如果这里返回FALSE, Yaf将不会调用自动视图引擎Render模板 30 | return TRUE; 31 | } 32 | 33 | public function testAction() { 34 | $model = new SampleModel(); 35 | echo $model->selectSample(); 36 | return FALSE; 37 | } 38 | 39 | public function voteAction() { 40 | $get = $this->getRequest()->getParams(); 41 | 42 | print_r($get); 43 | return false; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /applications/swoole-yaf/application/favicon.ico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moxiaobai/swoole-game/920f9ff4cb26c3d1ce686e52ff69443c99e6f79e/applications/swoole-yaf/application/favicon.ico.png -------------------------------------------------------------------------------- /applications/swoole-yaf/application/library/readme.txt: -------------------------------------------------------------------------------- 1 | 项目库文件放在这里 -------------------------------------------------------------------------------- /applications/swoole-yaf/application/models/Sample.php: -------------------------------------------------------------------------------- 1 | getMessage(); 3 | ?> 4 | -------------------------------------------------------------------------------- /applications/swoole-yaf/application/views/form/form.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello World 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /applications/swoole-yaf/application/views/form/index.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello World 4 | 5 | 6 | Hello, 7 | 8 | 9 | -------------------------------------------------------------------------------- /applications/swoole-yaf/application/views/index/index.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Hello

4 |
5 |

First name:

6 |

Last name:

7 | 8 |
9 | 10 |

请单击确认按钮

11 | click 12 | 13 | 14 | -------------------------------------------------------------------------------- /applications/swoole-yaf/conf/application.ini: -------------------------------------------------------------------------------- 1 | [common] 2 | application.directory = APPLICATION_PATH "/application" 3 | application.modules = "Index,Ajax,Api" 4 | application.dispatcher.catchException = TRUE 5 | 6 | [product : common] 7 | -------------------------------------------------------------------------------- /applications/swoole-yaf/conf/routes.ini: -------------------------------------------------------------------------------- 1 | routes.Home.type = rewrite 2 | routes.Home.match = /test 3 | routes.Home.route.module = Index 4 | routes.Home.route.controller = Index 5 | routes.Home.route.action = test -------------------------------------------------------------------------------- /applications/swoole-yaf/server/server.php: -------------------------------------------------------------------------------- 1 | set( 18 | array( 19 | 'worker_num' => 4, 20 | 'daemonize' => false, 21 | 'max_request' => 10000, 22 | 'dispatch_mode' => 1 23 | ) 24 | ); 25 | 26 | $http->on('WorkerStart' , array( $this , 'onWorkerStart')); 27 | 28 | $http->on('request', function ($request, $response) { 29 | if( isset($request->server) ) { 30 | HttpServer::$server = $request->server; 31 | } 32 | if( isset($request->header) ) { 33 | HttpServer::$header = $request->header; 34 | } 35 | if( isset($request->get) ) { 36 | HttpServer::$get = $request->get; 37 | } 38 | if( isset($request->post) ) { 39 | HttpServer::$post = $request->post; 40 | } 41 | 42 | // TODO handle img 43 | 44 | ob_start(); 45 | try { 46 | $yaf_request = new Yaf_Request_Http(HttpServer::$server['request_uri']); 47 | 48 | $this->application->getDispatcher()->dispatch($yaf_request); 49 | 50 | // unset(Yaf_Application::app()); 51 | } catch ( Yaf_Exception $e ) { 52 | var_dump( $e ); 53 | } 54 | 55 | $result = ob_get_contents(); 56 | 57 | ob_end_clean(); 58 | 59 | // add Header 60 | 61 | // add cookies 62 | 63 | // set status 64 | $response->end($result); 65 | }); 66 | 67 | $http->start(); 68 | } 69 | 70 | //Worker工作组 71 | public function onWorkerStart() { 72 | define('APPLICATION_PATH', dirname(__DIR__)); 73 | $this->application = new Yaf_Application( APPLICATION_PATH . 74 | "/conf/application.ini"); 75 | ob_start(); 76 | $this->application->bootstrap()->run(); 77 | ob_end_clean(); 78 | } 79 | 80 | public static function getInstance() { 81 | if (!self::$instance) { 82 | self::$instance = new HttpServer; 83 | } 84 | return self::$instance; 85 | } 86 | } 87 | 88 | HttpServer::getInstance(); 89 | -------------------------------------------------------------------------------- /applications/swoole-yaf/yaf_classes.php: -------------------------------------------------------------------------------- 1 | $recv_length) 25 | { 26 | // 还有这么多字节要接收 27 | return $total_length - $recv_length; 28 | } 29 | // 接收完毕 30 | return 0; 31 | } 32 | 33 | // 打包 34 | public static function encode($data) 35 | { 36 | // 选用json格式化数据 37 | $buffer = json_encode($data); 38 | // 包的整体长度为json长度加首部四个字节(首部数据包长度存储占用空间) 39 | $total_length = 4 + strlen($buffer); 40 | return pack('N', $total_length) . $buffer; 41 | } 42 | 43 | // 解包 44 | public static function decode($buffer) 45 | { 46 | $buffer_data = unpack('Ntotal_length', $buffer); 47 | // 得到这次数据的整体长度(字节) 48 | $total_length = $buffer_data['total_length']; 49 | // json的数据 50 | $json_string = substr($buffer, 4); 51 | return json_decode($json_string, true); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example/echo/client.php: -------------------------------------------------------------------------------- 1 | client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); 15 | 16 | $this->client->on('Connect', array($this, 'onConnect')); 17 | $this->client->on('Receive', array($this, 'onReceive')); 18 | $this->client->on('Close', array($this, 'onClose')); 19 | $this->client->on('Error', array($this, 'onError')); 20 | } 21 | 22 | public function connect() { 23 | $fp = $this->client->connect('127.0.0.1', 9501, 0.5, 0); 24 | if( !$fp ) { 25 | echo "Error: {$fp->errMsg}[{$fp->errCode}]\n"; 26 | return; 27 | } 28 | } 29 | 30 | public function onConnect( $cli) { 31 | fwrite(STDOUT, "Enter Msg:\n"); 32 | swoole_event_add(STDIN, function($fp){ 33 | global $cli; 34 | fwrite(STDOUT, "Enter Msg:"); 35 | $msg = trim(fgets(STDIN)); 36 | $cli->send( $msg ); 37 | }); 38 | } 39 | 40 | public function onReceive( $cli, $data ) { 41 | echo "Get Message From Server: {$data}\n"; 42 | } 43 | 44 | public function onClose( $cli) { 45 | echo "Client close connection\n"; 46 | } 47 | 48 | public function onError() { 49 | 50 | } 51 | 52 | public function send($data) { 53 | $this->client->send( $data ); 54 | } 55 | 56 | public function isConnected() { 57 | return $this->client->isConnected(); 58 | } 59 | } 60 | $cli = new Client(); 61 | $cli->connect(); -------------------------------------------------------------------------------- /example/echo/eofClient.php: -------------------------------------------------------------------------------- 1 | connect('127.0.0.1', 9501, 0.5, 0)) { 11 | echo "Over flow. errno=".$client->errCode; 12 | die("\n"); 13 | } 14 | 15 | $data = array( 16 | 'name' => __FILE__, 17 | 'content' => str_repeat('A', 8192 * rand(100, 200)), //800K 18 | ); 19 | $_send = serialize($data)."\r\n\r\n"; 20 | echo "send length=".strlen($_send)."\n"; 21 | 22 | if(!$client->send($_send)) 23 | { 24 | die("send failed.\n"); 25 | } -------------------------------------------------------------------------------- /example/echo/eofServer.php: -------------------------------------------------------------------------------- 1 | set(array( 12 | 'package_eof' => "\r\n\r\n", 13 | 'open_eof_check' => true, 14 | 'worker_num' => 1, 15 | 'dispatch_mode' => 1, 16 | 'package_max_length' => 1024 * 1024 * 2, //2M 17 | 'debug_mode'=> 1 18 | )); 19 | //$serv->on('connect', function ($serv, $fd) { 20 | // //echo "[#" . posix_getpid() . "]\tClient:Connect.\n"; 21 | //}); 22 | $serv->on('receive', function (swoole_server $serv, $fd, $from_id, $data) { 23 | echo "[#" . posix_getpid() . "] recv length=".strlen($data)."\n"; 24 | $req = unserialize(trim($data)); 25 | //echo $req['name'] . "\n"; 26 | //echo "content_length: " . strlen($data) . "\n"; 27 | $respData = '

Welcome to swoole-server!

'; 28 | $response = implode("\r\n", array( 29 | 'HTTP/1.1 200 OK', 30 | 'Cache-Control: must-revalidate,no-cache', 31 | 'Content-Language: zh-CN', 32 | 'Server: swoole-'.SWOOLE_VERSION, 33 | 'Content-Type: text/html', 34 | 'Connection: keep-alive', 35 | 'Content-Length: ' . strlen($respData), 36 | '', 37 | $respData)); 38 | //print_r($response); 39 | usleep(500000); 40 | //if ($serv->worker_id == 2) sleep(100); 41 | //$serv->send($fd, $response); 42 | }); 43 | //$serv->on('close', function ($serv, $fd) { 44 | //echo "[#" . posix_getpid() . "]\tClient: Close.\n"; 45 | //}); 46 | $serv->start(); -------------------------------------------------------------------------------- /example/echo/event.php: -------------------------------------------------------------------------------- 1 | \n"); 13 | } 14 | fwrite($fp, "HELLO world"); 15 | function stream_onRead($fp) 16 | { 17 | echo fread($fp, 1024)."\n"; 18 | sleep(1); 19 | swoole_event_set($fp, null, null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); 20 | //swoole_event_del($fp); 21 | //fclose($fp); 22 | } 23 | function stream_onWrite($fp) 24 | { 25 | fwrite($fp, "hi swoole\n"); 26 | swoole_event_set($fp, null, null, SWOOLE_EVENT_READ); 27 | } 28 | swoole_event_add($fp, 'stream_onRead', 'stream_onWrite'); 29 | echo "start\n"; -------------------------------------------------------------------------------- /example/echo/server.php: -------------------------------------------------------------------------------- 1 | serv = new swoole_server("0.0.0.0", 9501); 15 | $this->serv->set(array( 16 | //Worker进程数 17 | 'worker_num' => 4, 18 | //设置程序进入后台作为守护进程运行 19 | 'daemonize' => false, 20 | //每个worker进程允许处理的最大任务数 21 | 'max_request' => 10000, 22 | 'heartbeat_check_interval' => 60, 23 | 'dispatch_mode' => 2, 24 | 'debug_mode'=> 1 25 | )); 26 | 27 | //注册Server的事件回调函数 28 | $this->serv->on('Start', array($this, 'onStart')); 29 | $this->serv->on('WorkerStart', array($this, 'onWorkerStart')); 30 | $this->serv->on('Connect', array($this, 'onConnect')); 31 | $this->serv->on('Receive', array($this, 'onReceive')); 32 | $this->serv->on('Close', array($this, 'onClose')); 33 | 34 | $this->serv->start(); 35 | } 36 | 37 | //主进程 38 | public function onStart($serv) { 39 | //cli_set_process_title('MainWorker'); 40 | echo "Server Start" . PHP_EOL; 41 | 42 | //管理进程的PID,通过向管理进程发送SIGUSR1信号可实现柔性重启 43 | echo $serv->manager_pid . PHP_EOL; 44 | 45 | //主进程的PID,通过向主进程发送SIGTERM信号可安全关闭服务器 46 | echo $serv->master_pid . PHP_EOL; 47 | } 48 | 49 | //进程组 50 | public function onWorkerStart( $serv , $worker_id) { 51 | //cli_set_process_title('GroupWorker'); 52 | // 在Worker进程开启时绑定定时器 53 | echo "Woker进程组: $worker_id" . PHP_EOL; 54 | } 55 | 56 | public function onConnect(swoole_server $serv, $fd, $from_id ) { 57 | //获取连接的客户端信息 58 | $fdInfo = $serv->connection_info($fd); 59 | echo '
';
60 |         print_r($fdInfo);
61 |         echo '
'; 62 | 63 | $serv->send( $fd, "Welcome {$fd} Connect Server" ); 64 | } 65 | 66 | public function onReceive( swoole_server $serv, $fd, $from_id, $data ) { 67 | echo "Hello,Get Message From Client {$fd}:{$data}" . PHP_EOL; 68 | } 69 | 70 | public function onClose(swoole_server $serv, $fd, $from_id ) { 71 | echo "Client {$fd} close connection" . PHP_EOL; 72 | } 73 | } 74 | 75 | // 启动服务器 76 | $server = new Server(); -------------------------------------------------------------------------------- /example/http/index.php: -------------------------------------------------------------------------------- 1 | on('request', function ($request, $response) { 13 | echo '
';
14 |     print_r($request);
15 |     echo '
'; 16 | 17 | echo '
';
18 |     print_r($response);
19 |     echo '
'; 20 | 21 | $html = "

Hello Swoole.

"; 22 | $response->end($html); 23 | }); 24 | 25 | $http->start(); -------------------------------------------------------------------------------- /example/member.php: -------------------------------------------------------------------------------- 1 | 13392); 17 | 18 | $method = 'authLogin'; 19 | $params = array('username'=>'moxiaobai', 'password'=>'rxg622124'); 20 | $data = array('class'=>$class, 'method'=>$method, 'params'=>$params); 21 | 22 | $buffer = JsonProtocol::encode($data); 23 | 24 | stream_socket_sendto($socket, $buffer); 25 | 26 | // 读取服务端返回的数据 27 | $result = stream_socket_recvfrom($socket, 65535); 28 | 29 | 30 | $rs = JsonProtocol::decode($result); 31 | echo '
';
32 | print_r($rs);
33 | echo '
'; 34 | 35 | // 关闭链接 36 | //fclose($socket); 37 | 38 | 39 | 40 | exit; 41 | 42 | class Client 43 | { 44 | private $client; 45 | 46 | public function __construct() { 47 | $this->client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); 48 | 49 | $this->client->on('Connect', array($this, 'onConnect')); 50 | $this->client->on('Receive', array($this, 'onReceive')); 51 | $this->client->on('Close', array($this, 'onClose')); 52 | $this->client->on('Error', array($this, 'onError')); 53 | } 54 | 55 | public function connect() { 56 | $fp = $this->client->connect("127.0.0.1", 9500 , 1); 57 | if( !$fp ) { 58 | echo "Error: {$fp->errMsg}[{$fp->errCode}]\n"; 59 | return; 60 | } 61 | } 62 | 63 | public function onConnect( $cli) { 64 | $class = 'Member'; 65 | $method = 'userInfoByMid'; 66 | $params = array('mid'=>13392); 67 | $data = array('class'=>$class, 'method'=>$method, 'params'=>$params); 68 | 69 | $buffer = JsonProtocol::encode($data); 70 | $cli->send( $buffer ); 71 | } 72 | 73 | public function onReceive( $cli, $data ) { 74 | $rs = JsonProtocol::decode($data); 75 | echo '
';
76 |         print_r($rs);
77 |         echo '
'; 78 | } 79 | 80 | public function onClose( $cli) { 81 | echo "Client close connection\n"; 82 | } 83 | 84 | public function onError() { 85 | 86 | } 87 | 88 | public function send($data) { 89 | $this->client->send( $data ); 90 | } 91 | 92 | public function isConnected() { 93 | return $this->client->isConnected(); 94 | } 95 | } 96 | $cli = new Client(); 97 | $cli->connect(); 98 | -------------------------------------------------------------------------------- /example/mysql/async_mysql.php: -------------------------------------------------------------------------------- 1 | connect('127.0.0.1', 'root', '622124', 'crontab'); 16 | $db->query("show tables", MYSQLI_ASYNC); 17 | 18 | swoole_event_add(swoole_get_mysqli_sock($db), function($__db_sock) { 19 | global $db; 20 | 21 | var_dump($__db_sock); 22 | $res = $db->reap_async_query(); 23 | var_dump($res->fetch_all(MYSQLI_ASSOC)); 24 | $db->query("show tables", MYSQLI_ASYNC); 25 | 26 | swoole_event_exit(); 27 | }); 28 | 29 | echo "Finish\n"; -------------------------------------------------------------------------------- /example/mysql/mysqlPool.php: -------------------------------------------------------------------------------- 1 | serv = new swoole_server("0.0.0.0", 9306); 16 | $this->serv->set(array( 17 | 'worker_num' => 8, 18 | 'daemonize' => false, 19 | 'max_request' => 10000, 20 | 'dispatch_mode' => 3, 21 | 'debug_mode'=> 1 , 22 | 'task_worker_num' => 8 23 | )); 24 | $this->serv->on('WorkerStart', array($this, 'onWorkerStart')); 25 | $this->serv->on('Connect', array($this, 'onConnect')); 26 | $this->serv->on('Receive', array($this, 'onReceive')); 27 | $this->serv->on('Close', array($this, 'onClose')); 28 | // bind callback 29 | $this->serv->on('Task', array($this, 'onTask')); 30 | $this->serv->on('Finish', array($this, 'onFinish')); 31 | $this->serv->start(); 32 | } 33 | 34 | public function onWorkerStart( $serv , $worker_id) { 35 | echo "onWorkerStart\n"; 36 | // 判定是否为Task Worker进程 37 | if( $worker_id >= $serv->setting['worker_num'] ) { 38 | $this->pdo = new PDO( 39 | "mysql:host=localhost;port=3306;dbname=Test", 40 | "root", 41 | "622124", 42 | array( 43 | PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8';", 44 | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 45 | PDO::ATTR_PERSISTENT => true 46 | ) 47 | ); 48 | } 49 | } 50 | 51 | public function onConnect( $serv, $fd, $from_id ) { 52 | echo "Client {$fd} connect\n"; 53 | } 54 | 55 | public function onReceive( swoole_server $serv, $fd, $from_id, $data ) { 56 | $sql = array( 57 | 'sql'=>'Insert into Test values( pid = ?, name = ?)', 58 | 'param' => array( 59 | 0 , 60 | "'name'" 61 | ), 62 | 'fd' => $fd 63 | ); 64 | $serv->task( json_encode($sql) ); 65 | } 66 | 67 | public function onClose( $serv, $fd, $from_id ) { 68 | echo "Client {$fd} close connection\n"; 69 | } 70 | 71 | public function onTask($serv,$task_id,$from_id, $data) { 72 | try{ 73 | $sql = json_decode( $data , true ); 74 | 75 | $statement = $this->pdo->prepare($sql['sql']); 76 | $statement->execute($sql['param']); 77 | $serv->send( $sql['fd'],"Insert"); 78 | return true; 79 | } catch( PDOException $e ) { 80 | var_dump( $e ); 81 | return false; 82 | } 83 | } 84 | public function onFinish($serv,$task_id, $data) { 85 | } 86 | } 87 | 88 | new MySQLPool(); -------------------------------------------------------------------------------- /example/process/server.php: -------------------------------------------------------------------------------- 1 | start(); 18 | $workers[$pid] = $process; 19 | //echo "Master: new worker, PID=".$pid."\n"; 20 | } 21 | master_async($workers); 22 | //master_sync($workers); 23 | //异步主进程 24 | function master_async($workers) 25 | { 26 | swoole_process::signal(SIGCHLD, function ($signo) use ($workers) { 27 | $ret = swoole_process::wait(); 28 | $pid = $ret['pid']; 29 | unset($workers[$pid]); 30 | echo "Worker Exit, PID=" . $pid . PHP_EOL; 31 | }); 32 | /** 33 | * @var $process swoole_process 34 | */ 35 | foreach($workers as $pid => $process) 36 | { 37 | swoole_event_add($process->pipe, function($pipe) use ($process) { 38 | $recv = $process->read(); 39 | if ($recv) echo "From Worker: " . $recv; 40 | }); 41 | $process->write("hello worker[$pid]\n"); 42 | } 43 | } 44 | //同步主进程 45 | function master_sync($workers) 46 | { 47 | foreach($workers as $pid => $process) 48 | { 49 | $process->write("hello worker[$pid]\n"); 50 | echo "From Worker: ".$process->read(); 51 | } 52 | } 53 | function child_sync(swoole_process $worker) 54 | { 55 | //echo "Worker: start. PID=".$worker->pid."\n"; 56 | //recv data from master 57 | $recv = $worker->read(); 58 | echo "From Master: $recv\n"; 59 | //send data to master 60 | $worker->write("hello master\n"); 61 | sleep(2); 62 | $worker->exit(0); 63 | } 64 | function child_async(swoole_process $worker) 65 | { 66 | //echo "Worker: start. PID=".$worker->pid."\n"; 67 | //recv data from master 68 | $GLOBALS['worker'] = $worker; 69 | global $argv; 70 | $worker->name("{$argv[0]}: worker"); 71 | swoole_process::signal(SIGTERM, function($signal_num) use ($worker) { 72 | echo "signal call = $signal_num, #{$worker->pid}\n"; 73 | }); 74 | swoole_event_add($worker->pipe, function($pipe) use($worker) { 75 | $recv = $worker->read(); 76 | echo "From Master: $recv\n"; 77 | $worker->write("hello master\n"); 78 | }); 79 | } -------------------------------------------------------------------------------- /example/table/get.php: -------------------------------------------------------------------------------- 1 | get('350749960@qq.com')); 13 | var_dump($table->get('tianfenghan@qq.com')); 14 | var_dump($table->get('hello@qq.com')); -------------------------------------------------------------------------------- /example/table/set.php: -------------------------------------------------------------------------------- 1 | start(); 12 | 13 | 14 | //child 15 | function child1($worker){ 16 | echo 111; 17 | } -------------------------------------------------------------------------------- /example/task/client.php: -------------------------------------------------------------------------------- 1 | client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); 15 | 16 | $this->client->on('Connect', array($this, 'onConnect')); 17 | $this->client->on('Receive', array($this, 'onReceive')); 18 | $this->client->on('Close', array($this, 'onClose')); 19 | $this->client->on('Error', array($this, 'onError')); 20 | } 21 | 22 | public function connect() { 23 | $fp = $this->client->connect("127.0.0.1", 9502 , 1); 24 | if( !$fp ) { 25 | echo "Error: {$fp->errMsg}[{$fp->errCode}]\n"; 26 | return; 27 | } 28 | } 29 | 30 | public function onConnect( $cli) { 31 | fwrite(STDOUT, "Enter Msg:"); 32 | swoole_event_add(STDIN, function($fp){ 33 | global $cli; 34 | fwrite(STDOUT, "Enter Msg:"); 35 | $msg = trim(fgets(STDIN)); 36 | $cli->send( $msg ); 37 | }); 38 | } 39 | 40 | public function onReceive( $cli, $data ) { 41 | echo "Get Message From Server: {$data}\n"; 42 | } 43 | 44 | public function onClose( $cli) { 45 | echo "Client close connection\n"; 46 | } 47 | 48 | public function onError() { 49 | 50 | } 51 | 52 | public function send($data) { 53 | $this->client->send( $data ); 54 | } 55 | 56 | public function isConnected() { 57 | return $this->client->isConnected(); 58 | } 59 | } 60 | $cli = new Client(); 61 | $cli->connect(); -------------------------------------------------------------------------------- /example/task/server.php: -------------------------------------------------------------------------------- 1 | serv = new swoole_server("0.0.0.0", 9502); 16 | $this->serv->set(array( 17 | 'worker_num' => 4, 18 | 'daemonize' => false, 19 | 'max_request' => 10000, 20 | 'dispatch_mode' => 2, 21 | 'debug_mode'=> 1, 22 | 'task_worker_num' => 8 23 | )); 24 | 25 | $this->serv->on('Start', array($this, 'onStart')); 26 | $this->serv->on('Connect', array($this, 'onConnect')); 27 | $this->serv->on('Receive', array($this, 'onReceive')); 28 | $this->serv->on('Close', array($this, 'onClose')); 29 | 30 | // bind task callback 31 | $this->serv->on('Task', array($this, 'onTask')); 32 | $this->serv->on('Finish', array($this, 'onFinish')); 33 | $this->serv->start(); 34 | } 35 | public function onStart( $serv ) { 36 | cli_set_process_title('TaskMaster'); 37 | echo "Start\n"; 38 | } 39 | 40 | public function onConnect( $serv, $fd, $from_id ) { 41 | echo "工人: {$from_id}等待处理任务" . PHP_EOL; 42 | echo "Client {$fd} connect" . PHP_EOL; 43 | } 44 | 45 | public function onReceive( $serv, $fd, $from_id, $data ) { 46 | echo "工人: {$from_id} 开始处理任务" . PHP_EOL; 47 | echo "Get Message From Client {$fd}:{$data}\n"; 48 | 49 | // send a task to task worker. 50 | $serv->task($data); 51 | 52 | 53 | echo "继续执行任务\n"; 54 | } 55 | 56 | public function onClose( $serv, $fd, $from_id ) { 57 | echo "Client {$fd} close connection\n"; 58 | } 59 | 60 | public function onTask($serv, $task_id, $from_id, $data) { 61 | echo "任务: {$task_id} 由任务工人: {$from_id} 执行\n"; 62 | //echo "Data: {$data}\n"; 63 | 64 | //处理业务逻辑 65 | for($i = 0 ; $i < 4 ; $i ++ ) { 66 | sleep(1); 67 | echo "Taks {$task_id} Handle {$i} times...\n"; 68 | } 69 | 70 | return "$data -> OK"; 71 | } 72 | 73 | public function onFinish($serv, $task_id, $data) { 74 | echo "Task {$task_id} finish\n"; 75 | echo "Result: {$data}\n"; 76 | } 77 | } 78 | $server = new Server(); -------------------------------------------------------------------------------- /example/timer/basic.php: -------------------------------------------------------------------------------- 1 | serv = new swoole_server("0.0.0.0", 9503); 18 | $this->serv->set(array( 19 | 'worker_num' => 8, 20 | 'daemonize' => false, 21 | 'max_request' => 10000, 22 | 'dispatch_mode' => 2, 23 | 'debug_mode'=> 1 , 24 | )); 25 | 26 | $this->serv->on('WorkerStart', array($this, 'onWorkerStart')); 27 | $this->serv->on('Connect', array($this, 'onConnect')); 28 | $this->serv->on('Receive', array($this, 'onReceive')); 29 | $this->serv->on('Close', array($this, 'onClose')); 30 | 31 | // bind callback 32 | $this->serv->on('Timer', array($this, 'onTimer')); 33 | $this->serv->start(); 34 | } 35 | 36 | public function onWorkerStart( $serv , $worker_id) { 37 | // 在Worker进程开启时绑定定时器 38 | echo "onWorkerStart\n"; 39 | 40 | // 只有当worker_id为0时才添加定时器,避免重复添加 41 | if( $worker_id == 0 ) { 42 | $serv->addtimer(60000); 43 | $serv->addtimer(300000); 44 | $serv->addtimer(600000); 45 | } 46 | } 47 | 48 | public function onConnect( $serv, $fd, $from_id ) { 49 | echo "Client {$fd} connect\n"; 50 | } 51 | 52 | public function onReceive( swoole_server $serv, $fd, $from_id, $data ) { 53 | echo "Get Message From Client {$fd}:{$data}\n"; 54 | } 55 | 56 | public function onClose( $serv, $fd, $from_id ) { 57 | echo "Client {$fd} close connection\n"; 58 | } 59 | 60 | public function onTimer($serv, $interval) { 61 | switch( $interval ) { 62 | case 60000: { // 63 | echo "每分钟执行一次\n"; 64 | break; 65 | } 66 | case 300000:{ 67 | echo "每五分钟执行一次\n"; 68 | break; 69 | } 70 | case 600000:{ 71 | echo "每十分钟执行一次\n"; 72 | break; 73 | } 74 | } 75 | } 76 | } 77 | new TimerServer(); -------------------------------------------------------------------------------- /example/websocket/server.php: -------------------------------------------------------------------------------- 1 | set(['worker_num' => 4, 'user'=>'www', 'group'=>'www', 'daemonize'=>0]); 13 | 14 | //handshake成功之后回调, 和js的onopen对应 15 | $http->on('open', function($response) { 16 | echo "handshake success" . PHP_EOL; 17 | //print_r($response); 18 | }); 19 | 20 | //自定定握手规则,没有设置则用系统内置的(只支持version:13的) 21 | $http->on('handshake', function($request, $response) { 22 | print_r($request); 23 | 24 | if (!isset($request->header['sec-websocket-key'])) { 25 | //'Bad protocol implementation: it is not RFC6455.' 26 | $response->end(); 27 | return false; 28 | } 29 | 30 | if (0 === preg_match('#^[+/0-9A-Za-z]{21}[AQgw]==$#', $request->header['sec-websocket-key']) || 16 !== strlen(base64_decode($request->header['sec-websocket-key']))) { 31 | //Header Sec-WebSocket-Key is illegal; 32 | $response->end(); 33 | return false; 34 | } 35 | 36 | $headers = array( 37 | 'Upgrade' => 'websocket', 38 | 'Connection' => 'Upgrade', 39 | 'Sec-WebSocket-Accept' => ''. base64_encode(sha1($request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)), 40 | 'Sec-WebSocket-Version' => '13', 41 | 'KeedpAlive' => 'off', 42 | ); 43 | 44 | foreach($headers as $key => $val) { 45 | $response->header($key, $val); 46 | } 47 | $response->status(101); 48 | $response->end(); 49 | }); 50 | 51 | $http->on('message', function($response){ 52 | var_dump($response); 53 | 54 | $response->message($response->data); 55 | }); 56 | 57 | $http->on('request', function ($request, $response) { 58 | var_dump($request); 59 | $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); 60 | }); 61 | 62 | $http->on('close', function(){ 63 | echo "on close" . PHP_EOL; 64 | }); 65 | 66 | $http->start(); -------------------------------------------------------------------------------- /swoole-auto-complete-master/README.md: -------------------------------------------------------------------------------- 1 | swoole-auto-complete 2 | ==================== 3 | 4 | [Swoole](https://github.com/matyhtf/swoole) 在IDE下自动识别类、函数、宏,自动补全函数名 5 | 6 | Swoole 结构,便于开发过程中查看文档,以及屏蔽IDE undefined 提示,便于快速查看函数用法。 7 | 8 | 当前适用Swoole版本: 1.6.10 9 | 10 | ### 使用方式 11 | 12 | 普通IDE: 13 | 14 | 开发Swoole项目同时,在IDE中打开/导入本文件即可。 15 | 16 | 如果IDE自带 Include Path 功能(如:PhpStorm),设置该文件路径即可。 17 | 18 | PhpStorm 另一种方法: 19 | 20 | WinRAR打开 /plugins/php/lib/php.jar 文件 21 | 22 | 复制 swoole.php 到路径:com\jetbrains\php\lang\psi\stubs\data\ 23 | 24 | 当然你也可以直接使用已经做好的 [php.jar](https://github.com/EagleWu/swoole-auto-complete/tree/master/phpstorm) 直接覆盖即可。 25 | 26 | (php.jar 拷贝来源PhpStorm 7.1,其他版本如存在兼容性问题,请自行按上面方面进行处理,覆盖php.jar时注意备份) 27 | 28 | 保存文件,重启Phpstorm. 29 | 30 | 31 | PHPstorm使用演示(其他IDE同理): 32 | 33 | ![demo1](https://raw2.github.com/EagleWu/swoole-auto-complete/master/demo_img/01.png "demo1")
34 | ![demo2](https://raw2.github.com/EagleWu/swoole-auto-complete/master/demo_img/02.png "demo2")
35 | ![demo3](https://raw2.github.com/EagleWu/swoole-auto-complete/master/demo_img/03.png "demo3")
36 | ![demo4](https://raw2.github.com/EagleWu/swoole-auto-complete/master/demo_img/04.png "demo4")
37 | 38 | 使用php.jar包 39 | 40 | ![demo5](https://raw2.github.com/EagleWu/swoole-auto-complete/master/demo_img/05.png "demo5")
41 | 42 | -------------------------------------------------------------------------------- /swoole-auto-complete-master/Swoole.php: -------------------------------------------------------------------------------- 1 | /plugins/php/lib/php.jar 文件 15 | * 复制 swoole.php 到路径:com\jetbrains\php\lang\psi\stubs\data\ 16 | * 保存文件,重启Phpstorm. 17 | * 18 | * PS:替换前请备份php.jar 若发生错误便于恢复 :) 19 | * 20 | * Author:EagleWu 21 | * Date: 2014/01/17 22 | * 23 | */ 24 | 25 | 26 | /** 27 | * swoole_server_set函数用于设置swoole_server运行时的各项参数 28 | * 29 | * @param $serv 30 | * @param $arguments 31 | */ 32 | function swoole_server_set($serv, array $arguments) {} 33 | 34 | 35 | /** 36 | * 创建一个swoole server资源对象 37 | * 38 | * @param string $host 参数用来指定监听的ip地址,如127.0.0.1,或者外网地址,或者0.0.0.0监听全部地址 39 | * @param int $port 监听的端口,如9501,监听小于1024端口需要root权限,如果此端口被占用server-start时会失败 40 | * @param int $mode 运行的模式,swoole提供了3种运行模式,默认为多进程模式 41 | * @param string $sock_type 指定socket的类型,支持TCP/UDP、TCP6/UDP64种 42 | * @return int 43 | */ 44 | function swoole_server_create($host, $port, $mode = SWOOLE_PROCESS, $sock_type = SWOOLE_SOCK_TCP) {} 45 | 46 | 47 | /** 48 | * 增加监听的端口 49 | * 50 | * 您可以混合使用UDP/TCP,同时监听内网和外网端口 51 | * 业务代码中可以通过调用swoole_connection_info来获取某个连接来自于哪个端口 52 | * 53 | * @param object $serv 54 | * @param string $host 55 | * @param int $port 56 | * @return void 57 | */ 58 | function swoole_server_addlisten($serv, $host = '127.0.0.1', $port = 9502) {} 59 | 60 | 61 | /** 62 | * 设置定时器 63 | * 64 | * 第二个参数是定时器的间隔时间,单位为毫秒。 65 | * swoole定时器的最小颗粒是1毫秒,支持多个定时器。 66 | * 此函数可以用于worker进程中。或者通过swoole_server_set设置timer_interval来调整定时器最小间隔。 67 | * 68 | * 增加定时器后需要为Server设置onTimer回调函数,否则会造成严重错误。 69 | * 多个定时器都会回调此函数。 70 | * 在这个函数内需要自行switch,根据interval的值来判断是来自于哪个定时器。 71 | * 72 | * @param object $serv 73 | * @param int $interval 74 | * @return bool 75 | */ 76 | function swoole_server_addtimer($serv, $interval) {} 77 | 78 | 79 | /** 80 | * 设置Server的事件回调函数 81 | * 82 | * 第一个参数是swoole的资源对象 83 | * 第二个参数是回调的名称, 大小写不敏感,具体内容参考回调函数列表 84 | * 第三个函数是回调的PHP函数,可以是字符串,数组,匿名函数。 85 | * 86 | * 设置成功后返回true。如果$event_name填写错误将返回false。 87 | * 88 | * onConnect/onClose/onReceive 这3个回调函数必须设置,其他事件回调函数可选。 89 | * 如果设定了timer定时器,onTimer事件回调函数也必须设置 90 | * 91 | * @param object $serv 92 | * @param string $event_name 93 | * @param is_callable $event_callback_function 94 | * @return bool 95 | */ 96 | function swoole_server_handler($serv, $event_name, $event_callback_function) {} 97 | 98 | 99 | /** 100 | * 启动server,监听所有TCP/UDP端口 101 | * 102 | * 启动成功后会创建worker_num+2个进程。主进程+Manager进程+n*Worker进程。 103 | * 启动失败扩展内会抛出致命错误,请检查php error_log的相关信息。errno={number}是标准的Linux Errno,可参考相关文档。 104 | * 如果开启了log_file设置,信息会打印到指定的Log文件中。 105 | * 106 | * 如果想要在开机启动时,自动运行你的Server,可以在/etc/rc.local文件中加入: 107 | * 108 | * /usr/bin/php /data/webroot/www.swoole.com/server.php 109 | * 110 | * 常见的错误有及拍错方法: 111 | * 112 | * 1、bind端口失败,原因是其他进程已占用了此端口 113 | * 2、未设置必选回调函数,启动失败 114 | * 3、php有代码致命错误,请检查php的错误信息 115 | * 4、执行ulimit -c unlimited,打开core dump,查看是否有段错误 116 | * 5、关闭daemonize,关闭log,使错误信息可以打印到屏幕 117 | * 118 | * @param object $serv 119 | * @return bool 120 | */ 121 | function swoole_server_start($serv) {} 122 | 123 | 124 | /** 125 | * 平滑重启Server 126 | * 127 | * 一台繁忙的后端服务器随时都在处理请求,如果管理员通过kill进程方式来终止/重启服务器程序,可能导致刚好代码执行到一半终止。 128 | * 这种情况下会产生数据的不一致。如交易系统中,支付逻辑的下一段是发货,假设在支付逻辑之后进程被终止了。 129 | * 会导致用户支付了货币,但并没有发货,后果非常严重。 130 | * 131 | * Swoole提供了柔性终止/重启的机制,管理员只需要向SwooleServer发送特定的信号,Server的worker进程可以安全的结束。 132 | * 133 | * SIGTREM: 向主进程发送此信号服务器将安全终止 134 | * SIGUSR1: 向管理进程发送SIGUSR1信号,将平稳地restart所有worker进程,在PHP代码中可以调用swoole_server_reload($serv)完成此操作 135 | * 136 | * @param object $serv 137 | * @return void 138 | */ 139 | function swoole_server_reload($serv) {} 140 | 141 | 142 | /** 143 | * 关闭客户端连接 144 | * 145 | * Server主动close连接,也一样会触发onClose事件。 146 | * 不要在close之后写清理逻辑,应当放置到onClose回调中处理。 147 | * 148 | * @param object $serv 149 | * @param int $fd 150 | * @param int $from_id 151 | * @return bool 152 | */ 153 | function swoole_server_close($serv, $fd, $from_id = 0) {} 154 | 155 | 156 | /** 157 | * 向客户端发送数据 158 | * 159 | * $data的长度可以是任意的。扩展函数内会进行切分。 160 | * 如果是UDP协议,会直接在worker进程内发送数据包。 161 | * 发送成功会返回true,如果连接已被关闭或发送失败会返回false. 162 | * 163 | * @param object $serv 164 | * @param int $fd 165 | * @param string $data 166 | * @param int $from_id 167 | * @return bool 168 | */ 169 | function swoole_server_send($serv, $fd, $data, $from_id = 0) {} 170 | 171 | 172 | /** 173 | * 获取客户端连接的信息 174 | * 175 | * 返回数组含义: 176 | * from_id 来自哪个poll线程 177 | * from_fd 来自哪个server socket 178 | * from_port 来自哪个Server端口 179 | * remote_port 客户端连接的端口 180 | * remote_ip 客户端连接的ip 181 | * 182 | * 以下 v1.6.10 增加 183 | * connect_time 连接时间 184 | * last_time 最后一次发送数据的时间 185 | * 186 | * @param object $serv 187 | * @param int $fd 188 | * @return array on success or false on failure. 189 | */ 190 | function swoole_connection_info($serv, $fd) {} 191 | 192 | 193 | /** 194 | * 遍历当前Server所有的客户端连接 195 | * 196 | * 此函数接受3个参数,第一个参数是server的资源对象,第二个参数是起始fd,第三个参数是每页取多少条,最大不得超过100。 197 | * 调用成功将返回一个数字索引数组,元素是取到的$fd。 198 | * 数组会按从小到大排序,最后一个$fd作为新的start_fd再次尝试获取。 199 | * 200 | * @param object $serv 201 | * @param int $start_fd 202 | * @param int $pagesize 203 | * @return array on success or false on failure 204 | */ 205 | function swoole_connection_list($serv, $start_fd = 0, $pagesize = 10) {} 206 | 207 | 208 | /** 209 | * 设置进程的名称 210 | * 211 | * 修改进程名称后,通过ps命令看到的将不再是php your_file.php。而是设定的字符串。 212 | * 此函数接受一个字符串参数。 213 | * 此函数与PHP5.5提供的cli_set_process_title功能是相同的,但swoole_set_process_name可用于PHP5.2之上的任意版本。 214 | * 215 | * @param string $name 216 | * @return void 217 | */ 218 | function swoole_set_process_name($name) {} 219 | 220 | 221 | /** 222 | * 将Socket加入到swoole的reactor事件监听中 223 | * 224 | * 此函数可以用在Server或Client模式下 225 | * 226 | * 参数1为socket的文件描述符; 227 | * 参数2为回调函数,可以是字符串函数名、对象+方法、类静态方法或匿名函数,当此socket可读是回调制定的函数。 228 | * 229 | * Server程序中会增加到server socket的reactor中。 230 | * Client程序中,如果是第一次调用此函数会自动创建一个reactor,并添加此socket,程序将在此处进行wait。 231 | * swoole_event_add函数之后的代码不会执行。当调用swoole_event_exit才会停止wait,程序继续向下执行。 232 | * 第二次调用只增加此socket到reactor中,开始监听事件 233 | * 234 | * @param int $sock 235 | * @param is_callable $callback 236 | * @return bool 237 | */ 238 | function swoole_event_add($sock, $read_callback = null, $write_callback = null, $flag = null) {} 239 | 240 | /** 241 | * 修改socket的事件设置 242 | * 可以修改可读/可写事件的回调设置和监听的事件类型 243 | * 244 | * @param $sock 245 | * @param $read_callback 246 | * @param null $write_callback 247 | * @param null $flag 248 | */ 249 | function swoole_event_set($sock, $read_callback = null, $write_callback = null, $flag = null) {} 250 | 251 | /** 252 | * 从reactor中移除监听的Socket 253 | * 254 | * swoole_event_del应当与 swoole_event_add 成对使用 255 | * 256 | * @param int $sock 257 | * @return bool 258 | */ 259 | function swoole_event_del($sock) {} 260 | 261 | 262 | /** 263 | * 退出事件轮询 264 | * 265 | * @return void 266 | */ 267 | function swoole_event_exit() {} 268 | 269 | /** 270 | * 获取MySQLi的socket文件描述符 271 | * 272 | * 可将mysql的socket增加到swoole中,执行异步MySQL查询。 273 | * 如果想要使用异步MySQL,需要在编译swoole时制定--enable-async-mysql 274 | * swoole_get_mysqli_sock仅支持mysqlnd驱动,php5.4以下版本不支持此特性 275 | * 276 | * @param resource $db mysqli 277 | * @return int 278 | */ 279 | function swoole_get_mysqli_sock($db) {} 280 | 281 | 282 | /** 283 | * 投递异步任务到task_worker池中 284 | * 285 | * 此函数会立即返回,worker进程可以继续处理新的请求。 286 | * 此功能用于将慢速的任务异步地去执行,比如一个聊天室服务器,可以用它来进行发送广播。 287 | * 当任务完成时,在task_worker中调用swoole_server_finish($serv, "finish"); 288 | * 告诉worker进程此任务已完成。当然swoole_server_finish是可选的。 289 | * 290 | * 发送的$data必须为字符串,如果是数组或对象,请在业务代码中进行serialize处理,并在onTask/onFinish中进行unserialize。 291 | * $data可以为二进制数据,最大长度为8K。字符串可以使用gzip进行压缩。 292 | * 293 | * 使用swoole_server_task必须为Server设置onTask和onFinish回调, 294 | * 否则swoole_server_start会失败。此回调函数会在task_worker进程中被调用。 295 | * 296 | * 函数会返回一个$task_id数字,表示此任务的ID。如果有finish回应,onFinish回调中会携带$task_id参数。 297 | * 298 | * task_worker的数量在swoole_server_set参数中调整,如task_worker_num => 64,表示启动64个进程来接收异步任务。 299 | * swoole_server_task和swoole_server_finish可发送$data的长度最大不得超过8K,此参数受SW_BUFFER_SIZE宏控制。 300 | * 301 | * @param object $serv 302 | * @param string $data 303 | * @return int $task_id 304 | */ 305 | function swoole_server_task($serv, $data) {} 306 | 307 | 308 | /** 309 | * task_worker进程中通知worker进程,投递的任务已完成 310 | * 311 | * 此函数可以传递结果数据给worker进程 312 | * 使用swoole_server_finish函数必须为Server设置onFinish回调函数。此函数只可用于task_worker进程的onTask回调中 313 | * swoole_server_finish是可选的。如果worker进程不关心任务执行的结果,可以不调用此函数 314 | * 315 | * @param object $serv 316 | * @param string $response 317 | * @return void 318 | */ 319 | function swoole_server_finish($serv, $response) {} 320 | 321 | 322 | /** 323 | * 删除定时器 324 | * 325 | * $interval 参数为定时器的间隔时间 326 | * 根据定时器时间区分不同的定时器 327 | * 328 | * @param object $serv 329 | * @param int $interval 330 | * @return void 331 | */ 332 | function swoole_server_deltimer($serv, $interval) {} 333 | 334 | 335 | /** 336 | * 关闭服务器 337 | * 338 | * 此函数可以用在worker进程内。 339 | * 340 | * @param object $serv 341 | * @return void 342 | */ 343 | function swoole_server_shutdown($serv) {} 344 | 345 | 346 | /** 347 | * 投递堵塞任务到task进程池 348 | * 349 | * taskwait与task方法作用相同,用于投递一个异步的任务到task进程池去执行。 350 | * 与task不同的是taskwait是阻塞等待的,直到任务完成或者超时返回。 351 | * $result为任务执行的结果,由$serv->finish函数发出。如果此任务超时,这里会返回false。 352 | * 353 | * taskwait是阻塞接口,如果你的Server是全异步的请不要使用它 354 | * 355 | * @param string $task_data 356 | * @param float $timeout 357 | * @return string 358 | */ 359 | function swoole_server_taskwait($task_data, $timeout = 0.5) {} 360 | 361 | /** 362 | * 进行事件轮询 363 | * 364 | * PHP5.4之前的版本没有在ZendAPI中加入注册shutdown函数。所以swoole无法在脚本结尾处自动进行事件轮询。 365 | * 低于5.4的版本,需要在你的PHP脚本结尾处加swoole_event_wait函数,使脚本开始进行事件轮询。 366 | * 367 | * 5.4或更高版本不需要加此函数。 368 | * 369 | * @return void 370 | */ 371 | function swoole_event_wait() {} 372 | 373 | /** 374 | * 添加定时器,可用于客户端环境和fpm中 375 | * @param $interval 376 | * @param $callback 377 | */ 378 | function swoole_timer_add($interval, $callback) {} 379 | 380 | /** 381 | * 删除定时器 382 | * @param $interval 383 | * @param $callback 384 | */ 385 | function swoole_timer_del($interval) {} 386 | 387 | /** 388 | * swoole_client 389 | */ 390 | class swoole_client { 391 | 392 | /** 393 | * 函数执行错误会设置该变量 394 | * 395 | * @var 396 | */ 397 | public $errCode; 398 | 399 | 400 | /** 401 | * socket的文件描述符 402 | * 403 | * PHP代码中可以使用: 404 | * $sock = fopen("php://fd/".$swoole_client->sock); 405 | * 406 | * 将swoole_client的socket转换成一个stream socket。可以调用fread/fwrite/fclose等函数进程操作。 407 | * swoole_server中的$fd不能用此方法转换,因为$fd只是一个数字,$fd文件描述符属于主进程 408 | * $swoole_client->sock可以转换成int作为数组的key. 409 | * 410 | * @var int 411 | */ 412 | public $sock; 413 | 414 | 415 | /** 416 | * swoole_client构造函数 417 | * 418 | * @param int $sock_type 指定socket的类型,支持TCP/UDP、TCP6/UDP64种 419 | * @param int $sync_type SWOOLE_SOCK_SYNC/SWOOLE_SOCK_ASYNC 同步/异步 420 | */ 421 | public function __construct($sock_type, $sync_type = SWOOLE_SOCK_SYNC) {} 422 | 423 | 424 | /** 425 | * 连接到远程服务器 426 | * 427 | * @param string $host 是远程服务器的地址 v1.6.10+ 支持填写域名 Swoole会自动进行DNS查询 428 | * @param int $port 是远程服务器端口 429 | * @param float $timeout 是网络IO的超时,单位是s,支持浮点数。默认为0.1s,即100ms 430 | * @param int $flag 参数在UDP类型时表示是否启用udp_connect。设定此选项后将绑定$host与$port,此UDP将会丢弃非指定host/port的数据包。 431 | * 在send/recv前必须使用swoole_client_select来检测是否完成了连接 432 | * @return bool 433 | */ 434 | public function connect($host, $port, $timeout = 0.1, $flag = 0) {} 435 | 436 | 437 | /** 438 | * 向远程服务器发送数据 439 | * 440 | * 参数为字符串,支持二进制数据。 441 | * 成功发送返回的已发数据长度 442 | * 失败返回false,并设置$swoole_client->errCode 443 | * 444 | * @param string $data 445 | * @return bool 446 | */ 447 | public function send($data) {} 448 | 449 | 450 | /** 451 | * 从服务器端接收数据 452 | * 453 | * 如果设定了$waitall就必须设定准确的$size,否则会一直等待,直到接收的数据长度达到$size 454 | * 如果设置了错误的$size,会导致recv超时,返回 false 455 | * 调用成功返回结果字符串,失败返回 false,并设置$swoole_client->errCode属性 456 | * 457 | * @param int $size 接收数据的最大长度 458 | * @param bool $waitall 是否等待所有数据到达后返回 459 | * @return string 460 | */ 461 | public function recv($size = 65535, $waitall = false) {} 462 | 463 | /** 464 | * 关闭远程连接 465 | * 466 | * swoole_client对象在析构时会自动close 467 | * 468 | * @return bool 469 | */ 470 | public function close() {} 471 | 472 | /** 473 | * 注册异步事件回调函数 474 | * 475 | * @return bool 476 | */ 477 | public function on($event_name, $callback_function){} 478 | } 479 | 480 | class swoole_server 481 | { 482 | /** 483 | * 主进程PID 484 | * @var int 485 | */ 486 | public $master_pid; 487 | /** 488 | * 管理进程PID 489 | * @var int 490 | */ 491 | public $manager_pid; 492 | function __construct($host, $port, $mode = 3, $tcp_or_udp = 1){} 493 | 494 | /** 495 | * 设置事件回调函数 496 | * @param $event_name 497 | * @param $callback_function 498 | */ 499 | function on($event_name, $callback_function){} 500 | 501 | /** 502 | * 设置运行时参数 503 | * @param array $config 504 | */ 505 | function set(array $config){} 506 | 507 | /** 508 | * 启动服务器 509 | */ 510 | function start(){} 511 | 512 | /** 513 | * 发送数据到客户端连接 514 | * @param int $fd 515 | * @param $response 516 | * @param int $from_id 517 | * @return bool 518 | */ 519 | function send(int $fd, $response, $from_id = 0){} 520 | 521 | /** 522 | * 关闭连接 523 | * @param int $fd 524 | * @param int $from_id 525 | */ 526 | function close(int $fd, $from_id = 0){} 527 | 528 | /** 529 | * 投递任务到task_worker进程去执行,并阻塞等待结果返回 530 | * @param string $task_data 531 | * @param float $timeout 532 | * @return string 533 | */ 534 | function taskwait(string $task_data, float $timeout = 0.5){} 535 | 536 | /** 537 | * 投递一个异步任务 538 | * @param string $task_data 539 | * @param float $timeout 540 | * @return int 541 | */ 542 | function task(string $task_data, int $dst_worker_id = -1){} 543 | 544 | /** 545 | * 任务完成后发送结果到对应的worker进程 546 | * @param string $task_data 547 | */ 548 | function finish(string $task_data){} 549 | 550 | /** 551 | * 进行心跳检查,返回心跳超过约定时间的连接 552 | */ 553 | function heartbeat(){} 554 | 555 | /** 556 | * 返回连接的信息,支持TCP/UDP 557 | * array ( 558 | * 'from_id' => 0, 559 | * 'from_fd' => 12, 560 | * 'connect_time' => 1392895129, 561 | * 'last_time' => 1392895137, 562 | * 'from_port' => 9501, 563 | * 'remote_port' => 48918, 564 | * 'remote_ip' => '127.0.0.1', 565 | * ) 566 | * @return array | bool 567 | */ 568 | public function connection_info($fd, $from_id = -1){} 569 | 570 | /** 571 | * 遍历所有Server的TCP连接,这个接口是基于共享内存的,可以得到所有worker进程内的连接 572 | * 573 | * @return array | bool 574 | */ 575 | public function connection_list($start_fd = -1, $pagesize = 100){} 576 | 577 | /** 578 | * 重启所有worker进程 579 | * @return null 580 | */ 581 | public function reload(){} 582 | 583 | /** 584 | * 关闭服务器 585 | * @return null 586 | */ 587 | public function shutdown(){} 588 | 589 | /** 590 | * 增加监听端口 591 | * @param $host 592 | * @param $port 593 | * @param $type 594 | */ 595 | public function addlistener($host, $port, $type = SWOOLE_SOCK_TCP){} 596 | 597 | /** 598 | * 增加定时器 599 | * @param $interval 600 | */ 601 | public function addtimer($interval){} 602 | 603 | /** 604 | * 删除定时器 605 | * @param $interval 606 | */ 607 | public function deltimer($interval){} 608 | } 609 | 610 | 611 | /** 612 | * Class swoole_lock 613 | */ 614 | class swoole_lock { 615 | 616 | /** 617 | * @param int $type 为锁的类型 618 | * @param string $lockfile 当类型为SWOOLE_FILELOCK时必须传入,指定文件锁的路径 619 | * 注意每一种类型的锁支持的方法都不一样。如读写锁、文件锁可以支持 $lock->lock_read()。 620 | * 另外除文件锁外,其他类型的锁必须在父进程内创建,这样fork出的子进程之间才可以互相争抢锁。 621 | */ 622 | public function __construct($type, $lockfile = NULL) {} 623 | 624 | 625 | /** 626 | * 加锁操作 627 | * 628 | * 如果有其他进程持有锁,那这里将进入阻塞,直到持有锁的进程unlock。 629 | */ 630 | public function lock() {} 631 | 632 | 633 | /** 634 | * 加锁操作 635 | * 636 | * 与lock方法不同的是,trylock()不会阻塞,它会立即返回。 637 | * 当返回false时表示抢锁失败,有其他进程持有锁。返回true时表示加锁成功,此时可以修改共享变量。 638 | * 639 | * SWOOlE_SEM 信号量没有trylock方法 640 | */ 641 | public function trylock() {} 642 | 643 | 644 | /** 645 | * 释放锁 646 | */ 647 | public function unlock() {} 648 | 649 | 650 | /** 651 | * 阻塞加锁 652 | * 653 | * lock_read方法仅可用在读写锁(SWOOLE_RWLOCK)和文件锁(SWOOLE_FILELOCK)中,表示仅仅锁定读。 654 | * 在持有读锁的过程中,其他进程依然可以获得读锁,可以继续发生读操作。但不能$lock->lock()或$lock->trylock(),这两个方法是获取独占锁的。 655 | * 656 | * 当另外一个进程获得了独占锁(调用$lock->lock/$lock->trylock)时,$lock->lock_read()会发生阻塞,直到持有锁的进程释放。 657 | */ 658 | public function lock_read() {} 659 | 660 | 661 | /** 662 | * 非阻塞加锁 663 | * 664 | * 此方法与lock_read相同,但是非阻塞的。调用会立即返回,必须检测返回值以确定是否拿到了锁。 665 | */ 666 | public function trylock_read() {} 667 | } 668 | 669 | 670 | /** 671 | * IO事件循环 672 | * 673 | * 674 | * swoole_client的并行处理中用了select来做IO事件循环。为什么要用select呢? 675 | * 因为client一般不会有太多连接,而且大部分socket会很快接收到响应数据。 676 | * 在少量连接的情况下select比epoll性能更好,另外select更简单。 677 | * 678 | * $read,$write,$error分别是可读/可写/错误的文件描述符。 679 | * 这3个参数必须是数组变量的引用。数组的元素必须为swoole_client对象。 680 | * $timeout参数是select的超时时间,单位为秒,接受浮点数。 681 | * 682 | * 调用成功后,会返回事件的数量,并修改$read/$write/$error数组。 683 | * 使用foreach遍历数组,然后执行$item->recv/$item->send来收发数据。 684 | * 或者调用$item->close()或unset($item)来关闭socket。 685 | * 686 | * 687 | * @param array $read 可读 688 | * @param array $write 可写 689 | * @param array $error 错误 690 | * @param float $timeout 691 | */ 692 | function swoole_client_select(array &$read, array &$write, array &$error, float $timeout) {} 693 | 694 | /** 695 | * swoole进程管理类 696 | * 内置IPC通信支持,子进程和主进程之间可以方便的通信 697 | * 支持标准输入输出重定向,子进程内echo,会发送到管道中,而不是输出屏幕 698 | * Class swoole_process 699 | */ 700 | class swoole_process 701 | { 702 | /** 703 | * 进程的PID 704 | * @var int 705 | */ 706 | public $pid; 707 | 708 | /** 709 | * 管道PIPE 710 | * @var int 711 | */ 712 | public $pipe; 713 | 714 | /** 715 | * @param mixed $callback 子进程的回调函数 716 | * @param bool $redirect_stdin_stdout 是否重定向标准输入输出 717 | * @param bool $create_pipe 是否创建管道 718 | */ 719 | function __construct($callback, $redirect_stdin_stdout = false, $create_pipe = true){} 720 | 721 | /** 722 | * 向管道内写入数据 723 | * @param string $data 724 | */ 725 | function write($data){} 726 | 727 | /** 728 | * 从管道内读取数据 729 | * @param int $buffer_len 最大读取的长度 730 | * @return string 731 | */ 732 | function read($buffer_len = 8192){} 733 | 734 | /** 735 | * 退出子进程,实际函数名为exit,IDE将exit识别为关键词了,会有语法错误,所以这里叫_exit 736 | */ 737 | function _exit($code = 0){} 738 | 739 | /** 740 | * 阻塞等待子进程退出,并回收 741 | * 成功返回一个数组包含子进程的PID和退出状态码 742 | * 如array('code' => 0, 'pid' => 15001),失败返回false 743 | * @return false | array 744 | */ 745 | static function wait(){} 746 | 747 | /** 748 | * 启动子进程 749 | * @return int 750 | */ 751 | function start(){} 752 | } 753 | 754 | define('SWOOLE_VERSION', '1.6.9'); //当前Swoole的版本号 755 | 756 | /** 757 | * new swoole_server 构造函数参数 758 | */ 759 | define('SWOOLE_BASE', 1); //使用Base模式,业务代码在Reactor中直接执行 760 | define('SWOOLE_THREAD', 2); //使用线程模式,业务代码在Worker线程中执行 761 | define('SWOOLE_PROCESS', 3); //使用进程模式,业务代码在Worker进程中执行 762 | 763 | /** 764 | * new swoole_client 构造函数参数 765 | */ 766 | define('SWOOLE_SOCK_TCP', 1); //创建tcp socket 767 | define('SWOOLE_SOCK_TCP6', 3); //创建tcp ipv6 socket 768 | define('SWOOLE_SOCK_UDP', 2); //创建udp socket 769 | define('SWOOLE_SOCK_UDP6', 4); //创建udp ipv6 socket 770 | 771 | define('SWOOLE_TCP', 1); //创建tcp socket 772 | define('SWOOLE_TCP6', 2); //创建tcp ipv6 socket 773 | define('SWOOLE_UDP', 3); //创建udp socket 774 | define('SWOOLE_UDP6', 4); //创建udp ipv6 socket 775 | define('SWOOLE_UNIX_DGRAM', 5); 776 | define('SWOOLE_UNIX_STREAM', 6); 777 | 778 | define('SWOOLE_SOCK_SYNC', 0); //同步客户端 779 | define('SWOOLE_SOCK_ASYNC', 1); //异步客户端 780 | 781 | define('SWOOLE_SYNC', 0); //同步客户端 782 | define('SWOOLE_ASYNC', 1); //异步客户端 783 | 784 | /** 785 | * new swoole_lock构造函数参数 786 | */ 787 | define('SWOOLE_FILELOCK', 2); //创建文件锁 788 | define('SWOOLE_MUTEX', 3); //创建互斥锁 789 | define('SWOOLE_RWLOCK', 1); //创建读写锁 790 | define('SWOOLE_SPINLOCK', 5); //创建自旋锁 791 | define('SWOOLE_SEM', 4); //创建信号量 792 | 793 | define('SWOOLE_EVENT_WRITE', 1); 794 | define('SWOOLE_EVENT_READ', 2); 795 | -------------------------------------------------------------------------------- /swoole-auto-complete-master/phpstorm/php.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moxiaobai/swoole-game/920f9ff4cb26c3d1ce686e52ff69443c99e6f79e/swoole-auto-complete-master/phpstorm/php.jar --------------------------------------------------------------------------------