├── .gitattributes ├── Applications └── RayP2P │ ├── Events.php │ ├── start_businessworker.php │ ├── start_gateway.php │ └── start_register.php ├── LICENSE ├── README.md ├── README_CN.md ├── config.php ├── start_all.php ├── start_gateway.php ├── start_register.php ├── start_worker.php └── vendor ├── autoload.php ├── composer ├── ClassLoader.php ├── LICENSE ├── autoload_classmap.php ├── autoload_namespaces.php ├── autoload_psr4.php ├── autoload_real.php ├── autoload_static.php └── installed.json └── workerman ├── _root_RayP2P_start.php.pid ├── _root_php-signal_start.php.pid ├── gateway-worker ├── .gitignore ├── MIT-LICENSE.txt ├── README.md ├── composer.json └── src │ ├── BusinessWorker.php │ ├── Gateway.php │ ├── Lib │ ├── Context.php │ ├── Db.php │ ├── DbConnection.php │ └── Gateway.php │ ├── Protocols │ └── GatewayProtocol.php │ └── Register.php ├── workerman.log └── workerman ├── .gitignore ├── Autoloader.php ├── Connection ├── AsyncTcpConnection.php ├── AsyncUdpConnection.php ├── ConnectionInterface.php ├── TcpConnection.php └── UdpConnection.php ├── Events ├── Ev.php ├── Event.php ├── EventInterface.php ├── Libevent.php ├── React │ ├── Base.php │ ├── ExtEventLoop.php │ ├── ExtLibEventLoop.php │ └── StreamSelectLoop.php ├── Select.php └── Swoole.php ├── Lib ├── Constants.php └── Timer.php ├── MIT-LICENSE.txt ├── Protocols ├── Frame.php ├── Http.php ├── Http │ └── mime.types ├── ProtocolInterface.php ├── Text.php ├── Websocket.php └── Ws.php ├── README.md ├── WebServer.php ├── Worker.php └── composer.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Applications/RayP2P/Events.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | 15 | /** 16 | * 用于检测业务代码死循环或者长时间阻塞等问题 17 | * 如果发现业务卡死,可以将下面declare打开(去掉//注释),并执行php start.php reload 18 | * 然后观察一段时间workerman.log看是否有process_timeout异常 19 | */ 20 | //declare(ticks=1); 21 | 22 | use \GatewayWorker\Lib\Gateway; 23 | use \Workerman\Lib\Timer; 24 | use \MaxMind\Db\Reader; 25 | 26 | /** 27 | * 主逻辑 28 | * 主要是处理 onConnect onMessage onClose 三个方法 29 | * onConnect 和 onClose 如果不需要可以不用实现并删除 30 | */ 31 | class Events 32 | { 33 | /** 34 | * 当客户端连接时触发 35 | * 如果业务不需此回调可以删除onConnect 36 | * 37 | * @param int $client_id 连接id 38 | */ 39 | public static function onWorkerStart($worker) 40 | { 41 | 42 | } 43 | 44 | /** 45 | * 当客户端连接时触发 46 | * 如果业务不需此回调可以删除onConnect 47 | * 48 | * @param int $client_id 连接id 49 | */ 50 | public static function onWebSocketConnect($client_id,$data) 51 | { 52 | if(isset($data['get']['id']) && $data['get']['id']!=''){ 53 | //绑定 uniqid 54 | Gateway::bindUid($client_id, $data['get']['id']); 55 | } 56 | } 57 | 58 | /** 59 | * 当客户端发来消息时触发 60 | * @param int $client_id 连接id 61 | * @param mixed $message 具体消息 62 | */ 63 | public static function onMessage($client_id, $message) 64 | { 65 | $data = json_decode($message,true); 66 | if(!$data){ 67 | return; 68 | } 69 | switch($data['action']){ 70 | default: 71 | if(!Gateway::isUidOnline($data['to_peer_id'])){ 72 | $msg = array( 73 | "action"=>"signal", 74 | "from_peer_id"=>$data['to_peer_id'], 75 | ); 76 | Gateway::sendToUid($data['peer_id'], json_encode($msg)); 77 | }else{ 78 | $msg = array( 79 | "action"=>"signal", 80 | "from_peer_id"=>$data['peer_id'], 81 | "data"=>$data['data'], 82 | ); 83 | Gateway::sendToUid($data['to_peer_id'], json_encode($msg)); 84 | } 85 | break; 86 | 87 | } 88 | } 89 | 90 | /** 91 | * 当用户断开连接时触发 92 | * @param int $client_id 连接id 93 | */ 94 | public static function onClose($client_id) 95 | { 96 | 97 | } 98 | } -------------------------------------------------------------------------------- /Applications/RayP2P/start_businessworker.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | use \Workerman\Worker; 15 | use \Workerman\WebServer; 16 | use \GatewayWorker\Gateway; 17 | use \GatewayWorker\BusinessWorker; 18 | use \Workerman\Autoloader; 19 | 20 | // 自动加载类 21 | require_once __DIR__ . '/../../vendor/autoload.php'; 22 | require __DIR__ . '/../../config.php'; 23 | 24 | // bussinessWorker 进程 25 | $worker = new BusinessWorker(); 26 | // worker名称 27 | $worker->name = 'Signaler Worker'; 28 | // bussinessWorker进程数量 29 | $worker->count = $config['bussinessWorkers']; 30 | // 服务注册地址 31 | $worker->registerAddress = $config['registerAddress'].':'.$config['registerPort']; 32 | 33 | // 如果不是在根目录启动,则运行runAll方法 34 | if(!defined('GLOBAL_START')) 35 | { 36 | Worker::runAll(); 37 | } 38 | 39 | -------------------------------------------------------------------------------- /Applications/RayP2P/start_gateway.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | use \Workerman\Worker; 15 | use \Workerman\WebServer; 16 | use \GatewayWorker\Gateway; 17 | use \GatewayWorker\BusinessWorker; 18 | use \Workerman\Autoloader; 19 | 20 | // 自动加载类 21 | require_once __DIR__ . '/../../vendor/autoload.php'; 22 | require __DIR__ . '/../../config.php'; 23 | 24 | 25 | // gateway 进程,这里使用Text协议,可以用telnet测试 26 | if($config['wss']){ 27 | $context = array( 28 | 'ssl' => array( 29 | 'local_cert' => $config['wss_cert'], 30 | 'local_pk' => $config['wss_key'], 31 | 'verify_peer' => false, 32 | // 'allow_self_signed' => true, //If you are using self signed certification. please remove the comment. 33 | ) 34 | ); 35 | $gateway = new Gateway("websocket://0.0.0.0:{$config['gatewayPort']}",$context); 36 | $gateway->transport = 'ssl'; 37 | }else{ 38 | $gateway = new Gateway("websocket://0.0.0.0:{$config['gatewayPort']}"); 39 | } 40 | // gateway名称,status方便查看 41 | $gateway->name = 'Signaler GateWay'; 42 | // gateway进程数 43 | $gateway->count = $config['gatewayWorkers']; 44 | // 本机ip,分布式部署时使用内网ip 45 | $gateway->lanIp = $config['lanIP']; 46 | // 内部通讯起始端口,假如$gateway->count=4,起始端口为4000 47 | // 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口 48 | $gateway->startPort = 2900; 49 | // 服务注册地址 50 | $gateway->registerAddress = $config['registerAddress'].':'.$config['registerPort']; 51 | // 心跳间隔 52 | $gateway->pingInterval = 10; 53 | // RFC格式心跳 54 | $gateway->pingRfc = true; 55 | // 心跳无响应限制 56 | $gateway->pingNotResponseLimit = 0; 57 | // 如果不是在根目录启动,则运行runAll方法 58 | if(!defined('GLOBAL_START')) 59 | { 60 | Worker::runAll(); 61 | } 62 | 63 | -------------------------------------------------------------------------------- /Applications/RayP2P/start_register.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | use \Workerman\Worker; 15 | use \GatewayWorker\Register; 16 | 17 | // 自动加载类 18 | require_once __DIR__ . '/../../vendor/autoload.php'; 19 | require __DIR__ . '/../../config.php'; 20 | // register 必须是text协议 21 | $register = new Register('text://0.0.0.0:'.$config['registerPort']); 22 | $register->name = 'Signaler Register'; 23 | 24 | // 如果不是在根目录启动,则运行runAll方法 25 | if(!defined('GLOBAL_START')) 26 | { 27 | Worker::runAll(); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Linkec 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-signaler 2 | 3 | [中文文档](README_CN.md "中文文档") 4 | 5 | ## Introduction 6 | This is a project build with [Workerman](https://github.com/walkor/Workerman "Workerman"). 7 | 8 | Cloud support [hlsjs-p2p-engine](https://github.com/cdnbye/hlsjs-p2p-engine "hlsjs-p2p-engine") signaler service. 9 | 10 | Technically support all WebRTC signaler service. 11 | 12 | Written in full pure PHP. High performance and support cluster. 13 | 14 | ## Free Public Signal Service 15 | 16 | Websocket Address: wss://free.freesignal.net 17 | 18 | We are maintain a public and free signal service for everybody. 19 | 20 | Currently we use 21 | 22 | 2 servers for gateway 23 | 24 | 2 servers for worker 25 | 26 | 1 server for register 27 | 28 | We invite you to use our service, we will do the best as we can. 29 | 30 | Websocket Address: wss://free.freesignal.net 31 | 32 | ## Requires 33 | 1. PHP 5.3 or Higher 34 | 2. A POSIX compatible operating system (Linux, OSX, BSD) 35 | 3. POSIX and PCNTL extensions for PHP 36 | 37 | ## Install 38 | ### CentOS 39 | 40 | yum install php-cli php-process git gcc php-devel php-pear libevent-devel -y 41 | 42 | Basically this is already support you to run this project. 43 | But if you are face to more than 1000 connections, please read Optimize character. 44 | 45 | ### Optimize 46 | 47 | If you are using PHP 5.3.3, you can only install libevent extension. 48 | 49 | pecl install channel://pecl.php.net/libevent-0.1.0 50 | 51 | //Attention: "libevent installation [autodetect]:" message Enter 52 | 53 | echo extension=libevent.so > /etc/php.d/libevent.ini 54 | 55 | If your PHP version is higher than 5.3.3, install event extension will better. 56 | 57 | pecl install event 58 | 59 | //Attention: Include libevent OpenSSL support [yes] : type "no" then Enter, 60 | 61 | //Attention: PHP Namespace for all Event classes :type "yes" then Enter 62 | 63 | //Otherwise just Enter. 64 | 65 | echo extension=event.so > /etc/php.d/event.ini 66 | 67 | Download programe. 68 | 69 | git clone https://github.com/RayP2P/php-signaler 70 | 71 | ## Configure 72 | 73 | all you need to focus is [config.php](https://github.com/RayP2P/php-signaler/blob/master/config.php "config.php") 74 | 75 | //Gateway LanIP,change it when you are using in cluster.(public IP is also could be use, but not suggested.) 76 | 'lanIP'=>'127.0.0.1', 77 | //Register Address 78 | 'registerAddress'=>'127.0.0.1', 79 | //Register Listen Port 80 | 'registerPort'=>1238, 81 | //Gateway Listen Port 82 | 'gatewayPort'=>80, 83 | //using wss:// or ws:// 84 | 'wss'=>false, 85 | //wss cert 86 | 'wss_cert'=>'/root/server.crt', 87 | //wss key 88 | 'wss_key'=>'/root/server.key', 89 | //Gateway Workers 90 | 'gatewayWorkers'=>4, 91 | //Bussiness Workers 92 | 'bussinessWorkers'=>4, 93 | 94 | ## Run in single server. 95 | 96 | php php-signaler/start_all.php 97 | 98 | //test your signaler service. 99 | //if the test is success, add " -d" argument to running the service in daemon mode. 100 | 101 | php php-signaler/start_all.php -d 102 | 103 | ## Run in cluster mode.(at least need 2 servers,suggest at least 3 servers.) 104 | 105 | // Keep all server have the same content in config.php expect "lanIP" 106 | // lanIP is only thing you must to change when you setting up Gateway Server. 107 | 108 | //1. run register server.[only need setup one server] 109 | //This server is connecting gateway and worker.it will just a low loadAvg server. 110 | 111 | php php-signaler/start_register.php 112 | 113 | //2. run worker server.[when the loadAvg is higher than expect, you need add more.] 114 | //This server is working to process all the logical. 115 | 116 | php php-signaler/start_worker.php 117 | 118 | //3. run gateway server.[when the loadAvg is higher than expect, you need add more.] 119 | //This server is face to user, it needs public IP address. 120 | 121 | php php-signaler/start_gateway.php 122 | 123 | //4. Finally , test your signaler service. 124 | //if the test is success, add " -d" argument to running the service in daemon mode. 125 | 126 | php php-signaler/start_register.php -d 127 | php php-signaler/start_worker.php -d 128 | php php-signaler/start_gateway.php -d 129 | 130 | ## Configure hlsjs-p2p-engine 131 | 132 | 1.Change the signal address to your address 133 | ### Example 134 | var hlsjsConfig = { 135 | p2pConfig: { 136 | wsSignalerAddr: 'wss://example.com/ws', 137 | } 138 | }; 139 | More info visit [hlsjs-p2p-engine API.md](https://github.com/cdnbye/hlsjs-p2p-engine/blob/master/docs/English/API.md "hlsjs-p2p-engine API.md") 140 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # php-signaler 2 | ## 介绍 3 | php-signaler 是一个基于 [Workerman](https://github.com/walkor/Workerman "Workerman") 运行的信令服务器 4 | 5 | 适用于 [hlsjs-p2p-engine](https://github.com/cdnbye/hlsjs-p2p-engine "hlsjs-p2p-engine") 的信令服务 6 | 7 | 理论上适用于所有的WebRTC信令服务 8 | 9 | Workerman 是使用纯PHP原生代码编写的高性能,异步处理的Socket框架。 10 | 11 | 使用 Workerman Gateway 可以简单的部署集群式服务,方便横向扩展。 12 | 13 | ## 免费公开的信令服务器 14 | 15 | 信令服务地址:wss://free.freesignal.net 16 | 17 | 为了减少您的工作量,我们维护了一个基于本项目的 免费公开信令服务 18 | 19 | 当前信令服务集群使用了 20 | 21 | 2 台 前端 Gateway 服务器 22 | 23 | 2 台 后端 Worker 服务器 24 | 25 | 1 台 中间件 Register 服务器 26 | 27 | 信令服务地址:wss://free.freesignal.net 28 | 29 | ## 运行需求 30 | 1. PHP 5.3 或更高 31 | 2. 支持 POSIX 兼容的系统 (Linux, OSX, BSD) 32 | 3. PHP 安装了 POSIX 和 PCNTL 扩展 33 | 34 | ## 安装 35 | ### CentOS 36 | 37 | yum install php-cli php-process git gcc php-devel php-pear libevent-devel -y 38 | 39 | 当你安装完成这些依赖后,已经可以正常运行本项目了 40 | 但是如果您需要提供高于1000个连接数的服务,请参阅 优化 篇章 41 | 42 | ### 优化 43 | 44 | 如果您使用的是 PHP 5.3.3, 只能安装 libevent 扩展. 45 | 46 | pecl install channel://pecl.php.net/libevent-0.1.0 47 | 48 | //安装提示: "libevent installation [autodetect]:" 按回车 49 | 50 | echo extension=libevent.so > /etc/php.d/libevent.ini 51 | 52 | 如果您的PHP 版本大于 5.3.3, 安装 event 会更好. 53 | 54 | pecl install event 55 | 56 | //安装提示: Include libevent OpenSSL support [yes] : 输入 "no" 按回车 57 | 58 | //安装提示: PHP Namespace for all Event classes :输入 "yes" 按回车 59 | 60 | //其它的按回车即可. 61 | 62 | echo extension=event.so > /etc/php.d/event.ini 63 | 64 | 下载主程序. (请勿更新Workerman,官方版本的Workerman不支持RFC标准的心跳) 65 | 66 | git clone https://github.com/RayP2P/php-signaler 67 | 68 | ## 配置 69 | 70 | 您只需要关注于 [config.php](https://github.com/RayP2P/php-signaler/blob/master/config.php "config.php") 71 | 72 | //Gateway的内网IP.(公网IP也可以, 但并不推荐) 73 | 'lanIP'=>'127.0.0.1', 74 | //Register 服务器地址 75 | 'registerAddress'=>'127.0.0.1', 76 | //Register 监听端口 77 | 'registerPort'=>1238, 78 | //Gateway 监听端口 [如果使用wss需要改成443] 79 | 'gatewayPort'=>80, 80 | //使用wss还是ws协议,wss协议需要部署ssl 81 | 'wss'=>false, 82 | //ssl的证书 83 | 'wss_cert'=>'/root/server.crt', 84 | //ssl的私钥 85 | 'wss_key'=>'/root/server.key', 86 | //Gateway 进程数量[推荐根据CPU核心数进行调整] 87 | 'gatewayWorkers'=>4, 88 | //Worker 进程数量[推荐根据CPU核心数进行调整] 89 | 'bussinessWorkers'=>4, 90 | 91 | ## 仅在一台服务器部署. 92 | 93 | php php-signaler/start_all.php 94 | 95 | //测试信令服务是否正常. 96 | //如果测试通过,请添加 " -d" 指令来后台运行 97 | 98 | php php-signaler/start_all.php -d 99 | 100 | ## 集群模式部署.(至少2台服务器,推荐至少3台服务器部署) 101 | 102 | // 请保持所有服务器的config.php参数一致,lanIP除外 103 | // lanIP 是一个在部署Gateway服务器时 必须 修改的参数 104 | 105 | //1. 运行register服务.[整个集群仅需要部署1台] 106 | //该服务用于连接Gateway和Worker的协作,正常运行中将处于低负载状态 107 | 108 | php php-signaler/start_register.php 109 | 110 | //2. 运行worker服务.[当负载高于预期时,需要增加服务器] 111 | //该服务用于处理事务 112 | 113 | php php-signaler/start_worker.php 114 | 115 | //3. 运行gateway服务.[当负载高于预期时,需要增加服务器] 116 | //该服务用于维护用户的连接,需要有公网IP来提供服务 或者 内网转外网的负载均衡服务(具体查阅阿里云的负载均衡服务) 117 | 118 | php php-signaler/start_gateway.php 119 | 120 | //测试信令服务是否正常. 121 | //如果测试通过,请添加 " -d" 指令来后台运行 122 | 123 | php php-signaler/start_register.php -d 124 | php php-signaler/start_worker.php -d 125 | php php-signaler/start_gateway.php -d 126 | 127 | ## 配置播放器 128 | 129 | 1.更改信令服务器地址为您的信令服务器地址 130 | ### 范例 131 | var hlsjsConfig = { 132 | p2pConfig: { 133 | wsSignalerAddr: 'wss://example.com/ws', 134 | } 135 | }; 136 | 更多播放器配置项请参阅 [hlsjs-p2p-engine API.md](https://github.com/cdnbye/hlsjs-p2p-engine/blob/master/docs/%E4%B8%AD%E6%96%87/API.md "hlsjs-p2p-engine API.md") 137 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | '127.0.0.1', 5 | //Register Address 6 | 'registerAddress'=>'127.0.0.1', 7 | //Register Listen Port 8 | 'registerPort'=>1238, 9 | //Gateway Listen Port 10 | 'gatewayPort'=>80, 11 | //using wss:// or ws:// 12 | 'wss'=>false, 13 | //wss cert 14 | 'wss_cert'=>'/root/server.crt', 15 | //wss key 16 | 'wss_key'=>'/root/server.key', 17 | //Gateway Workers 18 | 'gatewayWorkers'=>4, 19 | //Bussiness Workers 20 | 'bussinessWorkers'=>4, 21 | ); 22 | ?> -------------------------------------------------------------------------------- /start_all.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer\Autoload; 14 | 15 | /** 16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17 | * 18 | * $loader = new \Composer\Autoload\ClassLoader(); 19 | * 20 | * // register classes with namespaces 21 | * $loader->add('Symfony\Component', __DIR__.'/component'); 22 | * $loader->add('Symfony', __DIR__.'/framework'); 23 | * 24 | * // activate the autoloader 25 | * $loader->register(); 26 | * 27 | * // to enable searching the include path (eg. for PEAR packages) 28 | * $loader->setUseIncludePath(true); 29 | * 30 | * In this example, if you try to use a class in the Symfony\Component 31 | * namespace or one of its children (Symfony\Component\Console for instance), 32 | * the autoloader will first look for the class under the component/ 33 | * directory, and it will then fallback to the framework/ directory if not 34 | * found before giving up. 35 | * 36 | * This class is loosely based on the Symfony UniversalClassLoader. 37 | * 38 | * @author Fabien Potencier 39 | * @author Jordi Boggiano 40 | * @see http://www.php-fig.org/psr/psr-0/ 41 | * @see http://www.php-fig.org/psr/psr-4/ 42 | */ 43 | class ClassLoader 44 | { 45 | // PSR-4 46 | private $prefixLengthsPsr4 = array(); 47 | private $prefixDirsPsr4 = array(); 48 | private $fallbackDirsPsr4 = array(); 49 | 50 | // PSR-0 51 | private $prefixesPsr0 = array(); 52 | private $fallbackDirsPsr0 = array(); 53 | 54 | private $useIncludePath = false; 55 | private $classMap = array(); 56 | private $classMapAuthoritative = false; 57 | private $missingClasses = array(); 58 | private $apcuPrefix; 59 | 60 | public function getPrefixes() 61 | { 62 | if (!empty($this->prefixesPsr0)) { 63 | return call_user_func_array('array_merge', $this->prefixesPsr0); 64 | } 65 | 66 | return array(); 67 | } 68 | 69 | public function getPrefixesPsr4() 70 | { 71 | return $this->prefixDirsPsr4; 72 | } 73 | 74 | public function getFallbackDirs() 75 | { 76 | return $this->fallbackDirsPsr0; 77 | } 78 | 79 | public function getFallbackDirsPsr4() 80 | { 81 | return $this->fallbackDirsPsr4; 82 | } 83 | 84 | public function getClassMap() 85 | { 86 | return $this->classMap; 87 | } 88 | 89 | /** 90 | * @param array $classMap Class to filename map 91 | */ 92 | public function addClassMap(array $classMap) 93 | { 94 | if ($this->classMap) { 95 | $this->classMap = array_merge($this->classMap, $classMap); 96 | } else { 97 | $this->classMap = $classMap; 98 | } 99 | } 100 | 101 | /** 102 | * Registers a set of PSR-0 directories for a given prefix, either 103 | * appending or prepending to the ones previously set for this prefix. 104 | * 105 | * @param string $prefix The prefix 106 | * @param array|string $paths The PSR-0 root directories 107 | * @param bool $prepend Whether to prepend the directories 108 | */ 109 | public function add($prefix, $paths, $prepend = false) 110 | { 111 | if (!$prefix) { 112 | if ($prepend) { 113 | $this->fallbackDirsPsr0 = array_merge( 114 | (array) $paths, 115 | $this->fallbackDirsPsr0 116 | ); 117 | } else { 118 | $this->fallbackDirsPsr0 = array_merge( 119 | $this->fallbackDirsPsr0, 120 | (array) $paths 121 | ); 122 | } 123 | 124 | return; 125 | } 126 | 127 | $first = $prefix[0]; 128 | if (!isset($this->prefixesPsr0[$first][$prefix])) { 129 | $this->prefixesPsr0[$first][$prefix] = (array) $paths; 130 | 131 | return; 132 | } 133 | if ($prepend) { 134 | $this->prefixesPsr0[$first][$prefix] = array_merge( 135 | (array) $paths, 136 | $this->prefixesPsr0[$first][$prefix] 137 | ); 138 | } else { 139 | $this->prefixesPsr0[$first][$prefix] = array_merge( 140 | $this->prefixesPsr0[$first][$prefix], 141 | (array) $paths 142 | ); 143 | } 144 | } 145 | 146 | /** 147 | * Registers a set of PSR-4 directories for a given namespace, either 148 | * appending or prepending to the ones previously set for this namespace. 149 | * 150 | * @param string $prefix The prefix/namespace, with trailing '\\' 151 | * @param array|string $paths The PSR-4 base directories 152 | * @param bool $prepend Whether to prepend the directories 153 | * 154 | * @throws \InvalidArgumentException 155 | */ 156 | public function addPsr4($prefix, $paths, $prepend = false) 157 | { 158 | if (!$prefix) { 159 | // Register directories for the root namespace. 160 | if ($prepend) { 161 | $this->fallbackDirsPsr4 = array_merge( 162 | (array) $paths, 163 | $this->fallbackDirsPsr4 164 | ); 165 | } else { 166 | $this->fallbackDirsPsr4 = array_merge( 167 | $this->fallbackDirsPsr4, 168 | (array) $paths 169 | ); 170 | } 171 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 172 | // Register directories for a new namespace. 173 | $length = strlen($prefix); 174 | if ('\\' !== $prefix[$length - 1]) { 175 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 176 | } 177 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 178 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 179 | } elseif ($prepend) { 180 | // Prepend directories for an already registered namespace. 181 | $this->prefixDirsPsr4[$prefix] = array_merge( 182 | (array) $paths, 183 | $this->prefixDirsPsr4[$prefix] 184 | ); 185 | } else { 186 | // Append directories for an already registered namespace. 187 | $this->prefixDirsPsr4[$prefix] = array_merge( 188 | $this->prefixDirsPsr4[$prefix], 189 | (array) $paths 190 | ); 191 | } 192 | } 193 | 194 | /** 195 | * Registers a set of PSR-0 directories for a given prefix, 196 | * replacing any others previously set for this prefix. 197 | * 198 | * @param string $prefix The prefix 199 | * @param array|string $paths The PSR-0 base directories 200 | */ 201 | public function set($prefix, $paths) 202 | { 203 | if (!$prefix) { 204 | $this->fallbackDirsPsr0 = (array) $paths; 205 | } else { 206 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 207 | } 208 | } 209 | 210 | /** 211 | * Registers a set of PSR-4 directories for a given namespace, 212 | * replacing any others previously set for this namespace. 213 | * 214 | * @param string $prefix The prefix/namespace, with trailing '\\' 215 | * @param array|string $paths The PSR-4 base directories 216 | * 217 | * @throws \InvalidArgumentException 218 | */ 219 | public function setPsr4($prefix, $paths) 220 | { 221 | if (!$prefix) { 222 | $this->fallbackDirsPsr4 = (array) $paths; 223 | } else { 224 | $length = strlen($prefix); 225 | if ('\\' !== $prefix[$length - 1]) { 226 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 227 | } 228 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 229 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 230 | } 231 | } 232 | 233 | /** 234 | * Turns on searching the include path for class files. 235 | * 236 | * @param bool $useIncludePath 237 | */ 238 | public function setUseIncludePath($useIncludePath) 239 | { 240 | $this->useIncludePath = $useIncludePath; 241 | } 242 | 243 | /** 244 | * Can be used to check if the autoloader uses the include path to check 245 | * for classes. 246 | * 247 | * @return bool 248 | */ 249 | public function getUseIncludePath() 250 | { 251 | return $this->useIncludePath; 252 | } 253 | 254 | /** 255 | * Turns off searching the prefix and fallback directories for classes 256 | * that have not been registered with the class map. 257 | * 258 | * @param bool $classMapAuthoritative 259 | */ 260 | public function setClassMapAuthoritative($classMapAuthoritative) 261 | { 262 | $this->classMapAuthoritative = $classMapAuthoritative; 263 | } 264 | 265 | /** 266 | * Should class lookup fail if not found in the current class map? 267 | * 268 | * @return bool 269 | */ 270 | public function isClassMapAuthoritative() 271 | { 272 | return $this->classMapAuthoritative; 273 | } 274 | 275 | /** 276 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 277 | * 278 | * @param string|null $apcuPrefix 279 | */ 280 | public function setApcuPrefix($apcuPrefix) 281 | { 282 | $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; 283 | } 284 | 285 | /** 286 | * The APCu prefix in use, or null if APCu caching is not enabled. 287 | * 288 | * @return string|null 289 | */ 290 | public function getApcuPrefix() 291 | { 292 | return $this->apcuPrefix; 293 | } 294 | 295 | /** 296 | * Registers this instance as an autoloader. 297 | * 298 | * @param bool $prepend Whether to prepend the autoloader or not 299 | */ 300 | public function register($prepend = false) 301 | { 302 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 303 | } 304 | 305 | /** 306 | * Unregisters this instance as an autoloader. 307 | */ 308 | public function unregister() 309 | { 310 | spl_autoload_unregister(array($this, 'loadClass')); 311 | } 312 | 313 | /** 314 | * Loads the given class or interface. 315 | * 316 | * @param string $class The name of the class 317 | * @return bool|null True if loaded, null otherwise 318 | */ 319 | public function loadClass($class) 320 | { 321 | if ($file = $this->findFile($class)) { 322 | includeFile($file); 323 | 324 | return true; 325 | } 326 | } 327 | 328 | /** 329 | * Finds the path to the file where the class is defined. 330 | * 331 | * @param string $class The name of the class 332 | * 333 | * @return string|false The path if found, false otherwise 334 | */ 335 | public function findFile($class) 336 | { 337 | // class map lookup 338 | if (isset($this->classMap[$class])) { 339 | return $this->classMap[$class]; 340 | } 341 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 342 | return false; 343 | } 344 | if (null !== $this->apcuPrefix) { 345 | $file = apcu_fetch($this->apcuPrefix.$class, $hit); 346 | if ($hit) { 347 | return $file; 348 | } 349 | } 350 | 351 | $file = $this->findFileWithExtension($class, '.php'); 352 | 353 | // Search for Hack files if we are running on HHVM 354 | if (false === $file && defined('HHVM_VERSION')) { 355 | $file = $this->findFileWithExtension($class, '.hh'); 356 | } 357 | 358 | if (null !== $this->apcuPrefix) { 359 | apcu_add($this->apcuPrefix.$class, $file); 360 | } 361 | 362 | if (false === $file) { 363 | // Remember that this class does not exist. 364 | $this->missingClasses[$class] = true; 365 | } 366 | 367 | return $file; 368 | } 369 | 370 | private function findFileWithExtension($class, $ext) 371 | { 372 | // PSR-4 lookup 373 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 374 | 375 | $first = $class[0]; 376 | if (isset($this->prefixLengthsPsr4[$first])) { 377 | $subPath = $class; 378 | while (false !== $lastPos = strrpos($subPath, '\\')) { 379 | $subPath = substr($subPath, 0, $lastPos); 380 | $search = $subPath.'\\'; 381 | if (isset($this->prefixDirsPsr4[$search])) { 382 | foreach ($this->prefixDirsPsr4[$search] as $dir) { 383 | $length = $this->prefixLengthsPsr4[$first][$search]; 384 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { 385 | return $file; 386 | } 387 | } 388 | } 389 | } 390 | } 391 | 392 | // PSR-4 fallback dirs 393 | foreach ($this->fallbackDirsPsr4 as $dir) { 394 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 395 | return $file; 396 | } 397 | } 398 | 399 | // PSR-0 lookup 400 | if (false !== $pos = strrpos($class, '\\')) { 401 | // namespaced class name 402 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 403 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 404 | } else { 405 | // PEAR-like class name 406 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 407 | } 408 | 409 | if (isset($this->prefixesPsr0[$first])) { 410 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 411 | if (0 === strpos($class, $prefix)) { 412 | foreach ($dirs as $dir) { 413 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 414 | return $file; 415 | } 416 | } 417 | } 418 | } 419 | } 420 | 421 | // PSR-0 fallback dirs 422 | foreach ($this->fallbackDirsPsr0 as $dir) { 423 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 424 | return $file; 425 | } 426 | } 427 | 428 | // PSR-0 include paths. 429 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 430 | return $file; 431 | } 432 | 433 | return false; 434 | } 435 | } 436 | 437 | /** 438 | * Scope isolated include. 439 | * 440 | * Prevents access to $this/self from included files. 441 | */ 442 | function includeFile($file) 443 | { 444 | include $file; 445 | } 446 | -------------------------------------------------------------------------------- /vendor/composer/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) Nils Adermann, Jordi Boggiano 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is furnished 9 | to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /vendor/composer/autoload_classmap.php: -------------------------------------------------------------------------------- 1 | array($vendorDir . '/workerman/workerman'), 10 | 'GatewayWorker\\' => array($vendorDir . '/workerman/gateway-worker/src'), 11 | ); 12 | -------------------------------------------------------------------------------- /vendor/composer/autoload_real.php: -------------------------------------------------------------------------------- 1 | = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 27 | if ($useStaticLoader) { 28 | require_once __DIR__ . '/autoload_static.php'; 29 | 30 | call_user_func(\Composer\Autoload\ComposerStaticInit4ca8bc2c515c94fb062bc5c50d5fad60::getInitializer($loader)); 31 | } else { 32 | $map = require __DIR__ . '/autoload_namespaces.php'; 33 | foreach ($map as $namespace => $path) { 34 | $loader->set($namespace, $path); 35 | } 36 | 37 | $map = require __DIR__ . '/autoload_psr4.php'; 38 | foreach ($map as $namespace => $path) { 39 | $loader->setPsr4($namespace, $path); 40 | } 41 | 42 | $classMap = require __DIR__ . '/autoload_classmap.php'; 43 | if ($classMap) { 44 | $loader->addClassMap($classMap); 45 | } 46 | } 47 | 48 | $loader->register(true); 49 | 50 | return $loader; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /vendor/composer/autoload_static.php: -------------------------------------------------------------------------------- 1 | 11 | array ( 12 | 'Workerman\\' => 10, 13 | ), 14 | 'G' => 15 | array ( 16 | 'GatewayWorker\\' => 14, 17 | ), 18 | ); 19 | 20 | public static $prefixDirsPsr4 = array ( 21 | 'Workerman\\' => 22 | array ( 23 | 0 => __DIR__ . '/..' . '/workerman/workerman', 24 | ), 25 | 'GatewayWorker\\' => 26 | array ( 27 | 0 => __DIR__ . '/..' . '/workerman/gateway-worker/src', 28 | ), 29 | ); 30 | 31 | public static function getInitializer(ClassLoader $loader) 32 | { 33 | return \Closure::bind(function () use ($loader) { 34 | $loader->prefixLengthsPsr4 = ComposerStaticInit4ca8bc2c515c94fb062bc5c50d5fad60::$prefixLengthsPsr4; 35 | $loader->prefixDirsPsr4 = ComposerStaticInit4ca8bc2c515c94fb062bc5c50d5fad60::$prefixDirsPsr4; 36 | 37 | }, null, ClassLoader::class); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "workerman/workerman", 4 | "version": "v3.5.12", 5 | "version_normalized": "3.5.12.0", 6 | "source": { 7 | "type": "git", 8 | "url": "https://github.com/walkor/Workerman.git", 9 | "reference": "2f89ef966809cb54de776bfb1ac8582d1deae185" 10 | }, 11 | "dist": { 12 | "type": "zip", 13 | "url": "https://api.github.com/repos/walkor/Workerman/zipball/2f89ef966809cb54de776bfb1ac8582d1deae185", 14 | "reference": "2f89ef966809cb54de776bfb1ac8582d1deae185", 15 | "shasum": "" 16 | }, 17 | "require": { 18 | "php": ">=5.3" 19 | }, 20 | "suggest": { 21 | "ext-event": "For better performance. " 22 | }, 23 | "time": "2018-07-05T08:31:48+00:00", 24 | "type": "library", 25 | "installation-source": "dist", 26 | "autoload": { 27 | "psr-4": { 28 | "Workerman\\": "./" 29 | } 30 | }, 31 | "notification-url": "https://packagist.org/downloads/", 32 | "license": [ 33 | "MIT" 34 | ], 35 | "authors": [ 36 | { 37 | "name": "walkor", 38 | "email": "walkor@workerman.net", 39 | "homepage": "http://www.workerman.net", 40 | "role": "Developer" 41 | } 42 | ], 43 | "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.", 44 | "homepage": "http://www.workerman.net", 45 | "keywords": [ 46 | "asynchronous", 47 | "event-loop" 48 | ] 49 | }, 50 | { 51 | "name": "workerman/gateway-worker", 52 | "version": "v3.0.9", 53 | "version_normalized": "3.0.9.0", 54 | "source": { 55 | "type": "git", 56 | "url": "https://github.com/walkor/GatewayWorker.git", 57 | "reference": "0d91d66dd5cdcc05a3c6ab17dc7c15087728baed" 58 | }, 59 | "dist": { 60 | "type": "zip", 61 | "url": "https://api.github.com/repos/walkor/GatewayWorker/zipball/0d91d66dd5cdcc05a3c6ab17dc7c15087728baed", 62 | "reference": "0d91d66dd5cdcc05a3c6ab17dc7c15087728baed", 63 | "shasum": "" 64 | }, 65 | "require": { 66 | "workerman/workerman": ">=3.1.8" 67 | }, 68 | "time": "2018-05-22T16:57:48+00:00", 69 | "type": "library", 70 | "installation-source": "dist", 71 | "autoload": { 72 | "psr-4": { 73 | "GatewayWorker\\": "./src" 74 | } 75 | }, 76 | "notification-url": "https://packagist.org/downloads/", 77 | "license": [ 78 | "MIT" 79 | ], 80 | "homepage": "http://www.workerman.net", 81 | "keywords": [ 82 | "communication", 83 | "distributed" 84 | ] 85 | } 86 | ] 87 | -------------------------------------------------------------------------------- /vendor/workerman/_root_RayP2P_start.php.pid: -------------------------------------------------------------------------------- 1 | 20339 -------------------------------------------------------------------------------- /vendor/workerman/_root_php-signal_start.php.pid: -------------------------------------------------------------------------------- 1 | 2875 -------------------------------------------------------------------------------- /vendor/workerman/gateway-worker/.gitignore: -------------------------------------------------------------------------------- 1 | .buildpath 2 | .project 3 | .settings 4 | .idea -------------------------------------------------------------------------------- /vendor/workerman/gateway-worker/MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2009-2015 walkor and contributors (see https://github.com/walkor/workerman/contributors) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/workerman/gateway-worker/README.md: -------------------------------------------------------------------------------- 1 | GatewayWorker 2 | ================= 3 | 4 | GatewayWorker基于[Workerman](https://github.com/walkor/Workerman)开发的一个项目框架,用于快速开发长连接应用,例如app推送服务端、即时IM服务端、游戏服务端、物联网、智能家居等等。 5 | 6 | GatewayWorker使用经典的Gateway和Worker进程模型。Gateway进程负责维持客户端连接,并转发客户端的数据给Worker进程处理;Worker进程负责处理实际的业务逻辑,并将结果推送给对应的客户端。Gateway服务和Worker服务可以分开部署在不同的服务器上,实现分布式集群。 7 | 8 | GatewayWorker提供非常方便的API,可以全局广播数据、可以向某个群体广播数据、也可以向某个特定客户端推送数据。配合Workerman的定时器,也可以定时推送数据。 9 | 10 | 快速开始 11 | ====== 12 | 开发者可以从一个简单的demo开始(demo中包含了GatewayWorker内核,以及start_gateway.php start_business.php等启动入口文件)
13 | [点击这里下载demo](http://www.workerman.net/download/GatewayWorker.zip)。
14 | demo说明见源码readme。 15 | 16 | 手册 17 | ======= 18 | http://www.workerman.net/gatewaydoc/ 19 | 20 | 安装内核 21 | ======= 22 | 23 | 只安装GatewayWorker内核文件(不包含start_gateway.php start_businessworker.php等启动入口文件) 24 | ``` 25 | composer require workerman/gateway-worker 26 | ``` 27 | 28 | 使用GatewayWorker开发的项目 29 | ======= 30 | ## [tadpole](http://kedou.workerman.net/) 31 | [Live demo](http://kedou.workerman.net/) 32 | [Source code](https://github.com/walkor/workerman) 33 | ![workerman todpole](http://www.workerman.net/img/workerman-todpole.png) 34 | 35 | ## [chat room](http://chat.workerman.net/) 36 | [Live demo](http://chat.workerman.net/) 37 | [Source code](https://github.com/walkor/workerman-chat) 38 | ![workerman-chat](http://www.workerman.net/img/workerman-chat.png) 39 | -------------------------------------------------------------------------------- /vendor/workerman/gateway-worker/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "workerman/gateway-worker", 3 | "keywords": ["distributed","communication"], 4 | "homepage": "http://www.workerman.net", 5 | "license" : "MIT", 6 | "require": { 7 | "workerman/workerman" : ">=3.1.8" 8 | }, 9 | "autoload": { 10 | "psr-4": {"GatewayWorker\\": "./src"} 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /vendor/workerman/gateway-worker/src/BusinessWorker.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace GatewayWorker; 15 | 16 | use Workerman\Connection\TcpConnection; 17 | 18 | use Workerman\Worker; 19 | use Workerman\Lib\Timer; 20 | use Workerman\Connection\AsyncTcpConnection; 21 | use GatewayWorker\Protocols\GatewayProtocol; 22 | use GatewayWorker\Lib\Context; 23 | 24 | /** 25 | * 26 | * BusinessWorker 用于处理Gateway转发来的数据 27 | * 28 | * @author walkor 29 | * 30 | */ 31 | class BusinessWorker extends Worker 32 | { 33 | /** 34 | * 保存与 gateway 的连接 connection 对象 35 | * 36 | * @var array 37 | */ 38 | public $gatewayConnections = array(); 39 | 40 | /** 41 | * 注册中心地址 42 | * 43 | * @var string 44 | */ 45 | public $registerAddress = '127.0.0.1:1236'; 46 | 47 | /** 48 | * 事件处理类,默认是 Event 类 49 | * 50 | * @var string 51 | */ 52 | public $eventHandler = 'Events'; 53 | 54 | /** 55 | * 业务超时时间,可用来定位程序卡在哪里 56 | * 57 | * @var int 58 | */ 59 | public $processTimeout = 30; 60 | 61 | /** 62 | * 业务超时时间,可用来定位程序卡在哪里 63 | * 64 | * @var callable 65 | */ 66 | public $processTimeoutHandler = '\\Workerman\\Worker::log'; 67 | 68 | /** 69 | * 秘钥 70 | * 71 | * @var string 72 | */ 73 | public $secretKey = ''; 74 | 75 | /** 76 | * businessWorker进程将消息转发给gateway进程的发送缓冲区大小 77 | * 78 | * @var int 79 | */ 80 | public $sendToGatewayBufferSize = 10240000; 81 | 82 | /** 83 | * 保存用户设置的 worker 启动回调 84 | * 85 | * @var callback 86 | */ 87 | protected $_onWorkerStart = null; 88 | 89 | /** 90 | * 保存用户设置的 workerReload 回调 91 | * 92 | * @var callback 93 | */ 94 | protected $_onWorkerReload = null; 95 | 96 | /** 97 | * 保存用户设置的 workerStop 回调 98 | * 99 | * @var callback 100 | */ 101 | protected $_onWorkerStop= null; 102 | 103 | /** 104 | * 到注册中心的连接 105 | * 106 | * @var AsyncTcpConnection 107 | */ 108 | protected $_registerConnection = null; 109 | 110 | /** 111 | * 处于连接状态的 gateway 通讯地址 112 | * 113 | * @var array 114 | */ 115 | protected $_connectingGatewayAddresses = array(); 116 | 117 | /** 118 | * 所有 geteway 内部通讯地址 119 | * 120 | * @var array 121 | */ 122 | protected $_gatewayAddresses = array(); 123 | 124 | /** 125 | * 等待连接个 gateway 地址 126 | * 127 | * @var array 128 | */ 129 | protected $_waitingConnectGatewayAddresses = array(); 130 | 131 | /** 132 | * Event::onConnect 回调 133 | * 134 | * @var callback 135 | */ 136 | protected $_eventOnConnect = null; 137 | 138 | /** 139 | * Event::onMessage 回调 140 | * 141 | * @var callback 142 | */ 143 | protected $_eventOnMessage = null; 144 | 145 | /** 146 | * Event::onClose 回调 147 | * 148 | * @var callback 149 | */ 150 | protected $_eventOnClose = null; 151 | 152 | /** 153 | * websocket回调 154 | * 155 | * @var null 156 | */ 157 | protected $_eventOnWebSocketConnect = null; 158 | 159 | /** 160 | * SESSION 版本缓存 161 | * 162 | * @var array 163 | */ 164 | protected $_sessionVersion = array(); 165 | 166 | /** 167 | * 用于保持长连接的心跳时间间隔 168 | * 169 | * @var int 170 | */ 171 | const PERSISTENCE_CONNECTION_PING_INTERVAL = 25; 172 | 173 | /** 174 | * 构造函数 175 | * 176 | * @param string $socket_name 177 | * @param array $context_option 178 | */ 179 | public function __construct($socket_name = '', $context_option = array()) 180 | { 181 | parent::__construct($socket_name, $context_option); 182 | $backrace = debug_backtrace(); 183 | $this->_autoloadRootPath = dirname($backrace[0]['file']); 184 | } 185 | 186 | /** 187 | * {@inheritdoc} 188 | */ 189 | public function run() 190 | { 191 | $this->_onWorkerStart = $this->onWorkerStart; 192 | $this->_onWorkerReload = $this->onWorkerReload; 193 | $this->_onWorkerStop = $this->onWorkerStop; 194 | $this->onWorkerStop = array($this, 'onWorkerStop'); 195 | $this->onWorkerStart = array($this, 'onWorkerStart'); 196 | $this->onWorkerReload = array($this, 'onWorkerReload'); 197 | parent::run(); 198 | } 199 | 200 | /** 201 | * 当进程启动时一些初始化工作 202 | * 203 | * @return void 204 | */ 205 | protected function onWorkerStart() 206 | { 207 | if (!class_exists('\Protocols\GatewayProtocol')) { 208 | class_alias('GatewayWorker\Protocols\GatewayProtocol', 'Protocols\GatewayProtocol'); 209 | } 210 | $this->connectToRegister(); 211 | \GatewayWorker\Lib\Gateway::setBusinessWorker($this); 212 | \GatewayWorker\Lib\Gateway::$secretKey = $this->secretKey; 213 | if ($this->_onWorkerStart) { 214 | call_user_func($this->_onWorkerStart, $this); 215 | } 216 | 217 | if (is_callable($this->eventHandler . '::onWorkerStart')) { 218 | call_user_func($this->eventHandler . '::onWorkerStart', $this); 219 | } 220 | 221 | if (function_exists('pcntl_signal')) { 222 | // 业务超时信号处理 223 | pcntl_signal(SIGALRM, array($this, 'timeoutHandler'), false); 224 | } else { 225 | $this->processTimeout = 0; 226 | } 227 | 228 | // 设置回调 229 | if (is_callable($this->eventHandler . '::onConnect')) { 230 | $this->_eventOnConnect = $this->eventHandler . '::onConnect'; 231 | } 232 | 233 | if (is_callable($this->eventHandler . '::onMessage')) { 234 | $this->_eventOnMessage = $this->eventHandler . '::onMessage'; 235 | } else { 236 | echo "Waring: {$this->eventHandler}::onMessage is not callable\n"; 237 | } 238 | 239 | if (is_callable($this->eventHandler . '::onClose')) { 240 | $this->_eventOnClose = $this->eventHandler . '::onClose'; 241 | } 242 | 243 | if (is_callable($this->eventHandler . '::onWebSocketConnect')) { 244 | $this->_eventOnWebSocketConnect = $this->eventHandler . '::onWebSocketConnect'; 245 | } 246 | 247 | // 如果Register服务器不在本地服务器,则需要保持心跳 248 | if (strpos($this->registerAddress, '127.0.0.1') !== 0) { 249 | Timer::add(self::PERSISTENCE_CONNECTION_PING_INTERVAL, array($this, 'pingRegister')); 250 | } 251 | } 252 | 253 | /** 254 | * onWorkerReload 回调 255 | * 256 | * @param Worker $worker 257 | */ 258 | protected function onWorkerReload($worker) 259 | { 260 | // 防止进程立刻退出 261 | $worker->reloadable = false; 262 | // 延迟 0.05 秒退出,避免 BusinessWorker 瞬间全部退出导致没有可用的 BusinessWorker 进程 263 | Timer::add(0.05, array('Workerman\Worker', 'stopAll')); 264 | // 执行用户定义的 onWorkerReload 回调 265 | if ($this->_onWorkerReload) { 266 | call_user_func($this->_onWorkerReload, $this); 267 | } 268 | } 269 | 270 | /** 271 | * 当进程关闭时一些清理工作 272 | * 273 | * @return void 274 | */ 275 | protected function onWorkerStop() 276 | { 277 | if ($this->_onWorkerStop) { 278 | call_user_func($this->_onWorkerStop, $this); 279 | } 280 | if (is_callable($this->eventHandler . '::onWorkerStop')) { 281 | call_user_func($this->eventHandler . '::onWorkerStop', $this); 282 | } 283 | } 284 | 285 | /** 286 | * 连接服务注册中心 287 | * 288 | * @return void 289 | */ 290 | public function connectToRegister() 291 | { 292 | $this->_registerConnection = new AsyncTcpConnection("text://{$this->registerAddress}"); 293 | $this->_registerConnection->send('{"event":"worker_connect","secret_key":"' . $this->secretKey . '"}'); 294 | $this->_registerConnection->onClose = array($this, 'onRegisterConnectionClose'); 295 | $this->_registerConnection->onMessage = array($this, 'onRegisterConnectionMessage'); 296 | $this->_registerConnection->connect(); 297 | } 298 | 299 | /** 300 | * 与注册中心连接关闭时,定时重连 301 | * 302 | * @return void 303 | */ 304 | public function onRegisterConnectionClose() 305 | { 306 | Timer::add(1, array($this, 'connectToRegister'), null, false); 307 | } 308 | 309 | /** 310 | * 当注册中心发来消息时 311 | * 312 | * @return void 313 | */ 314 | public function onRegisterConnectionMessage($register_connection, $data) 315 | { 316 | $data = json_decode($data, true); 317 | if (!isset($data['event'])) { 318 | echo "Received bad data from Register\n"; 319 | return; 320 | } 321 | $event = $data['event']; 322 | switch ($event) { 323 | case 'broadcast_addresses': 324 | if (!is_array($data['addresses'])) { 325 | echo "Received bad data from Register. Addresses empty\n"; 326 | return; 327 | } 328 | $addresses = $data['addresses']; 329 | $this->_gatewayAddresses = array(); 330 | foreach ($addresses as $addr) { 331 | $this->_gatewayAddresses[$addr] = $addr; 332 | } 333 | $this->checkGatewayConnections($addresses); 334 | break; 335 | default: 336 | echo "Receive bad event:$event from Register.\n"; 337 | } 338 | } 339 | 340 | /** 341 | * 当 gateway 转发来数据时 342 | * 343 | * @param TcpConnection $connection 344 | * @param mixed $data 345 | */ 346 | public function onGatewayMessage($connection, $data) 347 | { 348 | $cmd = $data['cmd']; 349 | if ($cmd === GatewayProtocol::CMD_PING) { 350 | return; 351 | } 352 | // 上下文数据 353 | Context::$client_ip = $data['client_ip']; 354 | Context::$client_port = $data['client_port']; 355 | Context::$local_ip = $data['local_ip']; 356 | Context::$local_port = $data['local_port']; 357 | Context::$connection_id = $data['connection_id']; 358 | Context::$client_id = Context::addressToClientId($data['local_ip'], $data['local_port'], 359 | $data['connection_id']); 360 | // $_SERVER 变量 361 | $_SERVER = array( 362 | 'REMOTE_ADDR' => long2ip($data['client_ip']), 363 | 'REMOTE_PORT' => $data['client_port'], 364 | 'GATEWAY_ADDR' => long2ip($data['local_ip']), 365 | 'GATEWAY_PORT' => $data['gateway_port'], 366 | 'GATEWAY_CLIENT_ID' => Context::$client_id, 367 | ); 368 | // 检查session版本,如果是过期的session数据则拉取最新的数据 369 | if ($cmd !== GatewayProtocol::CMD_ON_CLOSE && isset($this->_sessionVersion[Context::$client_id]) && $this->_sessionVersion[Context::$client_id] !== crc32($data['ext_data'])) { 370 | $_SESSION = Context::$old_session = \GatewayWorker\Lib\Gateway::getSession(Context::$client_id); 371 | } else { 372 | if (!isset($this->_sessionVersion[Context::$client_id])) { 373 | $this->_sessionVersion[Context::$client_id] = crc32($data['ext_data']); 374 | } 375 | // 尝试解析 session 376 | if ($data['ext_data'] != '') { 377 | Context::$old_session = $_SESSION = Context::sessionDecode($data['ext_data']); 378 | } else { 379 | Context::$old_session = $_SESSION = null; 380 | } 381 | } 382 | 383 | if ($this->processTimeout) { 384 | pcntl_alarm($this->processTimeout); 385 | } 386 | // 尝试执行 Event::onConnection、Event::onMessage、Event::onClose 387 | switch ($cmd) { 388 | case GatewayProtocol::CMD_ON_CONNECT: 389 | if ($this->_eventOnConnect) { 390 | call_user_func($this->_eventOnConnect, Context::$client_id); 391 | } 392 | break; 393 | case GatewayProtocol::CMD_ON_MESSAGE: 394 | if ($this->_eventOnMessage) { 395 | call_user_func($this->_eventOnMessage, Context::$client_id, $data['body']); 396 | } 397 | break; 398 | case GatewayProtocol::CMD_ON_CLOSE: 399 | unset($this->_sessionVersion[Context::$client_id]); 400 | if ($this->_eventOnClose) { 401 | call_user_func($this->_eventOnClose, Context::$client_id); 402 | } 403 | break; 404 | case GatewayProtocol::CMD_ON_WEBSOCKET_CONNECT: 405 | if ($this->_eventOnWebSocketConnect) { 406 | call_user_func($this->_eventOnWebSocketConnect, Context::$client_id, $data['body']); 407 | } 408 | break; 409 | } 410 | if ($this->processTimeout) { 411 | pcntl_alarm(0); 412 | } 413 | 414 | // session 必须是数组 415 | if ($_SESSION !== null && !is_array($_SESSION)) { 416 | throw new \Exception('$_SESSION must be an array. But $_SESSION=' . var_export($_SESSION, true) . ' is not array.'); 417 | } 418 | 419 | // 判断 session 是否被更改 420 | if ($_SESSION !== Context::$old_session && $cmd !== GatewayProtocol::CMD_ON_CLOSE) { 421 | $session_str_now = $_SESSION !== null ? Context::sessionEncode($_SESSION) : ''; 422 | \GatewayWorker\Lib\Gateway::setSocketSession(Context::$client_id, $session_str_now); 423 | $this->_sessionVersion[Context::$client_id] = crc32($session_str_now); 424 | } 425 | 426 | Context::clear(); 427 | } 428 | 429 | /** 430 | * 当与 Gateway 的连接断开时触发 431 | * 432 | * @param TcpConnection $connection 433 | * @return void 434 | */ 435 | public function onGatewayClose($connection) 436 | { 437 | $addr = $connection->remoteAddress; 438 | unset($this->gatewayConnections[$addr], $this->_connectingGatewayAddresses[$addr]); 439 | if (isset($this->_gatewayAddresses[$addr]) && !isset($this->_waitingConnectGatewayAddresses[$addr])) { 440 | Timer::add(1, array($this, 'tryToConnectGateway'), array($addr), false); 441 | $this->_waitingConnectGatewayAddresses[$addr] = $addr; 442 | } 443 | } 444 | 445 | /** 446 | * 尝试连接 Gateway 内部通讯地址 447 | * 448 | * @param string $addr 449 | */ 450 | public function tryToConnectGateway($addr) 451 | { 452 | if (!isset($this->gatewayConnections[$addr]) && !isset($this->_connectingGatewayAddresses[$addr]) && isset($this->_gatewayAddresses[$addr])) { 453 | $gateway_connection = new AsyncTcpConnection("GatewayProtocol://$addr"); 454 | $gateway_connection->remoteAddress = $addr; 455 | $gateway_connection->onConnect = array($this, 'onConnectGateway'); 456 | $gateway_connection->onMessage = array($this, 'onGatewayMessage'); 457 | $gateway_connection->onClose = array($this, 'onGatewayClose'); 458 | $gateway_connection->onError = array($this, 'onGatewayError'); 459 | $gateway_connection->maxSendBufferSize = $this->sendToGatewayBufferSize; 460 | if (TcpConnection::$defaultMaxSendBufferSize == $gateway_connection->maxSendBufferSize) { 461 | $gateway_connection->maxSendBufferSize = 50 * 1024 * 1024; 462 | } 463 | $gateway_data = GatewayProtocol::$empty; 464 | $gateway_data['cmd'] = GatewayProtocol::CMD_WORKER_CONNECT; 465 | $gateway_data['body'] = json_encode(array( 466 | 'worker_key' =>"{$this->name}:{$this->id}", 467 | 'secret_key' => $this->secretKey, 468 | )); 469 | $gateway_connection->send($gateway_data); 470 | $gateway_connection->connect(); 471 | $this->_connectingGatewayAddresses[$addr] = $addr; 472 | } 473 | unset($this->_waitingConnectGatewayAddresses[$addr]); 474 | } 475 | 476 | /** 477 | * 检查 gateway 的通信端口是否都已经连 478 | * 如果有未连接的端口,则尝试连接 479 | * 480 | * @param array $addresses_list 481 | */ 482 | public function checkGatewayConnections($addresses_list) 483 | { 484 | if (empty($addresses_list)) { 485 | return; 486 | } 487 | foreach ($addresses_list as $addr) { 488 | if (!isset($this->_waitingConnectGatewayAddresses[$addr])) { 489 | $this->tryToConnectGateway($addr); 490 | } 491 | } 492 | } 493 | 494 | /** 495 | * 当连接上 gateway 的通讯端口时触发 496 | * 将连接 connection 对象保存起来 497 | * 498 | * @param TcpConnection $connection 499 | * @return void 500 | */ 501 | public function onConnectGateway($connection) 502 | { 503 | $this->gatewayConnections[$connection->remoteAddress] = $connection; 504 | unset($this->_connectingGatewayAddresses[$connection->remoteAddress], $this->_waitingConnectGatewayAddresses[$connection->remoteAddress]); 505 | } 506 | 507 | /** 508 | * 当与 gateway 的连接出现错误时触发 509 | * 510 | * @param TcpConnection $connection 511 | * @param int $error_no 512 | * @param string $error_msg 513 | */ 514 | public function onGatewayError($connection, $error_no, $error_msg) 515 | { 516 | echo "GatewayConnection Error : $error_no ,$error_msg\n"; 517 | } 518 | 519 | /** 520 | * 获取所有 Gateway 内部通讯地址 521 | * 522 | * @return array 523 | */ 524 | public function getAllGatewayAddresses() 525 | { 526 | return $this->_gatewayAddresses; 527 | } 528 | 529 | /** 530 | * 业务超时回调 531 | * 532 | * @param int $signal 533 | * @throws \Exception 534 | */ 535 | public function timeoutHandler($signal) 536 | { 537 | switch ($signal) { 538 | // 超时时钟 539 | case SIGALRM: 540 | // 超时异常 541 | $e = new \Exception("process_timeout", 506); 542 | $trace_str = $e->getTraceAsString(); 543 | // 去掉第一行timeoutHandler的调用栈 544 | $trace_str = $e->getMessage() . ":\n" . substr($trace_str, strpos($trace_str, "\n") + 1) . "\n"; 545 | // 开发者没有设置超时处理函数,或者超时处理函数返回空则执行退出 546 | if (!$this->processTimeoutHandler || !call_user_func($this->processTimeoutHandler, $trace_str, $e)) { 547 | Worker::stopAll(); 548 | } 549 | break; 550 | } 551 | } 552 | 553 | /** 554 | * 向 Register 发送心跳,用来保持长连接 555 | */ 556 | public function pingRegister() 557 | { 558 | if ($this->_registerConnection) { 559 | $this->_registerConnection->send('{"event":"ping"}'); 560 | } 561 | } 562 | } 563 | -------------------------------------------------------------------------------- /vendor/workerman/gateway-worker/src/Lib/Context.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace GatewayWorker\Lib; 15 | 16 | use Exception; 17 | 18 | /** 19 | * 上下文 包含当前用户 uid, 内部通信 local_ip local_port socket_id,以及客户端 client_ip client_port 20 | */ 21 | class Context 22 | { 23 | /** 24 | * 内部通讯 id 25 | * 26 | * @var string 27 | */ 28 | public static $local_ip; 29 | 30 | /** 31 | * 内部通讯端口 32 | * 33 | * @var int 34 | */ 35 | public static $local_port; 36 | 37 | /** 38 | * 客户端 ip 39 | * 40 | * @var string 41 | */ 42 | public static $client_ip; 43 | 44 | /** 45 | * 客户端端口 46 | * 47 | * @var int 48 | */ 49 | public static $client_port; 50 | 51 | /** 52 | * client_id 53 | * 54 | * @var string 55 | */ 56 | public static $client_id; 57 | 58 | /** 59 | * 连接 connection->id 60 | * 61 | * @var int 62 | */ 63 | public static $connection_id; 64 | 65 | /** 66 | * 旧的session 67 | * 68 | * @var string 69 | */ 70 | public static $old_session; 71 | 72 | /** 73 | * 编码 session 74 | * 75 | * @param mixed $session_data 76 | * @return string 77 | */ 78 | public static function sessionEncode($session_data = '') 79 | { 80 | if ($session_data !== '') { 81 | return serialize($session_data); 82 | } 83 | return ''; 84 | } 85 | 86 | /** 87 | * 解码 session 88 | * 89 | * @param string $session_buffer 90 | * @return mixed 91 | */ 92 | public static function sessionDecode($session_buffer) 93 | { 94 | return unserialize($session_buffer); 95 | } 96 | 97 | /** 98 | * 清除上下文 99 | * 100 | * @return void 101 | */ 102 | public static function clear() 103 | { 104 | self::$local_ip = self::$local_port = self::$client_ip = self::$client_port = 105 | self::$client_id = self::$connection_id = self::$old_session = null; 106 | } 107 | 108 | /** 109 | * 通讯地址到 client_id 的转换 110 | * 111 | * @param int $local_ip 112 | * @param int $local_port 113 | * @param int $connection_id 114 | * @return string 115 | */ 116 | public static function addressToClientId($local_ip, $local_port, $connection_id) 117 | { 118 | return bin2hex(pack('NnN', $local_ip, $local_port, $connection_id)); 119 | } 120 | 121 | /** 122 | * client_id 到通讯地址的转换 123 | * 124 | * @param string $client_id 125 | * @return array 126 | * @throws Exception 127 | */ 128 | public static function clientIdToAddress($client_id) 129 | { 130 | if (strlen($client_id) !== 20) { 131 | echo new Exception("client_id $client_id is invalid"); 132 | return false; 133 | } 134 | return unpack('Nlocal_ip/nlocal_port/Nconnection_id', pack('H*', $client_id)); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /vendor/workerman/gateway-worker/src/Lib/Db.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace GatewayWorker\Lib; 15 | 16 | use Config\Db as DbConfig; 17 | use Exception; 18 | 19 | /** 20 | * 数据库类 21 | */ 22 | class Db 23 | { 24 | /** 25 | * 实例数组 26 | * 27 | * @var array 28 | */ 29 | protected static $instance = array(); 30 | 31 | /** 32 | * 获取实例 33 | * 34 | * @param string $config_name 35 | * @return DbConnection 36 | * @throws Exception 37 | */ 38 | public static function instance($config_name) 39 | { 40 | if (!isset(DbConfig::$$config_name)) { 41 | echo "\\Config\\Db::$config_name not set\n"; 42 | throw new Exception("\\Config\\Db::$config_name not set\n"); 43 | } 44 | 45 | if (empty(self::$instance[$config_name])) { 46 | $config = DbConfig::$$config_name; 47 | self::$instance[$config_name] = new DbConnection($config['host'], $config['port'], 48 | $config['user'], $config['password'], $config['dbname']); 49 | } 50 | return self::$instance[$config_name]; 51 | } 52 | 53 | /** 54 | * 关闭数据库实例 55 | * 56 | * @param string $config_name 57 | */ 58 | public static function close($config_name) 59 | { 60 | if (isset(self::$instance[$config_name])) { 61 | self::$instance[$config_name]->closeConnection(); 62 | self::$instance[$config_name] = null; 63 | } 64 | } 65 | 66 | /** 67 | * 关闭所有数据库实例 68 | */ 69 | public static function closeAll() 70 | { 71 | foreach (self::$instance as $connection) { 72 | $connection->closeConnection(); 73 | } 74 | self::$instance = array(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /vendor/workerman/gateway-worker/src/Protocols/GatewayProtocol.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace GatewayWorker\Protocols; 15 | 16 | /** 17 | * Gateway 与 Worker 间通讯的二进制协议 18 | * 19 | * struct GatewayProtocol 20 | * { 21 | * unsigned int pack_len, 22 | * unsigned char cmd,//命令字 23 | * unsigned int local_ip, 24 | * unsigned short local_port, 25 | * unsigned int client_ip, 26 | * unsigned short client_port, 27 | * unsigned int connection_id, 28 | * unsigned char flag, 29 | * unsigned short gateway_port, 30 | * unsigned int ext_len, 31 | * char[ext_len] ext_data, 32 | * char[pack_length-HEAD_LEN] body//包体 33 | * } 34 | * NCNnNnNCnN 35 | */ 36 | class GatewayProtocol 37 | { 38 | // 发给worker,gateway有一个新的连接 39 | const CMD_ON_CONNECT = 1; 40 | 41 | // 发给worker的,客户端有消息 42 | const CMD_ON_MESSAGE = 3; 43 | 44 | // 发给worker上的关闭链接事件 45 | const CMD_ON_CLOSE = 4; 46 | 47 | // 发给gateway的向单个用户发送数据 48 | const CMD_SEND_TO_ONE = 5; 49 | 50 | // 发给gateway的向所有用户发送数据 51 | const CMD_SEND_TO_ALL = 6; 52 | 53 | // 发给gateway的踢出用户 54 | // 1、如果有待发消息,将在发送完后立即销毁用户连接 55 | // 2、如果无待发消息,将立即销毁用户连接 56 | const CMD_KICK = 7; 57 | 58 | // 发给gateway的立即销毁用户连接 59 | const CMD_DESTROY = 8; 60 | 61 | // 发给gateway,通知用户session更新 62 | const CMD_UPDATE_SESSION = 9; 63 | 64 | // 获取在线状态 65 | const CMD_GET_ALL_CLIENT_SESSIONS = 10; 66 | 67 | // 判断是否在线 68 | const CMD_IS_ONLINE = 11; 69 | 70 | // client_id绑定到uid 71 | const CMD_BIND_UID = 12; 72 | 73 | // 解绑 74 | const CMD_UNBIND_UID = 13; 75 | 76 | // 向uid发送数据 77 | const CMD_SEND_TO_UID = 14; 78 | 79 | // 根据uid获取绑定的clientid 80 | const CMD_GET_CLIENT_ID_BY_UID = 15; 81 | 82 | // 加入组 83 | const CMD_JOIN_GROUP = 20; 84 | 85 | // 离开组 86 | const CMD_LEAVE_GROUP = 21; 87 | 88 | // 向组成员发消息 89 | const CMD_SEND_TO_GROUP = 22; 90 | 91 | // 获取组成员 92 | const CMD_GET_CLINET_SESSUONS_BY_GROUP = 23; 93 | 94 | // 获取组在线连接数 95 | const CMD_GET_CLIENT_COUNT_BY_GROUP = 24; 96 | 97 | // 按照条件查找 98 | const CMD_SELECT = 25; 99 | 100 | // 获取在线的群组ID 101 | const CMD_GET_GROUP_ID_LIST = 26; 102 | 103 | // 取消分组 104 | const CMD_UNGROUP = 27; 105 | 106 | // worker连接gateway事件 107 | const CMD_WORKER_CONNECT = 200; 108 | 109 | // 心跳 110 | const CMD_PING = 201; 111 | 112 | // GatewayClient连接gateway事件 113 | const CMD_GATEWAY_CLIENT_CONNECT = 202; 114 | 115 | // 根据client_id获取session 116 | const CMD_GET_SESSION_BY_CLIENT_ID = 203; 117 | 118 | // 发给gateway,覆盖session 119 | const CMD_SET_SESSION = 204; 120 | 121 | // 当websocket握手时触发,只有websocket协议支持此命令字 122 | const CMD_ON_WEBSOCKET_CONNECT = 205; 123 | 124 | // 包体是标量 125 | const FLAG_BODY_IS_SCALAR = 0x01; 126 | 127 | // 通知gateway在send时不调用协议encode方法,在广播组播时提升性能 128 | const FLAG_NOT_CALL_ENCODE = 0x02; 129 | 130 | /** 131 | * 包头长度 132 | * 133 | * @var int 134 | */ 135 | const HEAD_LEN = 28; 136 | 137 | public static $empty = array( 138 | 'cmd' => 0, 139 | 'local_ip' => 0, 140 | 'local_port' => 0, 141 | 'client_ip' => 0, 142 | 'client_port' => 0, 143 | 'connection_id' => 0, 144 | 'flag' => 0, 145 | 'gateway_port' => 0, 146 | 'ext_data' => '', 147 | 'body' => '', 148 | ); 149 | 150 | /** 151 | * 返回包长度 152 | * 153 | * @param string $buffer 154 | * @return int return current package length 155 | */ 156 | public static function input($buffer) 157 | { 158 | if (strlen($buffer) < self::HEAD_LEN) { 159 | return 0; 160 | } 161 | 162 | $data = unpack("Npack_len", $buffer); 163 | return $data['pack_len']; 164 | } 165 | 166 | /** 167 | * 获取整个包的 buffer 168 | * 169 | * @param mixed $data 170 | * @return string 171 | */ 172 | public static function encode($data) 173 | { 174 | $flag = (int)is_scalar($data['body']); 175 | if (!$flag) { 176 | $data['body'] = serialize($data['body']); 177 | } 178 | $data['flag'] |= $flag; 179 | $ext_len = strlen($data['ext_data']); 180 | $package_len = self::HEAD_LEN + $ext_len + strlen($data['body']); 181 | return pack("NCNnNnNCnN", $package_len, 182 | $data['cmd'], $data['local_ip'], 183 | $data['local_port'], $data['client_ip'], 184 | $data['client_port'], $data['connection_id'], 185 | $data['flag'], $data['gateway_port'], 186 | $ext_len) . $data['ext_data'] . $data['body']; 187 | } 188 | 189 | /** 190 | * 从二进制数据转换为数组 191 | * 192 | * @param string $buffer 193 | * @return array 194 | */ 195 | public static function decode($buffer) 196 | { 197 | $data = unpack("Npack_len/Ccmd/Nlocal_ip/nlocal_port/Nclient_ip/nclient_port/Nconnection_id/Cflag/ngateway_port/Next_len", 198 | $buffer); 199 | if ($data['ext_len'] > 0) { 200 | $data['ext_data'] = substr($buffer, self::HEAD_LEN, $data['ext_len']); 201 | if ($data['flag'] & self::FLAG_BODY_IS_SCALAR) { 202 | $data['body'] = substr($buffer, self::HEAD_LEN + $data['ext_len']); 203 | } else { 204 | $data['body'] = unserialize(substr($buffer, self::HEAD_LEN + $data['ext_len'])); 205 | } 206 | } else { 207 | $data['ext_data'] = ''; 208 | if ($data['flag'] & self::FLAG_BODY_IS_SCALAR) { 209 | $data['body'] = substr($buffer, self::HEAD_LEN); 210 | } else { 211 | $data['body'] = unserialize(substr($buffer, self::HEAD_LEN)); 212 | } 213 | } 214 | return $data; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /vendor/workerman/gateway-worker/src/Register.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace GatewayWorker; 15 | 16 | use Workerman\Worker; 17 | use Workerman\Lib\Timer; 18 | 19 | /** 20 | * 21 | * 注册中心,用于注册 Gateway 和 BusinessWorker 22 | * 23 | * @author walkor 24 | * 25 | */ 26 | class Register extends Worker 27 | { 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | public $name = 'Register'; 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public $reloadable = false; 37 | 38 | /** 39 | * 秘钥 40 | * @var string 41 | */ 42 | public $secretKey = ''; 43 | 44 | /** 45 | * 所有 gateway 的连接 46 | * 47 | * @var array 48 | */ 49 | protected $_gatewayConnections = array(); 50 | 51 | /** 52 | * 所有 worker 的连接 53 | * 54 | * @var array 55 | */ 56 | protected $_workerConnections = array(); 57 | 58 | /** 59 | * 进程启动时间 60 | * 61 | * @var int 62 | */ 63 | protected $_startTime = 0; 64 | 65 | /** 66 | * {@inheritdoc} 67 | */ 68 | public function run() 69 | { 70 | // 设置 onMessage 连接回调 71 | $this->onConnect = array($this, 'onConnect'); 72 | 73 | // 设置 onMessage 回调 74 | $this->onMessage = array($this, 'onMessage'); 75 | 76 | // 设置 onClose 回调 77 | $this->onClose = array($this, 'onClose'); 78 | 79 | // 记录进程启动的时间 80 | $this->_startTime = time(); 81 | 82 | // 强制使用text协议 83 | $this->protocol = '\Workerman\Protocols\Text'; 84 | 85 | // 运行父方法 86 | parent::run(); 87 | } 88 | 89 | /** 90 | * 设置个定时器,将未及时发送验证的连接关闭 91 | * 92 | * @param \Workerman\Connection\ConnectionInterface $connection 93 | * @return void 94 | */ 95 | public function onConnect($connection) 96 | { 97 | $connection->timeout_timerid = Timer::add(10, function () use ($connection) { 98 | Worker::log("Register auth timeout (".$connection->getRemoteIp()."). See http://wiki.workerman.net/Error4 for detail"); 99 | $connection->close(); 100 | }, null, false); 101 | } 102 | 103 | /** 104 | * 设置消息回调 105 | * 106 | * @param \Workerman\Connection\ConnectionInterface $connection 107 | * @param string $buffer 108 | * @return void 109 | */ 110 | public function onMessage($connection, $buffer) 111 | { 112 | // 删除定时器 113 | Timer::del($connection->timeout_timerid); 114 | $data = @json_decode($buffer, true); 115 | if (empty($data['event'])) { 116 | $error = "Bad request for Register service. Request info(IP:".$connection->getRemoteIp().", Request Buffer:$buffer). See http://wiki.workerman.net/Error4 for detail"; 117 | Worker::log($error); 118 | return $connection->close($error); 119 | } 120 | $event = $data['event']; 121 | $secret_key = isset($data['secret_key']) ? $data['secret_key'] : ''; 122 | // 开始验证 123 | switch ($event) { 124 | // 是 gateway 连接 125 | case 'gateway_connect': 126 | if (empty($data['address'])) { 127 | echo "address not found\n"; 128 | return $connection->close(); 129 | } 130 | if ($secret_key !== $this->secretKey) { 131 | Worker::log("Register: Key does not match ".var_export($secret_key, true)." !== ".var_export($this->secretKey, true)); 132 | return $connection->close(); 133 | } 134 | $this->_gatewayConnections[$connection->id] = $data['address']; 135 | $this->broadcastAddresses(); 136 | break; 137 | // 是 worker 连接 138 | case 'worker_connect': 139 | if ($secret_key !== $this->secretKey) { 140 | Worker::log("Register: Key does not match ".var_export($secret_key, true)." !== ".var_export($this->secretKey, true)); 141 | return $connection->close(); 142 | } 143 | $this->_workerConnections[$connection->id] = $connection; 144 | $this->broadcastAddresses($connection); 145 | break; 146 | case 'ping': 147 | break; 148 | default: 149 | Worker::log("Register unknown event:$event IP: ".$connection->getRemoteIp()." Buffer:$buffer. See http://wiki.workerman.net/Error4 for detail"); 150 | $connection->close(); 151 | } 152 | } 153 | 154 | /** 155 | * 连接关闭时 156 | * 157 | * @param \Workerman\Connection\ConnectionInterface $connection 158 | */ 159 | public function onClose($connection) 160 | { 161 | if (isset($this->_gatewayConnections[$connection->id])) { 162 | unset($this->_gatewayConnections[$connection->id]); 163 | $this->broadcastAddresses(); 164 | } 165 | if (isset($this->_workerConnections[$connection->id])) { 166 | unset($this->_workerConnections[$connection->id]); 167 | } 168 | } 169 | 170 | /** 171 | * 向 BusinessWorker 广播 gateway 内部通讯地址 172 | * 173 | * @param \Workerman\Connection\ConnectionInterface $connection 174 | */ 175 | public function broadcastAddresses($connection = null) 176 | { 177 | $data = array( 178 | 'event' => 'broadcast_addresses', 179 | 'addresses' => array_unique(array_values($this->_gatewayConnections)), 180 | ); 181 | $buffer = json_encode($data); 182 | if ($connection) { 183 | $connection->send($buffer); 184 | return; 185 | } 186 | foreach ($this->_workerConnections as $con) { 187 | $con->send($buffer); 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | .buildpath 3 | .project 4 | .settings 5 | .idea 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Autoloader.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman; 15 | 16 | /** 17 | * Autoload. 18 | */ 19 | class Autoloader 20 | { 21 | /** 22 | * Autoload root path. 23 | * 24 | * @var string 25 | */ 26 | protected static $_autoloadRootPath = ''; 27 | 28 | /** 29 | * Set autoload root path. 30 | * 31 | * @param string $root_path 32 | * @return void 33 | */ 34 | public static function setRootPath($root_path) 35 | { 36 | self::$_autoloadRootPath = $root_path; 37 | } 38 | 39 | /** 40 | * Load files by namespace. 41 | * 42 | * @param string $name 43 | * @return boolean 44 | */ 45 | public static function loadByNamespace($name) 46 | { 47 | $class_path = str_replace('\\', DIRECTORY_SEPARATOR, $name); 48 | if (strpos($name, 'Workerman\\') === 0) { 49 | $class_file = __DIR__ . substr($class_path, strlen('Workerman')) . '.php'; 50 | } else { 51 | if (self::$_autoloadRootPath) { 52 | $class_file = self::$_autoloadRootPath . DIRECTORY_SEPARATOR . $class_path . '.php'; 53 | } 54 | if (empty($class_file) || !is_file($class_file)) { 55 | $class_file = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . "$class_path.php"; 56 | } 57 | } 58 | 59 | if (is_file($class_file)) { 60 | require_once($class_file); 61 | if (class_exists($name, false)) { 62 | return true; 63 | } 64 | } 65 | return false; 66 | } 67 | } 68 | 69 | spl_autoload_register('\Workerman\Autoloader::loadByNamespace'); -------------------------------------------------------------------------------- /vendor/workerman/workerman/Connection/AsyncTcpConnection.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Connection; 15 | 16 | use Workerman\Events\EventInterface; 17 | use Workerman\Lib\Timer; 18 | use Workerman\Worker; 19 | use Exception; 20 | 21 | /** 22 | * AsyncTcpConnection. 23 | */ 24 | class AsyncTcpConnection extends TcpConnection 25 | { 26 | /** 27 | * Emitted when socket connection is successfully established. 28 | * 29 | * @var callback 30 | */ 31 | public $onConnect = null; 32 | 33 | /** 34 | * Transport layer protocol. 35 | * 36 | * @var string 37 | */ 38 | public $transport = 'tcp'; 39 | 40 | /** 41 | * Status. 42 | * 43 | * @var int 44 | */ 45 | protected $_status = self::STATUS_INITIAL; 46 | 47 | /** 48 | * Remote host. 49 | * 50 | * @var string 51 | */ 52 | protected $_remoteHost = ''; 53 | 54 | /** 55 | * Remote port. 56 | * 57 | * @var int 58 | */ 59 | protected $_remotePort = 80; 60 | 61 | /** 62 | * Connect start time. 63 | * 64 | * @var string 65 | */ 66 | protected $_connectStartTime = 0; 67 | 68 | /** 69 | * Remote URI. 70 | * 71 | * @var string 72 | */ 73 | protected $_remoteURI = ''; 74 | 75 | /** 76 | * Context option. 77 | * 78 | * @var array 79 | */ 80 | protected $_contextOption = null; 81 | 82 | /** 83 | * Reconnect timer. 84 | * 85 | * @var int 86 | */ 87 | protected $_reconnectTimer = null; 88 | 89 | 90 | /** 91 | * PHP built-in protocols. 92 | * 93 | * @var array 94 | */ 95 | protected static $_builtinTransports = array( 96 | 'tcp' => 'tcp', 97 | 'udp' => 'udp', 98 | 'unix' => 'unix', 99 | 'ssl' => 'ssl', 100 | 'sslv2' => 'sslv2', 101 | 'sslv3' => 'sslv3', 102 | 'tls' => 'tls' 103 | ); 104 | 105 | /** 106 | * Construct. 107 | * 108 | * @param string $remote_address 109 | * @param array $context_option 110 | * @throws Exception 111 | */ 112 | public function __construct($remote_address, $context_option = null) 113 | { 114 | $address_info = parse_url($remote_address); 115 | if (!$address_info) { 116 | list($scheme, $this->_remoteAddress) = explode(':', $remote_address, 2); 117 | if (!$this->_remoteAddress) { 118 | Worker::safeEcho(new \Exception('bad remote_address')); 119 | } 120 | } else { 121 | if (!isset($address_info['port'])) { 122 | $address_info['port'] = 80; 123 | } 124 | if (!isset($address_info['path'])) { 125 | $address_info['path'] = '/'; 126 | } 127 | if (!isset($address_info['query'])) { 128 | $address_info['query'] = ''; 129 | } else { 130 | $address_info['query'] = '?' . $address_info['query']; 131 | } 132 | $this->_remoteAddress = "{$address_info['host']}:{$address_info['port']}"; 133 | $this->_remoteHost = $address_info['host']; 134 | $this->_remotePort = $address_info['port']; 135 | $this->_remoteURI = "{$address_info['path']}{$address_info['query']}"; 136 | $scheme = isset($address_info['scheme']) ? $address_info['scheme'] : 'tcp'; 137 | } 138 | 139 | $this->id = $this->_id = self::$_idRecorder++; 140 | if(PHP_INT_MAX === self::$_idRecorder){ 141 | self::$_idRecorder = 0; 142 | } 143 | // Check application layer protocol class. 144 | if (!isset(self::$_builtinTransports[$scheme])) { 145 | $scheme = ucfirst($scheme); 146 | $this->protocol = '\\Protocols\\' . $scheme; 147 | if (!class_exists($this->protocol)) { 148 | $this->protocol = "\\Workerman\\Protocols\\$scheme"; 149 | if (!class_exists($this->protocol)) { 150 | throw new Exception("class \\Protocols\\$scheme not exist"); 151 | } 152 | } 153 | } else { 154 | $this->transport = self::$_builtinTransports[$scheme]; 155 | } 156 | 157 | // For statistics. 158 | self::$statistics['connection_count']++; 159 | $this->maxSendBufferSize = self::$defaultMaxSendBufferSize; 160 | $this->_contextOption = $context_option; 161 | static::$connections[$this->_id] = $this; 162 | } 163 | 164 | /** 165 | * Do connect. 166 | * 167 | * @return void 168 | */ 169 | public function connect() 170 | { 171 | if ($this->_status !== self::STATUS_INITIAL && $this->_status !== self::STATUS_CLOSING && 172 | $this->_status !== self::STATUS_CLOSED) { 173 | return; 174 | } 175 | $this->_status = self::STATUS_CONNECTING; 176 | $this->_connectStartTime = microtime(true); 177 | if ($this->transport !== 'unix') { 178 | // Open socket connection asynchronously. 179 | if ($this->_contextOption) { 180 | $context = stream_context_create($this->_contextOption); 181 | $this->_socket = stream_socket_client("tcp://{$this->_remoteHost}:{$this->_remotePort}", 182 | $errno, $errstr, 0, STREAM_CLIENT_ASYNC_CONNECT, $context); 183 | } else { 184 | $this->_socket = stream_socket_client("tcp://{$this->_remoteHost}:{$this->_remotePort}", 185 | $errno, $errstr, 0, STREAM_CLIENT_ASYNC_CONNECT); 186 | } 187 | } else { 188 | $this->_socket = stream_socket_client("{$this->transport}://{$this->_remoteAddress}", $errno, $errstr, 0, 189 | STREAM_CLIENT_ASYNC_CONNECT); 190 | } 191 | // If failed attempt to emit onError callback. 192 | if (!$this->_socket) { 193 | $this->emitError(WORKERMAN_CONNECT_FAIL, $errstr); 194 | if ($this->_status === self::STATUS_CLOSING) { 195 | $this->destroy(); 196 | } 197 | if ($this->_status === self::STATUS_CLOSED) { 198 | $this->onConnect = null; 199 | } 200 | return; 201 | } 202 | // Add socket to global event loop waiting connection is successfully established or faild. 203 | Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'checkConnection')); 204 | // For windows. 205 | if(DIRECTORY_SEPARATOR === '\\') { 206 | Worker::$globalEvent->add($this->_socket, EventInterface::EV_EXCEPT, array($this, 'checkConnection')); 207 | } 208 | } 209 | 210 | /** 211 | * Reconnect. 212 | * 213 | * @param int $after 214 | * @return void 215 | */ 216 | public function reconnect($after = 0) 217 | { 218 | $this->_status = self::STATUS_INITIAL; 219 | static::$connections[$this->_id] = $this; 220 | if ($this->_reconnectTimer) { 221 | Timer::del($this->_reconnectTimer); 222 | } 223 | if ($after > 0) { 224 | $this->_reconnectTimer = Timer::add($after, array($this, 'connect'), null, false); 225 | return; 226 | } 227 | $this->connect(); 228 | } 229 | 230 | /** 231 | * CancelReconnect. 232 | */ 233 | public function cancelReconnect() 234 | { 235 | if ($this->_reconnectTimer) { 236 | Timer::del($this->_reconnectTimer); 237 | } 238 | } 239 | 240 | /** 241 | * Get remote address. 242 | * 243 | * @return string 244 | */ 245 | public function getRemoteHost() 246 | { 247 | return $this->_remoteHost; 248 | } 249 | 250 | /** 251 | * Get remote URI. 252 | * 253 | * @return string 254 | */ 255 | public function getRemoteURI() 256 | { 257 | return $this->_remoteURI; 258 | } 259 | 260 | /** 261 | * Try to emit onError callback. 262 | * 263 | * @param int $code 264 | * @param string $msg 265 | * @return void 266 | */ 267 | protected function emitError($code, $msg) 268 | { 269 | $this->_status = self::STATUS_CLOSING; 270 | if ($this->onError) { 271 | try { 272 | call_user_func($this->onError, $this, $code, $msg); 273 | } catch (\Exception $e) { 274 | Worker::log($e); 275 | exit(250); 276 | } catch (\Error $e) { 277 | Worker::log($e); 278 | exit(250); 279 | } 280 | } 281 | } 282 | 283 | /** 284 | * Check connection is successfully established or faild. 285 | * 286 | * @param resource $socket 287 | * @return void 288 | */ 289 | public function checkConnection() 290 | { 291 | if ($this->_status != self::STATUS_CONNECTING) { 292 | return; 293 | } 294 | 295 | // Remove EV_EXPECT for windows. 296 | if(DIRECTORY_SEPARATOR === '\\') { 297 | Worker::$globalEvent->del($this->_socket, EventInterface::EV_EXCEPT); 298 | } 299 | 300 | // Check socket state. 301 | if ($address = stream_socket_get_name($this->_socket, true)) { 302 | // Nonblocking. 303 | stream_set_blocking($this->_socket, 0); 304 | // Compatible with hhvm 305 | if (function_exists('stream_set_read_buffer')) { 306 | stream_set_read_buffer($this->_socket, 0); 307 | } 308 | // Try to open keepalive for tcp and disable Nagle algorithm. 309 | if (function_exists('socket_import_stream') && $this->transport === 'tcp') { 310 | $raw_socket = socket_import_stream($this->_socket); 311 | socket_set_option($raw_socket, SOL_SOCKET, SO_KEEPALIVE, 1); 312 | socket_set_option($raw_socket, SOL_TCP, TCP_NODELAY, 1); 313 | } 314 | 315 | // Remove write listener. 316 | Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE); 317 | 318 | // SSL handshake. 319 | if ($this->transport === 'ssl') { 320 | $this->_sslHandshakeCompleted = $this->doSslHandshake($this->_socket); 321 | } else { 322 | // There are some data waiting to send. 323 | if ($this->_sendBuffer) { 324 | Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite')); 325 | } 326 | } 327 | 328 | // Register a listener waiting read event. 329 | Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead')); 330 | 331 | $this->_status = self::STATUS_ESTABLISHED; 332 | $this->_remoteAddress = $address; 333 | 334 | // Try to emit onConnect callback. 335 | if ($this->onConnect) { 336 | try { 337 | call_user_func($this->onConnect, $this); 338 | } catch (\Exception $e) { 339 | Worker::log($e); 340 | exit(250); 341 | } catch (\Error $e) { 342 | Worker::log($e); 343 | exit(250); 344 | } 345 | } 346 | // Try to emit protocol::onConnect 347 | if (method_exists($this->protocol, 'onConnect')) { 348 | try { 349 | call_user_func(array($this->protocol, 'onConnect'), $this); 350 | } catch (\Exception $e) { 351 | Worker::log($e); 352 | exit(250); 353 | } catch (\Error $e) { 354 | Worker::log($e); 355 | exit(250); 356 | } 357 | } 358 | } else { 359 | // Connection failed. 360 | $this->emitError(WORKERMAN_CONNECT_FAIL, 'connect ' . $this->_remoteAddress . ' fail after ' . round(microtime(true) - $this->_connectStartTime, 4) . ' seconds'); 361 | if ($this->_status === self::STATUS_CLOSING) { 362 | $this->destroy(); 363 | } 364 | if ($this->_status === self::STATUS_CLOSED) { 365 | $this->onConnect = null; 366 | } 367 | } 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Connection/AsyncUdpConnection.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Connection; 15 | 16 | use Workerman\Events\EventInterface; 17 | use Workerman\Worker; 18 | use Exception; 19 | 20 | /** 21 | * AsyncTcpConnection. 22 | */ 23 | class AsyncUdpConnection extends UdpConnection 24 | { 25 | /** 26 | * Emitted when socket connection is successfully established. 27 | * 28 | * @var callback 29 | */ 30 | public $onConnect = null; 31 | 32 | /** 33 | * Emitted when socket connection closed. 34 | * 35 | * @var callback 36 | */ 37 | public $onClose = null; 38 | 39 | /** 40 | * Connected or not. 41 | * 42 | * @var bool 43 | */ 44 | protected $connected = false; 45 | 46 | /** 47 | * Context option. 48 | * 49 | * @var array 50 | */ 51 | protected $_contextOption = null; 52 | 53 | /** 54 | * Construct. 55 | * 56 | * @param string $remote_address 57 | * @throws Exception 58 | */ 59 | public function __construct($remote_address, $context_option = null) 60 | { 61 | // Get the application layer communication protocol and listening address. 62 | list($scheme, $address) = explode(':', $remote_address, 2); 63 | // Check application layer protocol class. 64 | if ($scheme !== 'udp') { 65 | $scheme = ucfirst($scheme); 66 | $this->protocol = '\\Protocols\\' . $scheme; 67 | if (!class_exists($this->protocol)) { 68 | $this->protocol = "\\Workerman\\Protocols\\$scheme"; 69 | if (!class_exists($this->protocol)) { 70 | throw new Exception("class \\Protocols\\$scheme not exist"); 71 | } 72 | } 73 | } 74 | 75 | $this->_remoteAddress = substr($address, 2); 76 | $this->_contextOption = $context_option; 77 | } 78 | 79 | /** 80 | * For udp package. 81 | * 82 | * @param resource $socket 83 | * @return bool 84 | */ 85 | public function baseRead($socket) 86 | { 87 | $recv_buffer = stream_socket_recvfrom($socket, Worker::MAX_UDP_PACKAGE_SIZE, 0, $remote_address); 88 | if (false === $recv_buffer || empty($remote_address)) { 89 | return false; 90 | } 91 | 92 | if ($this->onMessage) { 93 | if ($this->protocol) { 94 | $parser = $this->protocol; 95 | $recv_buffer = $parser::decode($recv_buffer, $this); 96 | } 97 | ConnectionInterface::$statistics['total_request']++; 98 | try { 99 | call_user_func($this->onMessage, $this, $recv_buffer); 100 | } catch (\Exception $e) { 101 | Worker::log($e); 102 | exit(250); 103 | } catch (\Error $e) { 104 | Worker::log($e); 105 | exit(250); 106 | } 107 | } 108 | return true; 109 | } 110 | 111 | /** 112 | * Sends data on the connection. 113 | * 114 | * @param string $send_buffer 115 | * @param bool $raw 116 | * @return void|boolean 117 | */ 118 | public function send($send_buffer, $raw = false) 119 | { 120 | if (false === $raw && $this->protocol) { 121 | $parser = $this->protocol; 122 | $send_buffer = $parser::encode($send_buffer, $this); 123 | if ($send_buffer === '') { 124 | return null; 125 | } 126 | } 127 | if ($this->connected === false) { 128 | $this->connect(); 129 | } 130 | return strlen($send_buffer) === stream_socket_sendto($this->_socket, $send_buffer, 0); 131 | } 132 | 133 | 134 | /** 135 | * Close connection. 136 | * 137 | * @param mixed $data 138 | * @param bool $raw 139 | * 140 | * @return bool 141 | */ 142 | public function close($data = null, $raw = false) 143 | { 144 | if ($data !== null) { 145 | $this->send($data, $raw); 146 | } 147 | Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ); 148 | fclose($this->_socket); 149 | $this->connected = false; 150 | // Try to emit onClose callback. 151 | if ($this->onClose) { 152 | try { 153 | call_user_func($this->onClose, $this); 154 | } catch (\Exception $e) { 155 | Worker::log($e); 156 | exit(250); 157 | } catch (\Error $e) { 158 | Worker::log($e); 159 | exit(250); 160 | } 161 | } 162 | $this->onConnect = $this->onMessage = $this->onClose = null; 163 | return true; 164 | } 165 | 166 | /** 167 | * Connect. 168 | * 169 | * @return void 170 | */ 171 | public function connect() 172 | { 173 | if ($this->connected === true) { 174 | return; 175 | } 176 | if ($this->_contextOption) { 177 | $context = stream_context_create($this->_contextOption); 178 | $this->_socket = stream_socket_client("udp://{$this->_remoteAddress}", $errno, $errmsg, 179 | 30, STREAM_CLIENT_CONNECT, $context); 180 | } else { 181 | $this->_socket = stream_socket_client("udp://{$this->_remoteAddress}", $errno, $errmsg); 182 | } 183 | 184 | if (!$this->_socket) { 185 | Worker::safeEcho(new \Exception($errmsg)); 186 | return; 187 | } 188 | if ($this->onMessage) { 189 | Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead')); 190 | } 191 | $this->connected = true; 192 | // Try to emit onConnect callback. 193 | if ($this->onConnect) { 194 | try { 195 | call_user_func($this->onConnect, $this); 196 | } catch (\Exception $e) { 197 | Worker::log($e); 198 | exit(250); 199 | } catch (\Error $e) { 200 | Worker::log($e); 201 | exit(250); 202 | } 203 | } 204 | } 205 | 206 | } 207 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Connection/ConnectionInterface.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Connection; 15 | 16 | /** 17 | * ConnectionInterface. 18 | */ 19 | abstract class ConnectionInterface 20 | { 21 | /** 22 | * Statistics for status command. 23 | * 24 | * @var array 25 | */ 26 | public static $statistics = array( 27 | 'connection_count' => 0, 28 | 'total_request' => 0, 29 | 'throw_exception' => 0, 30 | 'send_fail' => 0, 31 | ); 32 | 33 | /** 34 | * Emitted when data is received. 35 | * 36 | * @var callback 37 | */ 38 | public $onMessage = null; 39 | 40 | /** 41 | * Emitted when the other end of the socket sends a FIN packet. 42 | * 43 | * @var callback 44 | */ 45 | public $onClose = null; 46 | 47 | /** 48 | * Emitted when an error occurs with connection. 49 | * 50 | * @var callback 51 | */ 52 | public $onError = null; 53 | 54 | /** 55 | * Sends data on the connection. 56 | * 57 | * @param string $send_buffer 58 | * @return void|boolean 59 | */ 60 | abstract public function send($send_buffer); 61 | 62 | /** 63 | * Get remote IP. 64 | * 65 | * @return string 66 | */ 67 | abstract public function getRemoteIp(); 68 | 69 | /** 70 | * Get remote port. 71 | * 72 | * @return int 73 | */ 74 | abstract public function getRemotePort(); 75 | 76 | /** 77 | * Get remote address. 78 | * 79 | * @return string 80 | */ 81 | abstract public function getRemoteAddress(); 82 | 83 | /** 84 | * Get local IP. 85 | * 86 | * @return string 87 | */ 88 | abstract public function getLocalIp(); 89 | 90 | /** 91 | * Get local port. 92 | * 93 | * @return int 94 | */ 95 | abstract public function getLocalPort(); 96 | 97 | /** 98 | * Get local address. 99 | * 100 | * @return string 101 | */ 102 | abstract public function getLocalAddress(); 103 | 104 | /** 105 | * Is ipv4. 106 | * 107 | * @return bool 108 | */ 109 | abstract public function isIPv4(); 110 | 111 | /** 112 | * Is ipv6. 113 | * 114 | * @return bool 115 | */ 116 | abstract public function isIPv6(); 117 | 118 | /** 119 | * Close connection. 120 | * 121 | * @param $data 122 | * @return void 123 | */ 124 | abstract public function close($data = null); 125 | } 126 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Connection/UdpConnection.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Connection; 15 | 16 | /** 17 | * UdpConnection. 18 | */ 19 | class UdpConnection extends ConnectionInterface 20 | { 21 | /** 22 | * Application layer protocol. 23 | * The format is like this Workerman\\Protocols\\Http. 24 | * 25 | * @var \Workerman\Protocols\ProtocolInterface 26 | */ 27 | public $protocol = null; 28 | 29 | /** 30 | * Udp socket. 31 | * 32 | * @var resource 33 | */ 34 | protected $_socket = null; 35 | 36 | /** 37 | * Remote address. 38 | * 39 | * @var string 40 | */ 41 | protected $_remoteAddress = ''; 42 | 43 | /** 44 | * Construct. 45 | * 46 | * @param resource $socket 47 | * @param string $remote_address 48 | */ 49 | public function __construct($socket, $remote_address) 50 | { 51 | $this->_socket = $socket; 52 | $this->_remoteAddress = $remote_address; 53 | } 54 | 55 | /** 56 | * Sends data on the connection. 57 | * 58 | * @param string $send_buffer 59 | * @param bool $raw 60 | * @return void|boolean 61 | */ 62 | public function send($send_buffer, $raw = false) 63 | { 64 | if (false === $raw && $this->protocol) { 65 | $parser = $this->protocol; 66 | $send_buffer = $parser::encode($send_buffer, $this); 67 | if ($send_buffer === '') { 68 | return null; 69 | } 70 | } 71 | return strlen($send_buffer) === stream_socket_sendto($this->_socket, $send_buffer, 0, $this->_remoteAddress); 72 | } 73 | 74 | /** 75 | * Get remote IP. 76 | * 77 | * @return string 78 | */ 79 | public function getRemoteIp() 80 | { 81 | $pos = strrpos($this->_remoteAddress, ':'); 82 | if ($pos) { 83 | return trim(substr($this->_remoteAddress, 0, $pos), '[]'); 84 | } 85 | return ''; 86 | } 87 | 88 | /** 89 | * Get remote port. 90 | * 91 | * @return int 92 | */ 93 | public function getRemotePort() 94 | { 95 | if ($this->_remoteAddress) { 96 | return (int)substr(strrchr($this->_remoteAddress, ':'), 1); 97 | } 98 | return 0; 99 | } 100 | 101 | /** 102 | * Get remote address. 103 | * 104 | * @return string 105 | */ 106 | public function getRemoteAddress() 107 | { 108 | return $this->_remoteAddress; 109 | } 110 | 111 | /** 112 | * Get local IP. 113 | * 114 | * @return string 115 | */ 116 | public function getLocalIp() 117 | { 118 | $address = $this->getLocalAddress(); 119 | $pos = strrpos($address, ':'); 120 | if (!$pos) { 121 | return ''; 122 | } 123 | return substr($address, 0, $pos); 124 | } 125 | 126 | /** 127 | * Get local port. 128 | * 129 | * @return int 130 | */ 131 | public function getLocalPort() 132 | { 133 | $address = $this->getLocalAddress(); 134 | $pos = strrpos($address, ':'); 135 | if (!$pos) { 136 | return 0; 137 | } 138 | return (int)substr(strrchr($address, ':'), 1); 139 | } 140 | 141 | /** 142 | * Get local address. 143 | * 144 | * @return string 145 | */ 146 | public function getLocalAddress() 147 | { 148 | return (string)@stream_socket_get_name($this->_socket, false); 149 | } 150 | 151 | /** 152 | * Is ipv4. 153 | * 154 | * return bool. 155 | */ 156 | public function isIpV4() 157 | { 158 | if ($this->transport === 'unix') { 159 | return false; 160 | } 161 | return strpos($this->getRemoteIp(), ':') === false; 162 | } 163 | 164 | /** 165 | * Is ipv6. 166 | * 167 | * return bool. 168 | */ 169 | public function isIpV6() 170 | { 171 | if ($this->transport === 'unix') { 172 | return false; 173 | } 174 | return strpos($this->getRemoteIp(), ':') !== false; 175 | } 176 | 177 | /** 178 | * Close connection. 179 | * 180 | * @param mixed $data 181 | * @param bool $raw 182 | * @return bool 183 | */ 184 | public function close($data = null, $raw = false) 185 | { 186 | if ($data !== null) { 187 | $this->send($data, $raw); 188 | } 189 | return true; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Events/Ev.php: -------------------------------------------------------------------------------- 1 | 10 | * @link http://www.workerman.net/ 11 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 12 | */ 13 | namespace Workerman\Events; 14 | 15 | use Workerman\Worker; 16 | 17 | /** 18 | * ev eventloop 19 | */ 20 | class Ev implements EventInterface 21 | { 22 | /** 23 | * All listeners for read/write event. 24 | * 25 | * @var array 26 | */ 27 | protected $_allEvents = array(); 28 | 29 | /** 30 | * Event listeners of signal. 31 | * 32 | * @var array 33 | */ 34 | protected $_eventSignal = array(); 35 | 36 | /** 37 | * All timer event listeners. 38 | * [func, args, event, flag, time_interval] 39 | * 40 | * @var array 41 | */ 42 | protected $_eventTimer = array(); 43 | 44 | /** 45 | * Timer id. 46 | * 47 | * @var int 48 | */ 49 | protected static $_timerId = 1; 50 | 51 | /** 52 | * Add a timer. 53 | * {@inheritdoc} 54 | */ 55 | public function add($fd, $flag, $func, $args = null) 56 | { 57 | $callback = function ($event, $socket) use ($fd, $func) { 58 | try { 59 | call_user_func($func, $fd); 60 | } catch (\Exception $e) { 61 | Worker::log($e); 62 | exit(250); 63 | } catch (\Error $e) { 64 | Worker::log($e); 65 | exit(250); 66 | } 67 | }; 68 | switch ($flag) { 69 | case self::EV_SIGNAL: 70 | $event = new \EvSignal($fd, $callback); 71 | $this->_eventSignal[$fd] = $event; 72 | return true; 73 | case self::EV_TIMER: 74 | case self::EV_TIMER_ONCE: 75 | $repeat = $flag == self::EV_TIMER_ONCE ? 0 : $fd; 76 | $param = array($func, (array)$args, $flag, $fd, self::$_timerId); 77 | $event = new \EvTimer($fd, $repeat, array($this, 'timerCallback'), $param); 78 | $this->_eventTimer[self::$_timerId] = $event; 79 | return self::$_timerId++; 80 | default : 81 | $fd_key = (int)$fd; 82 | $real_flag = $flag === self::EV_READ ? \Ev::READ : \Ev::WRITE; 83 | $event = new \EvIo($fd, $real_flag, $callback); 84 | $this->_allEvents[$fd_key][$flag] = $event; 85 | return true; 86 | } 87 | 88 | } 89 | 90 | /** 91 | * Remove a timer. 92 | * {@inheritdoc} 93 | */ 94 | public function del($fd, $flag) 95 | { 96 | switch ($flag) { 97 | case self::EV_READ: 98 | case self::EV_WRITE: 99 | $fd_key = (int)$fd; 100 | if (isset($this->_allEvents[$fd_key][$flag])) { 101 | $this->_allEvents[$fd_key][$flag]->stop(); 102 | unset($this->_allEvents[$fd_key][$flag]); 103 | } 104 | if (empty($this->_allEvents[$fd_key])) { 105 | unset($this->_allEvents[$fd_key]); 106 | } 107 | break; 108 | case self::EV_SIGNAL: 109 | $fd_key = (int)$fd; 110 | if (isset($this->_eventSignal[$fd_key])) { 111 | $this->_eventSignal[$fd_key]->stop(); 112 | unset($this->_eventSignal[$fd_key]); 113 | } 114 | break; 115 | case self::EV_TIMER: 116 | case self::EV_TIMER_ONCE: 117 | if (isset($this->_eventTimer[$fd])) { 118 | $this->_eventTimer[$fd]->stop(); 119 | unset($this->_eventTimer[$fd]); 120 | } 121 | break; 122 | } 123 | return true; 124 | } 125 | 126 | /** 127 | * Timer callback. 128 | * 129 | * @param \EvWatcher $event 130 | */ 131 | public function timerCallback($event) 132 | { 133 | $param = $event->data; 134 | $timer_id = $param[4]; 135 | if ($param[2] === self::EV_TIMER_ONCE) { 136 | $this->_eventTimer[$timer_id]->stop(); 137 | unset($this->_eventTimer[$timer_id]); 138 | } 139 | try { 140 | call_user_func_array($param[0], $param[1]); 141 | } catch (\Exception $e) { 142 | Worker::log($e); 143 | exit(250); 144 | } catch (\Error $e) { 145 | Worker::log($e); 146 | exit(250); 147 | } 148 | } 149 | 150 | /** 151 | * Remove all timers. 152 | * 153 | * @return void 154 | */ 155 | public function clearAllTimer() 156 | { 157 | foreach ($this->_eventTimer as $event) { 158 | $event->stop(); 159 | } 160 | $this->_eventTimer = array(); 161 | } 162 | 163 | /** 164 | * Main loop. 165 | * 166 | * @see EventInterface::loop() 167 | */ 168 | public function loop() 169 | { 170 | \Ev::run(); 171 | } 172 | 173 | /** 174 | * Destroy loop. 175 | * 176 | * @return void 177 | */ 178 | public function destroy() 179 | { 180 | foreach ($this->_allEvents as $event) { 181 | $event->stop(); 182 | } 183 | } 184 | 185 | /** 186 | * Get timer count. 187 | * 188 | * @return integer 189 | */ 190 | public function getTimerCount() 191 | { 192 | return count($this->_eventTimer); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Events/Event.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright 有个鬼<42765633@qq.com> 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Events; 15 | 16 | use Workerman\Worker; 17 | 18 | /** 19 | * libevent eventloop 20 | */ 21 | class Event implements EventInterface 22 | { 23 | /** 24 | * Event base. 25 | * @var object 26 | */ 27 | protected $_eventBase = null; 28 | 29 | /** 30 | * All listeners for read/write event. 31 | * @var array 32 | */ 33 | protected $_allEvents = array(); 34 | 35 | /** 36 | * Event listeners of signal. 37 | * @var array 38 | */ 39 | protected $_eventSignal = array(); 40 | 41 | /** 42 | * All timer event listeners. 43 | * [func, args, event, flag, time_interval] 44 | * @var array 45 | */ 46 | protected $_eventTimer = array(); 47 | 48 | /** 49 | * Timer id. 50 | * @var int 51 | */ 52 | protected static $_timerId = 1; 53 | 54 | /** 55 | * construct 56 | * @return void 57 | */ 58 | public function __construct() 59 | { 60 | $this->_eventBase = new \EventBase(); 61 | } 62 | 63 | /** 64 | * @see EventInterface::add() 65 | */ 66 | public function add($fd, $flag, $func, $args=array()) 67 | { 68 | switch ($flag) { 69 | case self::EV_SIGNAL: 70 | 71 | $fd_key = (int)$fd; 72 | $event = \Event::signal($this->_eventBase, $fd, $func); 73 | if (!$event||!$event->add()) { 74 | return false; 75 | } 76 | $this->_eventSignal[$fd_key] = $event; 77 | return true; 78 | 79 | case self::EV_TIMER: 80 | case self::EV_TIMER_ONCE: 81 | 82 | $param = array($func, (array)$args, $flag, $fd, self::$_timerId); 83 | $event = new \Event($this->_eventBase, -1, \Event::TIMEOUT|\Event::PERSIST, array($this, "timerCallback"), $param); 84 | if (!$event||!$event->addTimer($fd)) { 85 | return false; 86 | } 87 | $this->_eventTimer[self::$_timerId] = $event; 88 | return self::$_timerId++; 89 | 90 | default : 91 | $fd_key = (int)$fd; 92 | $real_flag = $flag === self::EV_READ ? \Event::READ | \Event::PERSIST : \Event::WRITE | \Event::PERSIST; 93 | $event = new \Event($this->_eventBase, $fd, $real_flag, $func, $fd); 94 | if (!$event||!$event->add()) { 95 | return false; 96 | } 97 | $this->_allEvents[$fd_key][$flag] = $event; 98 | return true; 99 | } 100 | } 101 | 102 | /** 103 | * @see Events\EventInterface::del() 104 | */ 105 | public function del($fd, $flag) 106 | { 107 | switch ($flag) { 108 | 109 | case self::EV_READ: 110 | case self::EV_WRITE: 111 | 112 | $fd_key = (int)$fd; 113 | if (isset($this->_allEvents[$fd_key][$flag])) { 114 | $this->_allEvents[$fd_key][$flag]->del(); 115 | unset($this->_allEvents[$fd_key][$flag]); 116 | } 117 | if (empty($this->_allEvents[$fd_key])) { 118 | unset($this->_allEvents[$fd_key]); 119 | } 120 | break; 121 | 122 | case self::EV_SIGNAL: 123 | $fd_key = (int)$fd; 124 | if (isset($this->_eventSignal[$fd_key])) { 125 | $this->_eventSignal[$fd_key]->del(); 126 | unset($this->_eventSignal[$fd_key]); 127 | } 128 | break; 129 | 130 | case self::EV_TIMER: 131 | case self::EV_TIMER_ONCE: 132 | if (isset($this->_eventTimer[$fd])) { 133 | $this->_eventTimer[$fd]->del(); 134 | unset($this->_eventTimer[$fd]); 135 | } 136 | break; 137 | } 138 | return true; 139 | } 140 | 141 | /** 142 | * Timer callback. 143 | * @param null $fd 144 | * @param int $what 145 | * @param int $timer_id 146 | */ 147 | public function timerCallback($fd, $what, $param) 148 | { 149 | $timer_id = $param[4]; 150 | 151 | if ($param[2] === self::EV_TIMER_ONCE) { 152 | $this->_eventTimer[$timer_id]->del(); 153 | unset($this->_eventTimer[$timer_id]); 154 | } 155 | 156 | try { 157 | call_user_func_array($param[0], $param[1]); 158 | } catch (\Exception $e) { 159 | Worker::log($e); 160 | exit(250); 161 | } catch (\Error $e) { 162 | Worker::log($e); 163 | exit(250); 164 | } 165 | } 166 | 167 | /** 168 | * @see Events\EventInterface::clearAllTimer() 169 | * @return void 170 | */ 171 | public function clearAllTimer() 172 | { 173 | foreach ($this->_eventTimer as $event) { 174 | $event->del(); 175 | } 176 | $this->_eventTimer = array(); 177 | } 178 | 179 | 180 | /** 181 | * @see EventInterface::loop() 182 | */ 183 | public function loop() 184 | { 185 | $this->_eventBase->loop(); 186 | } 187 | 188 | /** 189 | * Destroy loop. 190 | * 191 | * @return void 192 | */ 193 | public function destroy() 194 | { 195 | foreach ($this->_eventSignal as $event) { 196 | $event->del(); 197 | } 198 | } 199 | 200 | /** 201 | * Get timer count. 202 | * 203 | * @return integer 204 | */ 205 | public function getTimerCount() 206 | { 207 | return count($this->_eventTimer); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Events/EventInterface.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Events; 15 | 16 | interface EventInterface 17 | { 18 | /** 19 | * Read event. 20 | * 21 | * @var int 22 | */ 23 | const EV_READ = 1; 24 | 25 | /** 26 | * Write event. 27 | * 28 | * @var int 29 | */ 30 | const EV_WRITE = 2; 31 | 32 | /** 33 | * Except event 34 | * 35 | * @var int 36 | */ 37 | const EV_EXCEPT = 3; 38 | 39 | /** 40 | * Signal event. 41 | * 42 | * @var int 43 | */ 44 | const EV_SIGNAL = 4; 45 | 46 | /** 47 | * Timer event. 48 | * 49 | * @var int 50 | */ 51 | const EV_TIMER = 8; 52 | 53 | /** 54 | * Timer once event. 55 | * 56 | * @var int 57 | */ 58 | const EV_TIMER_ONCE = 16; 59 | 60 | /** 61 | * Add event listener to event loop. 62 | * 63 | * @param mixed $fd 64 | * @param int $flag 65 | * @param callable $func 66 | * @param mixed $args 67 | * @return bool 68 | */ 69 | public function add($fd, $flag, $func, $args = null); 70 | 71 | /** 72 | * Remove event listener from event loop. 73 | * 74 | * @param mixed $fd 75 | * @param int $flag 76 | * @return bool 77 | */ 78 | public function del($fd, $flag); 79 | 80 | /** 81 | * Remove all timers. 82 | * 83 | * @return void 84 | */ 85 | public function clearAllTimer(); 86 | 87 | /** 88 | * Main loop. 89 | * 90 | * @return void 91 | */ 92 | public function loop(); 93 | 94 | /** 95 | * Destroy loop. 96 | * 97 | * @return mixed 98 | */ 99 | public function destroy(); 100 | 101 | /** 102 | * Get Timer count. 103 | * 104 | * @return mixed 105 | */ 106 | public function getTimerCount(); 107 | } 108 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Events/Libevent.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Events; 15 | 16 | use Workerman\Worker; 17 | 18 | /** 19 | * libevent eventloop 20 | */ 21 | class Libevent implements EventInterface 22 | { 23 | /** 24 | * Event base. 25 | * 26 | * @var resource 27 | */ 28 | protected $_eventBase = null; 29 | 30 | /** 31 | * All listeners for read/write event. 32 | * 33 | * @var array 34 | */ 35 | protected $_allEvents = array(); 36 | 37 | /** 38 | * Event listeners of signal. 39 | * 40 | * @var array 41 | */ 42 | protected $_eventSignal = array(); 43 | 44 | /** 45 | * All timer event listeners. 46 | * [func, args, event, flag, time_interval] 47 | * 48 | * @var array 49 | */ 50 | protected $_eventTimer = array(); 51 | 52 | /** 53 | * construct 54 | */ 55 | public function __construct() 56 | { 57 | $this->_eventBase = event_base_new(); 58 | } 59 | 60 | /** 61 | * {@inheritdoc} 62 | */ 63 | public function add($fd, $flag, $func, $args = array()) 64 | { 65 | switch ($flag) { 66 | case self::EV_SIGNAL: 67 | $fd_key = (int)$fd; 68 | $real_flag = EV_SIGNAL | EV_PERSIST; 69 | $this->_eventSignal[$fd_key] = event_new(); 70 | if (!event_set($this->_eventSignal[$fd_key], $fd, $real_flag, $func, null)) { 71 | return false; 72 | } 73 | if (!event_base_set($this->_eventSignal[$fd_key], $this->_eventBase)) { 74 | return false; 75 | } 76 | if (!event_add($this->_eventSignal[$fd_key])) { 77 | return false; 78 | } 79 | return true; 80 | case self::EV_TIMER: 81 | case self::EV_TIMER_ONCE: 82 | $event = event_new(); 83 | $timer_id = (int)$event; 84 | if (!event_set($event, 0, EV_TIMEOUT, array($this, 'timerCallback'), $timer_id)) { 85 | return false; 86 | } 87 | 88 | if (!event_base_set($event, $this->_eventBase)) { 89 | return false; 90 | } 91 | 92 | $time_interval = $fd * 1000000; 93 | if (!event_add($event, $time_interval)) { 94 | return false; 95 | } 96 | $this->_eventTimer[$timer_id] = array($func, (array)$args, $event, $flag, $time_interval); 97 | return $timer_id; 98 | 99 | default : 100 | $fd_key = (int)$fd; 101 | $real_flag = $flag === self::EV_READ ? EV_READ | EV_PERSIST : EV_WRITE | EV_PERSIST; 102 | 103 | $event = event_new(); 104 | 105 | if (!event_set($event, $fd, $real_flag, $func, null)) { 106 | return false; 107 | } 108 | 109 | if (!event_base_set($event, $this->_eventBase)) { 110 | return false; 111 | } 112 | 113 | if (!event_add($event)) { 114 | return false; 115 | } 116 | 117 | $this->_allEvents[$fd_key][$flag] = $event; 118 | 119 | return true; 120 | } 121 | 122 | } 123 | 124 | /** 125 | * {@inheritdoc} 126 | */ 127 | public function del($fd, $flag) 128 | { 129 | switch ($flag) { 130 | case self::EV_READ: 131 | case self::EV_WRITE: 132 | $fd_key = (int)$fd; 133 | if (isset($this->_allEvents[$fd_key][$flag])) { 134 | event_del($this->_allEvents[$fd_key][$flag]); 135 | unset($this->_allEvents[$fd_key][$flag]); 136 | } 137 | if (empty($this->_allEvents[$fd_key])) { 138 | unset($this->_allEvents[$fd_key]); 139 | } 140 | break; 141 | case self::EV_SIGNAL: 142 | $fd_key = (int)$fd; 143 | if (isset($this->_eventSignal[$fd_key])) { 144 | event_del($this->_eventSignal[$fd_key]); 145 | unset($this->_eventSignal[$fd_key]); 146 | } 147 | break; 148 | case self::EV_TIMER: 149 | case self::EV_TIMER_ONCE: 150 | // 这里 fd 为timerid 151 | if (isset($this->_eventTimer[$fd])) { 152 | event_del($this->_eventTimer[$fd][2]); 153 | unset($this->_eventTimer[$fd]); 154 | } 155 | break; 156 | } 157 | return true; 158 | } 159 | 160 | /** 161 | * Timer callback. 162 | * 163 | * @param mixed $_null1 164 | * @param int $_null2 165 | * @param mixed $timer_id 166 | */ 167 | protected function timerCallback($_null1, $_null2, $timer_id) 168 | { 169 | if ($this->_eventTimer[$timer_id][3] === self::EV_TIMER) { 170 | event_add($this->_eventTimer[$timer_id][2], $this->_eventTimer[$timer_id][4]); 171 | } 172 | try { 173 | call_user_func_array($this->_eventTimer[$timer_id][0], $this->_eventTimer[$timer_id][1]); 174 | } catch (\Exception $e) { 175 | Worker::log($e); 176 | exit(250); 177 | } catch (\Error $e) { 178 | Worker::log($e); 179 | exit(250); 180 | } 181 | if (isset($this->_eventTimer[$timer_id]) && $this->_eventTimer[$timer_id][3] === self::EV_TIMER_ONCE) { 182 | $this->del($timer_id, self::EV_TIMER_ONCE); 183 | } 184 | } 185 | 186 | /** 187 | * {@inheritdoc} 188 | */ 189 | public function clearAllTimer() 190 | { 191 | foreach ($this->_eventTimer as $task_data) { 192 | event_del($task_data[2]); 193 | } 194 | $this->_eventTimer = array(); 195 | } 196 | 197 | /** 198 | * {@inheritdoc} 199 | */ 200 | public function loop() 201 | { 202 | event_base_loop($this->_eventBase); 203 | } 204 | 205 | /** 206 | * Destroy loop. 207 | * 208 | * @return void 209 | */ 210 | public function destroy() 211 | { 212 | foreach ($this->_eventSignal as $event) { 213 | event_del($event); 214 | } 215 | } 216 | 217 | /** 218 | * Get timer count. 219 | * 220 | * @return integer 221 | */ 222 | public function getTimerCount() 223 | { 224 | return count($this->_eventTimer); 225 | } 226 | } 227 | 228 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Events/React/Base.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Events\React; 15 | use Workerman\Events\EventInterface; 16 | use React\EventLoop\TimerInterface; 17 | 18 | /** 19 | * Class StreamSelectLoop 20 | * @package Workerman\Events\React 21 | */ 22 | class Base implements \React\EventLoop\LoopInterface 23 | { 24 | /** 25 | * @var array 26 | */ 27 | protected $_timerIdMap = array(); 28 | 29 | /** 30 | * @var int 31 | */ 32 | protected $_timerIdIndex = 0; 33 | 34 | /** 35 | * @var array 36 | */ 37 | protected $_signalHandlerMap = array(); 38 | 39 | /** 40 | * @var \React\EventLoop\LoopInterface 41 | */ 42 | protected $_eventLoop = null; 43 | 44 | /** 45 | * Base constructor. 46 | */ 47 | public function __construct() 48 | { 49 | $this->_eventLoop = new \React\EventLoop\StreamSelectLoop(); 50 | } 51 | 52 | /** 53 | * Add event listener to event loop. 54 | * 55 | * @param $fd 56 | * @param $flag 57 | * @param $func 58 | * @param array $args 59 | * @return bool 60 | */ 61 | public function add($fd, $flag, $func, $args = array()) 62 | { 63 | $args = (array)$args; 64 | switch ($flag) { 65 | case EventInterface::EV_READ: 66 | return $this->addReadStream($fd, $func); 67 | case EventInterface::EV_WRITE: 68 | return $this->addWriteStream($fd, $func); 69 | case EventInterface::EV_SIGNAL: 70 | if (isset($this->_signalHandlerMap[$fd])) { 71 | $this->removeSignal($fd, $this->_signalHandlerMap[$fd]); 72 | } 73 | $this->_signalHandlerMap[$fd] = $func; 74 | return $this->addSignal($fd, $func); 75 | case EventInterface::EV_TIMER: 76 | $timer_obj = $this->addPeriodicTimer($fd, function() use ($func, $args) { 77 | call_user_func_array($func, $args); 78 | }); 79 | $this->_timerIdMap[++$this->_timerIdIndex] = $timer_obj; 80 | return $this->_timerIdIndex; 81 | case EventInterface::EV_TIMER_ONCE: 82 | $index = ++$this->_timerIdIndex; 83 | $timer_obj = $this->addTimer($fd, function() use ($func, $args, $index) { 84 | $this->del($index,EventInterface::EV_TIMER_ONCE); 85 | call_user_func_array($func, $args); 86 | }); 87 | $this->_timerIdMap[$index] = $timer_obj; 88 | return $this->_timerIdIndex; 89 | } 90 | return false; 91 | } 92 | 93 | /** 94 | * Remove event listener from event loop. 95 | * 96 | * @param mixed $fd 97 | * @param int $flag 98 | * @return bool 99 | */ 100 | public function del($fd, $flag) 101 | { 102 | switch ($flag) { 103 | case EventInterface::EV_READ: 104 | return $this->removeReadStream($fd); 105 | case EventInterface::EV_WRITE: 106 | return $this->removeWriteStream($fd); 107 | case EventInterface::EV_SIGNAL: 108 | if (!isset($this->_eventLoop[$fd])) { 109 | return false; 110 | } 111 | $func = $this->_eventLoop[$fd]; 112 | unset($this->_eventLoop[$fd]); 113 | return $this->removeSignal($fd, $func); 114 | 115 | case EventInterface::EV_TIMER: 116 | case EventInterface::EV_TIMER_ONCE: 117 | if (isset($this->_timerIdMap[$fd])){ 118 | $timer_obj = $this->_timerIdMap[$fd]; 119 | unset($this->_timerIdMap[$fd]); 120 | $this->cancelTimer($timer_obj); 121 | return true; 122 | } 123 | } 124 | return false; 125 | } 126 | 127 | 128 | /** 129 | * Main loop. 130 | * 131 | * @return void 132 | */ 133 | public function loop() 134 | { 135 | $this->run(); 136 | } 137 | 138 | 139 | /** 140 | * Destroy loop. 141 | * 142 | * @return void 143 | */ 144 | public function destroy() 145 | { 146 | 147 | } 148 | 149 | /** 150 | * Get timer count. 151 | * 152 | * @return integer 153 | */ 154 | public function getTimerCount() 155 | { 156 | return count($this->_timerIdMap); 157 | } 158 | 159 | /** 160 | * @param resource $stream 161 | * @param callable $listener 162 | */ 163 | public function addReadStream($stream, $listener) 164 | { 165 | return $this->_eventLoop->addReadStream($stream, $listener); 166 | } 167 | 168 | /** 169 | * @param resource $stream 170 | * @param callable $listener 171 | */ 172 | public function addWriteStream($stream, $listener) 173 | { 174 | return $this->_eventLoop->addWriteStream($stream, $listener); 175 | } 176 | 177 | /** 178 | * @param resource $stream 179 | */ 180 | public function removeReadStream($stream) 181 | { 182 | return $this->_eventLoop->removeReadStream($stream); 183 | } 184 | 185 | /** 186 | * @param resource $stream 187 | */ 188 | public function removeWriteStream($stream) 189 | { 190 | return $this->_eventLoop->removeWriteStream($stream); 191 | } 192 | 193 | /** 194 | * @param float|int $interval 195 | * @param callable $callback 196 | * @return \React\EventLoop\Timer\Timer|TimerInterface 197 | */ 198 | public function addTimer($interval, $callback) 199 | { 200 | return $this->_eventLoop->addTimer($interval, $callback); 201 | } 202 | 203 | /** 204 | * @param float|int $interval 205 | * @param callable $callback 206 | * @return \React\EventLoop\Timer\Timer|TimerInterface 207 | */ 208 | public function addPeriodicTimer($interval, $callback) 209 | { 210 | return $this->_eventLoop->addPeriodicTimer($interval, $callback); 211 | } 212 | 213 | /** 214 | * @param TimerInterface $timer 215 | */ 216 | public function cancelTimer(TimerInterface $timer) 217 | { 218 | return $this->_eventLoop->cancelTimer($timer); 219 | } 220 | 221 | /** 222 | * @param callable $listener 223 | */ 224 | public function futureTick($listener) 225 | { 226 | return $this->_eventLoop->futureTick($listener); 227 | } 228 | 229 | /** 230 | * @param int $signal 231 | * @param callable $listener 232 | */ 233 | public function addSignal($signal, $listener) 234 | { 235 | return $this->_eventLoop->addSignal($signal, $listener); 236 | } 237 | 238 | /** 239 | * @param int $signal 240 | * @param callable $listener 241 | */ 242 | public function removeSignal($signal, $listener) 243 | { 244 | return $this->_eventLoop->removeSignal($signal, $listener); 245 | } 246 | 247 | /** 248 | * Run. 249 | */ 250 | public function run() 251 | { 252 | return $this->_eventLoop->run(); 253 | } 254 | 255 | /** 256 | * Stop. 257 | */ 258 | public function stop() 259 | { 260 | return $this->_eventLoop->stop(); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Events/React/ExtEventLoop.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Events\React; 15 | 16 | /** 17 | * Class ExtEventLoop 18 | * @package Workerman\Events\React 19 | */ 20 | class ExtEventLoop extends Base 21 | { 22 | 23 | public function __construct() 24 | { 25 | $this->_eventLoop = new \React\EventLoop\ExtEventLoop(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Events/React/ExtLibEventLoop.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Events\React; 15 | use Workerman\Events\EventInterface; 16 | 17 | /** 18 | * Class ExtLibEventLoop 19 | * @package Workerman\Events\React 20 | */ 21 | class ExtLibEventLoop extends Base 22 | { 23 | public function __construct() 24 | { 25 | $this->_eventLoop = new \React\EventLoop\ExtLibeventLoop(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Events/React/StreamSelectLoop.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Events\React; 15 | 16 | /** 17 | * Class StreamSelectLoop 18 | * @package Workerman\Events\React 19 | */ 20 | class StreamSelectLoop extends Base 21 | { 22 | public function __construct() 23 | { 24 | $this->_eventLoop = new \React\EventLoop\StreamSelectLoop(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Events/Select.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Events; 15 | 16 | /** 17 | * select eventloop 18 | */ 19 | class Select implements EventInterface 20 | { 21 | /** 22 | * All listeners for read/write event. 23 | * 24 | * @var array 25 | */ 26 | public $_allEvents = array(); 27 | 28 | /** 29 | * Event listeners of signal. 30 | * 31 | * @var array 32 | */ 33 | public $_signalEvents = array(); 34 | 35 | /** 36 | * Fds waiting for read event. 37 | * 38 | * @var array 39 | */ 40 | protected $_readFds = array(); 41 | 42 | /** 43 | * Fds waiting for write event. 44 | * 45 | * @var array 46 | */ 47 | protected $_writeFds = array(); 48 | 49 | /** 50 | * Fds waiting for except event. 51 | * 52 | * @var array 53 | */ 54 | protected $_exceptFds = array(); 55 | 56 | /** 57 | * Timer scheduler. 58 | * {['data':timer_id, 'priority':run_timestamp], ..} 59 | * 60 | * @var \SplPriorityQueue 61 | */ 62 | protected $_scheduler = null; 63 | 64 | /** 65 | * All timer event listeners. 66 | * [[func, args, flag, timer_interval], ..] 67 | * 68 | * @var array 69 | */ 70 | protected $_eventTimer = array(); 71 | 72 | /** 73 | * Timer id. 74 | * 75 | * @var int 76 | */ 77 | protected $_timerId = 1; 78 | 79 | /** 80 | * Select timeout. 81 | * 82 | * @var int 83 | */ 84 | protected $_selectTimeout = 100000000; 85 | 86 | /** 87 | * Paired socket channels 88 | * 89 | * @var array 90 | */ 91 | protected $channel = array(); 92 | 93 | /** 94 | * Construct. 95 | */ 96 | public function __construct() 97 | { 98 | // Create a pipeline and put into the collection of the read to read the descriptor to avoid empty polling. 99 | $this->channel = stream_socket_pair(DIRECTORY_SEPARATOR === '/' ? STREAM_PF_UNIX : STREAM_PF_INET, 100 | STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); 101 | if($this->channel) { 102 | stream_set_blocking($this->channel[0], 0); 103 | $this->_readFds[0] = $this->channel[0]; 104 | } 105 | // Init SplPriorityQueue. 106 | $this->_scheduler = new \SplPriorityQueue(); 107 | $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); 108 | } 109 | 110 | /** 111 | * {@inheritdoc} 112 | */ 113 | public function add($fd, $flag, $func, $args = array()) 114 | { 115 | switch ($flag) { 116 | case self::EV_READ: 117 | $fd_key = (int)$fd; 118 | $this->_allEvents[$fd_key][$flag] = array($func, $fd); 119 | $this->_readFds[$fd_key] = $fd; 120 | break; 121 | case self::EV_WRITE: 122 | $fd_key = (int)$fd; 123 | $this->_allEvents[$fd_key][$flag] = array($func, $fd); 124 | $this->_writeFds[$fd_key] = $fd; 125 | break; 126 | case self::EV_EXCEPT: 127 | $fd_key = (int)$fd; 128 | $this->_allEvents[$fd_key][$flag] = array($func, $fd); 129 | $this->_exceptFds[$fd_key] = $fd; 130 | break; 131 | case self::EV_SIGNAL: 132 | // Windows not support signal. 133 | if(DIRECTORY_SEPARATOR !== '/') { 134 | return false; 135 | } 136 | $fd_key = (int)$fd; 137 | $this->_signalEvents[$fd_key][$flag] = array($func, $fd); 138 | pcntl_signal($fd, array($this, 'signalHandler')); 139 | break; 140 | case self::EV_TIMER: 141 | case self::EV_TIMER_ONCE: 142 | $timer_id = $this->_timerId++; 143 | $run_time = microtime(true) + $fd; 144 | $this->_scheduler->insert($timer_id, -$run_time); 145 | $this->_eventTimer[$timer_id] = array($func, (array)$args, $flag, $fd); 146 | $select_timeout = ($run_time - microtime(true)) * 1000000; 147 | if( $this->_selectTimeout > $select_timeout ){ 148 | $this->_selectTimeout = $select_timeout; 149 | } 150 | return $timer_id; 151 | } 152 | 153 | return true; 154 | } 155 | 156 | /** 157 | * Signal handler. 158 | * 159 | * @param int $signal 160 | */ 161 | public function signalHandler($signal) 162 | { 163 | call_user_func_array($this->_signalEvents[$signal][self::EV_SIGNAL][0], array($signal)); 164 | } 165 | 166 | /** 167 | * {@inheritdoc} 168 | */ 169 | public function del($fd, $flag) 170 | { 171 | $fd_key = (int)$fd; 172 | switch ($flag) { 173 | case self::EV_READ: 174 | unset($this->_allEvents[$fd_key][$flag], $this->_readFds[$fd_key]); 175 | if (empty($this->_allEvents[$fd_key])) { 176 | unset($this->_allEvents[$fd_key]); 177 | } 178 | return true; 179 | case self::EV_WRITE: 180 | unset($this->_allEvents[$fd_key][$flag], $this->_writeFds[$fd_key]); 181 | if (empty($this->_allEvents[$fd_key])) { 182 | unset($this->_allEvents[$fd_key]); 183 | } 184 | return true; 185 | case self::EV_EXCEPT: 186 | unset($this->_allEvents[$fd_key][$flag], $this->_exceptFds[$fd_key]); 187 | if(empty($this->_allEvents[$fd_key])) 188 | { 189 | unset($this->_allEvents[$fd_key]); 190 | } 191 | return true; 192 | case self::EV_SIGNAL: 193 | if(DIRECTORY_SEPARATOR !== '/') { 194 | return false; 195 | } 196 | unset($this->_signalEvents[$fd_key]); 197 | pcntl_signal($fd, SIG_IGN); 198 | break; 199 | case self::EV_TIMER: 200 | case self::EV_TIMER_ONCE; 201 | unset($this->_eventTimer[$fd_key]); 202 | return true; 203 | } 204 | return false; 205 | } 206 | 207 | /** 208 | * Tick for timer. 209 | * 210 | * @return void 211 | */ 212 | protected function tick() 213 | { 214 | while (!$this->_scheduler->isEmpty()) { 215 | $scheduler_data = $this->_scheduler->top(); 216 | $timer_id = $scheduler_data['data']; 217 | $next_run_time = -$scheduler_data['priority']; 218 | $time_now = microtime(true); 219 | $this->_selectTimeout = ($next_run_time - $time_now) * 1000000; 220 | if ($this->_selectTimeout <= 0) { 221 | $this->_scheduler->extract(); 222 | 223 | if (!isset($this->_eventTimer[$timer_id])) { 224 | continue; 225 | } 226 | 227 | // [func, args, flag, timer_interval] 228 | $task_data = $this->_eventTimer[$timer_id]; 229 | if ($task_data[2] === self::EV_TIMER) { 230 | $next_run_time = $time_now + $task_data[3]; 231 | $this->_scheduler->insert($timer_id, -$next_run_time); 232 | } 233 | call_user_func_array($task_data[0], $task_data[1]); 234 | if (isset($this->_eventTimer[$timer_id]) && $task_data[2] === self::EV_TIMER_ONCE) { 235 | $this->del($timer_id, self::EV_TIMER_ONCE); 236 | } 237 | continue; 238 | } 239 | return; 240 | } 241 | $this->_selectTimeout = 100000000; 242 | } 243 | 244 | /** 245 | * {@inheritdoc} 246 | */ 247 | public function clearAllTimer() 248 | { 249 | $this->_scheduler = new \SplPriorityQueue(); 250 | $this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); 251 | $this->_eventTimer = array(); 252 | } 253 | 254 | /** 255 | * {@inheritdoc} 256 | */ 257 | public function loop() 258 | { 259 | $e = null; 260 | while (1) { 261 | if(DIRECTORY_SEPARATOR === '/') { 262 | // Calls signal handlers for pending signals 263 | pcntl_signal_dispatch(); 264 | } 265 | 266 | $read = $this->_readFds; 267 | $write = $this->_writeFds; 268 | $except = $this->_exceptFds; 269 | 270 | // Waiting read/write/signal/timeout events. 271 | set_error_handler(function(){}); 272 | $ret = stream_select($read, $write, $except, 0, $this->_selectTimeout); 273 | restore_error_handler(); 274 | 275 | 276 | if (!$this->_scheduler->isEmpty()) { 277 | $this->tick(); 278 | } 279 | 280 | if (!$ret) { 281 | continue; 282 | } 283 | 284 | if ($read) { 285 | foreach ($read as $fd) { 286 | $fd_key = (int)$fd; 287 | if (isset($this->_allEvents[$fd_key][self::EV_READ])) { 288 | call_user_func_array($this->_allEvents[$fd_key][self::EV_READ][0], 289 | array($this->_allEvents[$fd_key][self::EV_READ][1])); 290 | } 291 | } 292 | } 293 | 294 | if ($write) { 295 | foreach ($write as $fd) { 296 | $fd_key = (int)$fd; 297 | if (isset($this->_allEvents[$fd_key][self::EV_WRITE])) { 298 | call_user_func_array($this->_allEvents[$fd_key][self::EV_WRITE][0], 299 | array($this->_allEvents[$fd_key][self::EV_WRITE][1])); 300 | } 301 | } 302 | } 303 | 304 | if($except) { 305 | foreach($except as $fd) { 306 | $fd_key = (int) $fd; 307 | if(isset($this->_allEvents[$fd_key][self::EV_EXCEPT])) { 308 | call_user_func_array($this->_allEvents[$fd_key][self::EV_EXCEPT][0], 309 | array($this->_allEvents[$fd_key][self::EV_EXCEPT][1])); 310 | } 311 | } 312 | } 313 | } 314 | } 315 | 316 | /** 317 | * Destroy loop. 318 | * 319 | * @return void 320 | */ 321 | public function destroy() 322 | { 323 | 324 | } 325 | 326 | /** 327 | * Get timer count. 328 | * 329 | * @return integer 330 | */ 331 | public function getTimerCount() 332 | { 333 | return count($this->_eventTimer); 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Events/Swoole.php: -------------------------------------------------------------------------------- 1 | 10 | * @link http://www.workerman.net/ 11 | * @link https://github.com/ares333/Workerman 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Events; 15 | 16 | use Swoole\Event; 17 | use Swoole\Timer; 18 | 19 | class Swoole implements EventInterface 20 | { 21 | 22 | protected $_timer = array(); 23 | 24 | protected $_timerOnceMap = array(); 25 | 26 | protected $_fd = array(); 27 | 28 | // milisecond 29 | public static $signalDispatchInterval = 200; 30 | 31 | protected $_hasSignal = false; 32 | 33 | /** 34 | * 35 | * {@inheritdoc} 36 | * 37 | * @see \Workerman\Events\EventInterface::add() 38 | */ 39 | public function add($fd, $flag, $func, $args = null) 40 | { 41 | if (! isset($args)) { 42 | $args = array(); 43 | } 44 | switch ($flag) { 45 | case self::EV_SIGNAL: 46 | $res = pcntl_signal($fd, $func, false); 47 | if (! $this->_hasSignal && $res) { 48 | Timer::tick(static::$signalDispatchInterval, 49 | function () { 50 | pcntl_signal_dispatch(); 51 | }); 52 | $this->_hasSignal = true; 53 | } 54 | return $res; 55 | case self::EV_TIMER: 56 | case self::EV_TIMER_ONCE: 57 | $method = self::EV_TIMER == $flag ? 'tick' : 'after'; 58 | $mapId = count($this->_timerOnceMap); 59 | $timer_id = Timer::$method($fd * 1000, 60 | function ($timer_id = null) use ($func, $args, $mapId) { 61 | call_user_func_array($func, $args); 62 | // EV_TIMER_ONCE 63 | if (! isset($timer_id)) { 64 | // may be deleted in $func 65 | if (array_key_exists($mapId, $this->_timerOnceMap)) { 66 | $timer_id = $this->_timerOnceMap[$mapId]; 67 | unset($this->_timer[$timer_id], 68 | $this->_timerOnceMap[$mapId]); 69 | } 70 | } 71 | }); 72 | if ($flag == self::EV_TIMER_ONCE) { 73 | $this->_timerOnceMap[$mapId] = $timer_id; 74 | $this->_timer[$timer_id] = $mapId; 75 | } else { 76 | $this->_timer[$timer_id] = null; 77 | } 78 | return $timer_id; 79 | case self::EV_READ: 80 | case self::EV_WRITE: 81 | $fd_key = (int) $fd; 82 | if (! isset($this->_fd[$fd_key])) { 83 | if ($flag == self::EV_READ) { 84 | $res = Event::add($fd, $func, null, SWOOLE_EVENT_READ); 85 | $fd_type = SWOOLE_EVENT_READ; 86 | } else { 87 | $res = Event::add($fd, null, $func, SWOOLE_EVENT_WRITE); 88 | $fd_type = SWOOLE_EVENT_WRITE; 89 | } 90 | if ($res) { 91 | $this->_fd[$fd_key] = $fd_type; 92 | } 93 | } else { 94 | $fd_val = $this->_fd[$fd_key]; 95 | $res = true; 96 | if ($flag == self::EV_READ) { 97 | if (($fd_val & SWOOLE_EVENT_READ) != SWOOLE_EVENT_READ) { 98 | $res = Event::set($fd, $func, null, 99 | SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); 100 | $this->_fd[$fd_key] |= SWOOLE_EVENT_READ; 101 | } 102 | } else { 103 | if (($fd_val & SWOOLE_EVENT_WRITE) != SWOOLE_EVENT_WRITE) { 104 | $res = Event::set($fd, null, $func, 105 | SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); 106 | $this->_fd[$fd_key] |= SWOOLE_EVENT_WRITE; 107 | } 108 | } 109 | } 110 | return $res; 111 | } 112 | } 113 | 114 | /** 115 | * 116 | * {@inheritdoc} 117 | * 118 | * @see \Workerman\Events\EventInterface::del() 119 | */ 120 | public function del($fd, $flag) 121 | { 122 | switch ($flag) { 123 | case self::EV_SIGNAL: 124 | return pcntl_signal($fd, SIG_IGN, false); 125 | case self::EV_TIMER: 126 | case self::EV_TIMER_ONCE: 127 | // already remove in EV_TIMER_ONCE callback. 128 | if (! array_key_exists($fd, $this->_timer)) { 129 | return true; 130 | } 131 | $res = Timer::clear($fd); 132 | if ($res) { 133 | $mapId = $this->_timer[$fd]; 134 | if (isset($mapId)) { 135 | unset($this->_timerOnceMap[$mapId]); 136 | } 137 | unset($this->_timer[$fd]); 138 | } 139 | return $res; 140 | case self::EV_READ: 141 | case self::EV_WRITE: 142 | $fd_key = (int) $fd; 143 | if (isset($this->_fd[$fd_key])) { 144 | $fd_val = $this->_fd[$fd_key]; 145 | if ($flag == self::EV_READ) { 146 | $flag_remove = ~ SWOOLE_EVENT_READ; 147 | } else { 148 | $flag_remove = ~ SWOOLE_EVENT_WRITE; 149 | } 150 | $fd_val &= $flag_remove; 151 | if (0 === $fd_val) { 152 | $res = Event::del($fd); 153 | if ($res) { 154 | unset($this->_fd[$fd_key]); 155 | } 156 | } else { 157 | $res = Event::set($fd, null, null, $fd_val); 158 | if ($res) { 159 | $this->_fd[$fd_key] = $fd_val; 160 | } 161 | } 162 | } else { 163 | $res = true; 164 | } 165 | return $res; 166 | } 167 | } 168 | 169 | /** 170 | * 171 | * {@inheritdoc} 172 | * 173 | * @see \Workerman\Events\EventInterface::clearAllTimer() 174 | */ 175 | public function clearAllTimer() 176 | { 177 | foreach (array_keys($this->_timer) as $v) { 178 | Timer::clear($v); 179 | } 180 | $this->_timer = array(); 181 | $this->_timerOnceMap = array(); 182 | } 183 | 184 | /** 185 | * 186 | * {@inheritdoc} 187 | * 188 | * @see \Workerman\Events\EventInterface::loop() 189 | */ 190 | public function loop() 191 | { 192 | Event::wait(); 193 | } 194 | 195 | /** 196 | * 197 | * {@inheritdoc} 198 | * 199 | * @see \Workerman\Events\EventInterface::destroy() 200 | */ 201 | public function destroy() 202 | { 203 | //Event::exit(); 204 | } 205 | 206 | /** 207 | * 208 | * {@inheritdoc} 209 | * 210 | * @see \Workerman\Events\EventInterface::getTimerCount() 211 | */ 212 | public function getTimerCount() 213 | { 214 | return count($this->_timer); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Lib/Constants.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | 15 | // Date.timezone 16 | if (!ini_get('date.timezone')) { 17 | date_default_timezone_set('Asia/Shanghai'); 18 | } 19 | // Display errors. 20 | ini_set('display_errors', 'on'); 21 | // Reporting all. 22 | error_reporting(E_ALL); 23 | 24 | // Reset opcache. 25 | if (function_exists('opcache_reset')) { 26 | opcache_reset(); 27 | } 28 | 29 | // For onError callback. 30 | define('WORKERMAN_CONNECT_FAIL', 1); 31 | // For onError callback. 32 | define('WORKERMAN_SEND_FAIL', 2); 33 | 34 | // Define OS Type 35 | define('OS_TYPE_LINUX', 'linux'); 36 | define('OS_TYPE_WINDOWS', 'windows'); 37 | 38 | // Compatible with php7 39 | if(!class_exists('Error')) 40 | { 41 | class Error extends Exception 42 | { 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Lib/Timer.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Lib; 15 | 16 | use Workerman\Events\EventInterface; 17 | use Exception; 18 | 19 | /** 20 | * Timer. 21 | * 22 | * example: 23 | * Workerman\Lib\Timer::add($time_interval, callback, array($arg1, $arg2..)); 24 | */ 25 | class Timer 26 | { 27 | /** 28 | * Tasks that based on ALARM signal. 29 | * [ 30 | * run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]], 31 | * run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]], 32 | * .. 33 | * ] 34 | * 35 | * @var array 36 | */ 37 | protected static $_tasks = array(); 38 | 39 | /** 40 | * event 41 | * 42 | * @var \Workerman\Events\EventInterface 43 | */ 44 | protected static $_event = null; 45 | 46 | /** 47 | * Init. 48 | * 49 | * @param \Workerman\Events\EventInterface $event 50 | * @return void 51 | */ 52 | public static function init($event = null) 53 | { 54 | if ($event) { 55 | self::$_event = $event; 56 | } else { 57 | if (function_exists('pcntl_signal')) { 58 | pcntl_signal(SIGALRM, array('\Workerman\Lib\Timer', 'signalHandle'), false); 59 | } 60 | } 61 | } 62 | 63 | /** 64 | * ALARM signal handler. 65 | * 66 | * @return void 67 | */ 68 | public static function signalHandle() 69 | { 70 | if (!self::$_event) { 71 | pcntl_alarm(1); 72 | self::tick(); 73 | } 74 | } 75 | 76 | /** 77 | * Add a timer. 78 | * 79 | * @param float $time_interval 80 | * @param callable $func 81 | * @param mixed $args 82 | * @param bool $persistent 83 | * @return int/false 84 | */ 85 | public static function add($time_interval, $func, $args = array(), $persistent = true) 86 | { 87 | if ($time_interval <= 0) { 88 | Worker::safeEcho(new Exception("bad time_interval")); 89 | return false; 90 | } 91 | 92 | if (self::$_event) { 93 | return self::$_event->add($time_interval, 94 | $persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE, $func, $args); 95 | } 96 | 97 | if (!is_callable($func)) { 98 | Worker::safeEcho(new Exception("not callable")); 99 | return false; 100 | } 101 | 102 | if (empty(self::$_tasks)) { 103 | pcntl_alarm(1); 104 | } 105 | 106 | $time_now = time(); 107 | $run_time = $time_now + $time_interval; 108 | if (!isset(self::$_tasks[$run_time])) { 109 | self::$_tasks[$run_time] = array(); 110 | } 111 | self::$_tasks[$run_time][] = array($func, (array)$args, $persistent, $time_interval); 112 | return 1; 113 | } 114 | 115 | 116 | /** 117 | * Tick. 118 | * 119 | * @return void 120 | */ 121 | public static function tick() 122 | { 123 | if (empty(self::$_tasks)) { 124 | pcntl_alarm(0); 125 | return; 126 | } 127 | 128 | $time_now = time(); 129 | foreach (self::$_tasks as $run_time => $task_data) { 130 | if ($time_now >= $run_time) { 131 | foreach ($task_data as $index => $one_task) { 132 | $task_func = $one_task[0]; 133 | $task_args = $one_task[1]; 134 | $persistent = $one_task[2]; 135 | $time_interval = $one_task[3]; 136 | try { 137 | call_user_func_array($task_func, $task_args); 138 | } catch (\Exception $e) { 139 | Worker::safeEcho($e); 140 | } 141 | if ($persistent) { 142 | self::add($time_interval, $task_func, $task_args); 143 | } 144 | } 145 | unset(self::$_tasks[$run_time]); 146 | } 147 | } 148 | } 149 | 150 | /** 151 | * Remove a timer. 152 | * 153 | * @param mixed $timer_id 154 | * @return bool 155 | */ 156 | public static function del($timer_id) 157 | { 158 | if (self::$_event) { 159 | return self::$_event->del($timer_id, EventInterface::EV_TIMER); 160 | } 161 | 162 | return false; 163 | } 164 | 165 | /** 166 | * Remove all timers. 167 | * 168 | * @return void 169 | */ 170 | public static function delAll() 171 | { 172 | self::$_tasks = array(); 173 | pcntl_alarm(0); 174 | if (self::$_event) { 175 | self::$_event->clearAllTimer(); 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2009-2015 walkor and contributors (see https://github.com/walkor/workerman/contributors) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Protocols/Frame.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Protocols; 15 | 16 | use Workerman\Connection\TcpConnection; 17 | 18 | /** 19 | * Frame Protocol. 20 | */ 21 | class Frame 22 | { 23 | /** 24 | * Check the integrity of the package. 25 | * 26 | * @param string $buffer 27 | * @param TcpConnection $connection 28 | * @return int 29 | */ 30 | public static function input($buffer, TcpConnection $connection) 31 | { 32 | if (strlen($buffer) < 4) { 33 | return 0; 34 | } 35 | $unpack_data = unpack('Ntotal_length', $buffer); 36 | return $unpack_data['total_length']; 37 | } 38 | 39 | /** 40 | * Decode. 41 | * 42 | * @param string $buffer 43 | * @return string 44 | */ 45 | public static function decode($buffer) 46 | { 47 | return substr($buffer, 4); 48 | } 49 | 50 | /** 51 | * Encode. 52 | * 53 | * @param string $buffer 54 | * @return string 55 | */ 56 | public static function encode($buffer) 57 | { 58 | $total_length = 4 + strlen($buffer); 59 | return pack('N', $total_length) . $buffer; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Protocols/Http/mime.types: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/x-javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/java-archive jar war ear; 28 | application/mac-binhex40 hqx; 29 | application/msword doc; 30 | application/pdf pdf; 31 | application/postscript ps eps ai; 32 | application/rtf rtf; 33 | application/vnd.ms-excel xls; 34 | application/vnd.ms-powerpoint ppt; 35 | application/vnd.wap.wmlc wmlc; 36 | application/vnd.google-earth.kml+xml kml; 37 | application/vnd.google-earth.kmz kmz; 38 | application/x-7z-compressed 7z; 39 | application/x-cocoa cco; 40 | application/x-java-archive-diff jardiff; 41 | application/x-java-jnlp-file jnlp; 42 | application/x-makeself run; 43 | application/x-perl pl pm; 44 | application/x-pilot prc pdb; 45 | application/x-rar-compressed rar; 46 | application/x-redhat-package-manager rpm; 47 | application/x-sea sea; 48 | application/x-shockwave-flash swf; 49 | application/x-stuffit sit; 50 | application/x-tcl tcl tk; 51 | application/x-x509-ca-cert der pem crt; 52 | application/x-xpinstall xpi; 53 | application/xhtml+xml xhtml; 54 | application/zip zip; 55 | 56 | application/octet-stream bin exe dll; 57 | application/octet-stream deb; 58 | application/octet-stream dmg; 59 | application/octet-stream eot; 60 | application/octet-stream iso img; 61 | application/octet-stream msi msp msm; 62 | 63 | audio/midi mid midi kar; 64 | audio/mpeg mp3; 65 | audio/ogg ogg; 66 | audio/x-m4a m4a; 67 | audio/x-realaudio ra; 68 | 69 | video/3gpp 3gpp 3gp; 70 | video/mp4 mp4; 71 | video/mpeg mpeg mpg; 72 | video/quicktime mov; 73 | video/webm webm; 74 | video/x-flv flv; 75 | video/x-m4v m4v; 76 | video/x-mng mng; 77 | video/x-ms-asf asx asf; 78 | video/x-ms-wmv wmv; 79 | video/x-msvideo avi; 80 | } 81 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Protocols/ProtocolInterface.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Protocols; 15 | 16 | use Workerman\Connection\ConnectionInterface; 17 | 18 | /** 19 | * Protocol interface 20 | */ 21 | interface ProtocolInterface 22 | { 23 | /** 24 | * Check the integrity of the package. 25 | * Please return the length of package. 26 | * If length is unknow please return 0 that mean wating more data. 27 | * If the package has something wrong please return false the connection will be closed. 28 | * 29 | * @param ConnectionInterface $connection 30 | * @param string $recv_buffer 31 | * @return int|false 32 | */ 33 | public static function input($recv_buffer, ConnectionInterface $connection); 34 | 35 | /** 36 | * Decode package and emit onMessage($message) callback, $message is the result that decode returned. 37 | * 38 | * @param ConnectionInterface $connection 39 | * @param string $recv_buffer 40 | * @return mixed 41 | */ 42 | public static function decode($recv_buffer, ConnectionInterface $connection); 43 | 44 | /** 45 | * Encode package brefore sending to client. 46 | * 47 | * @param ConnectionInterface $connection 48 | * @param mixed $data 49 | * @return string 50 | */ 51 | public static function encode($data, ConnectionInterface $connection); 52 | } 53 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/Protocols/Text.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman\Protocols; 15 | 16 | use Workerman\Connection\TcpConnection; 17 | 18 | /** 19 | * Text Protocol. 20 | */ 21 | class Text 22 | { 23 | /** 24 | * Check the integrity of the package. 25 | * 26 | * @param string $buffer 27 | * @param TcpConnection $connection 28 | * @return int 29 | */ 30 | public static function input($buffer, TcpConnection $connection) 31 | { 32 | // Judge whether the package length exceeds the limit. 33 | if (strlen($buffer) >= TcpConnection::$maxPackageSize) { 34 | $connection->close(); 35 | return 0; 36 | } 37 | // Find the position of "\n". 38 | $pos = strpos($buffer, "\n"); 39 | // No "\n", packet length is unknown, continue to wait for the data so return 0. 40 | if ($pos === false) { 41 | return 0; 42 | } 43 | // Return the current package length. 44 | return $pos + 1; 45 | } 46 | 47 | /** 48 | * Encode. 49 | * 50 | * @param string $buffer 51 | * @return string 52 | */ 53 | public static function encode($buffer) 54 | { 55 | // Add "\n" 56 | return $buffer . "\n"; 57 | } 58 | 59 | /** 60 | * Decode. 61 | * 62 | * @param string $buffer 63 | * @return string 64 | */ 65 | public static function decode($buffer) 66 | { 67 | // Remove "\n" 68 | return trim($buffer); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/README.md: -------------------------------------------------------------------------------- 1 | # Workerman 2 | [![Gitter](https://badges.gitter.im/walkor/Workerman.svg)](https://gitter.im/walkor/Workerman?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) 3 | [![Latest Stable Version](https://poser.pugx.org/workerman/workerman/v/stable)](https://packagist.org/packages/workerman/workerman) 4 | [![Total Downloads](https://poser.pugx.org/workerman/workerman/downloads)](https://packagist.org/packages/workerman/workerman) 5 | [![Monthly Downloads](https://poser.pugx.org/workerman/workerman/d/monthly)](https://packagist.org/packages/workerman/workerman) 6 | [![Daily Downloads](https://poser.pugx.org/workerman/workerman/d/daily)](https://packagist.org/packages/workerman/workerman) 7 | [![License](https://poser.pugx.org/workerman/workerman/license)](https://packagist.org/packages/workerman/workerman) 8 | 9 | ## What is it 10 | Workerman is an asynchronous event driven PHP framework with high performance for easily building fast, scalable network applications. Supports HTTP, Websocket, SSL and other custom protocols. Supports libevent, [HHVM](https://github.com/facebook/hhvm) , [ReactPHP](https://github.com/reactphp/react). 11 | 12 | ## Requires 13 | PHP 5.3 or Higher 14 | A POSIX compatible operating system (Linux, OSX, BSD) 15 | POSIX and PCNTL extensions for PHP 16 | 17 | ## Installation 18 | 19 | ``` 20 | composer require workerman/workerman 21 | ``` 22 | 23 | ## Basic Usage 24 | 25 | ### A websocket server 26 | ```php 27 | count = 4; 36 | 37 | // Emitted when new connection come 38 | $ws_worker->onConnect = function($connection) 39 | { 40 | echo "New connection\n"; 41 | }; 42 | 43 | // Emitted when data received 44 | $ws_worker->onMessage = function($connection, $data) 45 | { 46 | // Send hello $data 47 | $connection->send('hello ' . $data); 48 | }; 49 | 50 | // Emitted when connection closed 51 | $ws_worker->onClose = function($connection) 52 | { 53 | echo "Connection closed\n"; 54 | }; 55 | 56 | // Run worker 57 | Worker::runAll(); 58 | ``` 59 | 60 | ### An http server 61 | ```php 62 | require_once __DIR__ . '/vendor/autoload.php'; 63 | use Workerman\Worker; 64 | 65 | // #### http worker #### 66 | $http_worker = new Worker("http://0.0.0.0:2345"); 67 | 68 | // 4 processes 69 | $http_worker->count = 4; 70 | 71 | // Emitted when data received 72 | $http_worker->onMessage = function($connection, $data) 73 | { 74 | // $_GET, $_POST, $_COOKIE, $_SESSION, $_SERVER, $_FILES are available 75 | var_dump($_GET, $_POST, $_COOKIE, $_SESSION, $_SERVER, $_FILES); 76 | // send data to client 77 | $connection->send("hello world \n"); 78 | }; 79 | 80 | // run all workers 81 | Worker::runAll(); 82 | ``` 83 | 84 | ### A WebServer 85 | ```php 86 | require_once __DIR__ . '/vendor/autoload.php'; 87 | use Workerman\WebServer; 88 | use Workerman\Worker; 89 | 90 | // WebServer 91 | $web = new WebServer("http://0.0.0.0:80"); 92 | 93 | // 4 processes 94 | $web->count = 4; 95 | 96 | // Set the root of domains 97 | $web->addRoot('www.your_domain.com', '/your/path/Web'); 98 | $web->addRoot('www.another_domain.com', '/another/path/Web'); 99 | // run all workers 100 | Worker::runAll(); 101 | ``` 102 | 103 | ### A tcp server 104 | ```php 105 | require_once __DIR__ . '/vendor/autoload.php'; 106 | use Workerman\Worker; 107 | 108 | // #### create socket and listen 1234 port #### 109 | $tcp_worker = new Worker("tcp://0.0.0.0:1234"); 110 | 111 | // 4 processes 112 | $tcp_worker->count = 4; 113 | 114 | // Emitted when new connection come 115 | $tcp_worker->onConnect = function($connection) 116 | { 117 | echo "New Connection\n"; 118 | }; 119 | 120 | // Emitted when data received 121 | $tcp_worker->onMessage = function($connection, $data) 122 | { 123 | // send data to client 124 | $connection->send("hello $data \n"); 125 | }; 126 | 127 | // Emitted when new connection come 128 | $tcp_worker->onClose = function($connection) 129 | { 130 | echo "Connection closed\n"; 131 | }; 132 | 133 | Worker::runAll(); 134 | ``` 135 | 136 | ### Enable SSL 137 | ```php 138 | array( 145 | 'local_cert' => '/your/path/of/server.pem', 146 | 'local_pk' => '/your/path/of/server.key', 147 | 'verify_peer' => false, 148 | ) 149 | ); 150 | 151 | // Create a Websocket server with ssl context. 152 | $ws_worker = new Worker("websocket://0.0.0.0:2346", $context); 153 | 154 | // Enable SSL. WebSocket+SSL means that Secure WebSocket (wss://). 155 | // The similar approaches for Https etc. 156 | $ws_worker->transport = 'ssl'; 157 | 158 | $ws_worker->onMessage = function($connection, $data) 159 | { 160 | // Send hello $data 161 | $connection->send('hello ' . $data); 162 | }; 163 | 164 | Worker::runAll(); 165 | ``` 166 | 167 | ### Custom protocol 168 | Protocols/MyTextProtocol.php 169 | ```php 170 | namespace Protocols; 171 | /** 172 | * User defined protocol 173 | * Format Text+"\n" 174 | */ 175 | class MyTextProtocol 176 | { 177 | public static function input($recv_buffer) 178 | { 179 | // Find the position of the first occurrence of "\n" 180 | $pos = strpos($recv_buffer, "\n"); 181 | // Not a complete package. Return 0 because the length of package can not be calculated 182 | if($pos === false) 183 | { 184 | return 0; 185 | } 186 | // Return length of the package 187 | return $pos+1; 188 | } 189 | 190 | public static function decode($recv_buffer) 191 | { 192 | return trim($recv_buffer); 193 | } 194 | 195 | public static function encode($data) 196 | { 197 | return $data."\n"; 198 | } 199 | } 200 | ``` 201 | 202 | ```php 203 | require_once __DIR__ . '/vendor/autoload.php'; 204 | use Workerman\Worker; 205 | 206 | // #### MyTextProtocol worker #### 207 | $text_worker = new Worker("MyTextProtocol://0.0.0.0:5678"); 208 | 209 | $text_worker->onConnect = function($connection) 210 | { 211 | echo "New connection\n"; 212 | }; 213 | 214 | $text_worker->onMessage = function($connection, $data) 215 | { 216 | // send data to client 217 | $connection->send("hello world \n"); 218 | }; 219 | 220 | $text_worker->onClose = function($connection) 221 | { 222 | echo "Connection closed\n"; 223 | }; 224 | 225 | // run all workers 226 | Worker::runAll(); 227 | ``` 228 | 229 | ### Timer 230 | ```php 231 | require_once __DIR__ . '/vendor/autoload.php'; 232 | use Workerman\Worker; 233 | use Workerman\Lib\Timer; 234 | 235 | $task = new Worker(); 236 | $task->onWorkerStart = function($task) 237 | { 238 | // 2.5 seconds 239 | $time_interval = 2.5; 240 | $timer_id = Timer::add($time_interval, 241 | function() 242 | { 243 | echo "Timer run\n"; 244 | } 245 | ); 246 | }; 247 | 248 | // run all workers 249 | Worker::runAll(); 250 | ``` 251 | 252 | ### AsyncTcpConnection (tcp/ws/text/frame etc...) 253 | ```php 254 | require_once __DIR__ . '/vendor/autoload.php'; 255 | use Workerman\Worker; 256 | use Workerman\Connection\AsyncTcpConnection; 257 | 258 | $worker = new Worker(); 259 | $worker->onWorkerStart = function() 260 | { 261 | // Websocket protocol for client. 262 | $ws_connection = new AsyncTcpConnection("ws://echo.websocket.org:80"); 263 | $ws_connection->onConnect = function($connection){ 264 | $connection->send('hello'); 265 | }; 266 | $ws_connection->onMessage = function($connection, $data){ 267 | echo "recv: $data\n"; 268 | }; 269 | $ws_connection->onError = function($connection, $code, $msg){ 270 | echo "error: $msg\n"; 271 | }; 272 | $ws_connection->onClose = function($connection){ 273 | echo "connection closed\n"; 274 | }; 275 | $ws_connection->connect(); 276 | }; 277 | Worker::runAll(); 278 | ``` 279 | 280 | ### Async Mysql of ReactPHP 281 | ``` 282 | composer require react/mysql 283 | ``` 284 | 285 | ```php 286 | onWorkerStart = function() { 292 | global $mysql; 293 | $loop = Worker::getEventLoop(); 294 | $mysql = new React\MySQL\Connection($loop, array( 295 | 'host' => '127.0.0.1', 296 | 'dbname' => 'dbname', 297 | 'user' => 'user', 298 | 'passwd' => 'passwd', 299 | )); 300 | $mysql->on('error', function($e){ 301 | echo $e; 302 | }); 303 | $mysql->connect(function ($e) { 304 | if($e) { 305 | echo $e; 306 | } else { 307 | echo "connect success\n"; 308 | } 309 | }); 310 | }; 311 | $worker->onMessage = function($connection, $data) { 312 | global $mysql; 313 | $mysql->query('show databases' /*trim($data)*/, function ($command, $mysql) use ($connection) { 314 | if ($command->hasError()) { 315 | $error = $command->getError(); 316 | } else { 317 | $results = $command->resultRows; 318 | $fields = $command->resultFields; 319 | $connection->send(json_encode($results)); 320 | } 321 | }); 322 | }; 323 | Worker::runAll(); 324 | ``` 325 | 326 | ### Async Redis of ReactPHP 327 | ``` 328 | composer require clue/redis-react 329 | ``` 330 | 331 | ```php 332 | onWorkerStart = function() { 341 | global $factory; 342 | $loop = Worker::getEventLoop(); 343 | $factory = new Factory($loop); 344 | }; 345 | 346 | $worker->onMessage = function($connection, $data) { 347 | global $factory; 348 | $factory->createClient('localhost:6379')->then(function (Client $client) use ($connection) { 349 | $client->set('greeting', 'Hello world'); 350 | $client->append('greeting', '!'); 351 | 352 | $client->get('greeting')->then(function ($greeting) use ($connection){ 353 | // Hello world! 354 | echo $greeting . PHP_EOL; 355 | $connection->send($greeting); 356 | }); 357 | 358 | $client->incr('invocation')->then(function ($n) use ($connection){ 359 | echo 'This is invocation #' . $n . PHP_EOL; 360 | $connection->send($n); 361 | }); 362 | }); 363 | }; 364 | 365 | Worker::runAll(); 366 | ``` 367 | 368 | ### Aysnc dns of ReactPHP 369 | ``` 370 | composer require react/dns 371 | ``` 372 | 373 | ```php 374 | require_once __DIR__ . '/vendor/autoload.php'; 375 | use Workerman\Worker; 376 | $worker = new Worker('tcp://0.0.0.0:6161'); 377 | $worker->onWorkerStart = function() { 378 | global $dns; 379 | // Get event-loop. 380 | $loop = Worker::getEventLoop(); 381 | $factory = new React\Dns\Resolver\Factory(); 382 | $dns = $factory->create('8.8.8.8', $loop); 383 | }; 384 | $worker->onMessage = function($connection, $host) { 385 | global $dns; 386 | $host = trim($host); 387 | $dns->resolve($host)->then(function($ip) use($host, $connection) { 388 | $connection->send("$host: $ip"); 389 | },function($e) use($host, $connection){ 390 | $connection->send("$host: {$e->getMessage()}"); 391 | }); 392 | }; 393 | 394 | Worker::runAll(); 395 | ``` 396 | 397 | ### Http client of ReactPHP 398 | ``` 399 | composer require react/http-client 400 | ``` 401 | 402 | ```php 403 | onMessage = function($connection, $host) { 410 | $loop = Worker::getEventLoop(); 411 | $client = new \React\HttpClient\Client($loop); 412 | $request = $client->request('GET', trim($host)); 413 | $request->on('error', function(Exception $e) use ($connection) { 414 | $connection->send($e); 415 | }); 416 | $request->on('response', function ($response) use ($connection) { 417 | $response->on('data', function ($data) use ($connection) { 418 | $connection->send($data); 419 | }); 420 | }); 421 | $request->end(); 422 | }; 423 | 424 | Worker::runAll(); 425 | ``` 426 | 427 | ### ZMQ of ReactPHP 428 | ``` 429 | composer require react/zmq 430 | ``` 431 | 432 | ```php 433 | onWorkerStart = function() { 440 | global $pull; 441 | $loop = Worker::getEventLoop(); 442 | $context = new React\ZMQ\Context($loop); 443 | $pull = $context->getSocket(ZMQ::SOCKET_PULL); 444 | $pull->bind('tcp://127.0.0.1:5555'); 445 | 446 | $pull->on('error', function ($e) { 447 | var_dump($e->getMessage()); 448 | }); 449 | 450 | $pull->on('message', function ($msg) { 451 | echo "Received: $msg\n"; 452 | }); 453 | }; 454 | 455 | Worker::runAll(); 456 | ``` 457 | 458 | ### STOMP of ReactPHP 459 | ``` 460 | composer require react/stomp 461 | ``` 462 | 463 | ```php 464 | onWorkerStart = function() { 471 | global $client; 472 | $loop = Worker::getEventLoop(); 473 | $factory = new React\Stomp\Factory($loop); 474 | $client = $factory->createClient(array('vhost' => '/', 'login' => 'guest', 'passcode' => 'guest')); 475 | 476 | $client 477 | ->connect() 478 | ->then(function ($client) use ($loop) { 479 | $client->subscribe('/topic/foo', function ($frame) { 480 | echo "Message received: {$frame->body}\n"; 481 | }); 482 | }); 483 | }; 484 | 485 | Worker::runAll(); 486 | ``` 487 | 488 | 489 | 490 | ## Available commands 491 | ```php start.php start ``` 492 | ```php start.php start -d ``` 493 | ![workerman start](http://www.workerman.net/img/workerman-start.png) 494 | ```php start.php status ``` 495 | ![workerman satus](http://www.workerman.net/img/workerman-status.png?a=123) 496 | ```php start.php connections``` 497 | ```php start.php stop ``` 498 | ```php start.php restart ``` 499 | ```php start.php reload ``` 500 | 501 | ## Documentation 502 | 503 | 中文主页:[http://www.workerman.net](http://www.workerman.net) 504 | 505 | 中文文档: [http://doc.workerman.net](http://doc.workerman.net) 506 | 507 | Documentation:[https://github.com/walkor/workerman-manual](https://github.com/walkor/workerman-manual/blob/master/english/src/SUMMARY.md) 508 | 509 | # Benchmarks 510 | ``` 511 | CPU: Intel(R) Core(TM) i3-3220 CPU @ 3.30GHz and 4 processors totally 512 | Memory: 8G 513 | OS: Ubuntu 14.04 LTS 514 | Software: ab 515 | PHP: 5.5.9 516 | ``` 517 | 518 | **Codes** 519 | ```php 520 | count=3; 524 | $worker->onMessage = function($connection, $data) 525 | { 526 | $connection->send("HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nServer: workerman\r\nContent-Length: 5\r\n\r\nhello"); 527 | }; 528 | Worker::runAll(); 529 | ``` 530 | **Result** 531 | 532 | ```shell 533 | ab -n1000000 -c100 -k http://127.0.0.1:1234/ 534 | This is ApacheBench, Version 2.3 <$Revision: 1528965 $> 535 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 536 | Licensed to The Apache Software Foundation, http://www.apache.org/ 537 | 538 | Benchmarking 127.0.0.1 (be patient) 539 | Completed 100000 requests 540 | Completed 200000 requests 541 | Completed 300000 requests 542 | Completed 400000 requests 543 | Completed 500000 requests 544 | Completed 600000 requests 545 | Completed 700000 requests 546 | Completed 800000 requests 547 | Completed 900000 requests 548 | Completed 1000000 requests 549 | Finished 1000000 requests 550 | 551 | 552 | Server Software: workerman/3.1.4 553 | Server Hostname: 127.0.0.1 554 | Server Port: 1234 555 | 556 | Document Path: / 557 | Document Length: 5 bytes 558 | 559 | Concurrency Level: 100 560 | Time taken for tests: 7.240 seconds 561 | Complete requests: 1000000 562 | Failed requests: 0 563 | Keep-Alive requests: 1000000 564 | Total transferred: 73000000 bytes 565 | HTML transferred: 5000000 bytes 566 | Requests per second: 138124.14 [#/sec] (mean) 567 | Time per request: 0.724 [ms] (mean) 568 | Time per request: 0.007 [ms] (mean, across all concurrent requests) 569 | Transfer rate: 9846.74 [Kbytes/sec] received 570 | 571 | Connection Times (ms) 572 | min mean[+/-sd] median max 573 | Connect: 0 0 0.0 0 5 574 | Processing: 0 1 0.2 1 9 575 | Waiting: 0 1 0.2 1 9 576 | Total: 0 1 0.2 1 9 577 | 578 | Percentage of the requests served within a certain time (ms) 579 | 50% 1 580 | 66% 1 581 | 75% 1 582 | 80% 1 583 | 90% 1 584 | 95% 1 585 | 98% 1 586 | 99% 1 587 | 100% 9 (longest request) 588 | 589 | ``` 590 | 591 | 592 | ## Other links with workerman 593 | 594 | [PHPSocket.IO](https://github.com/walkor/phpsocket.io) 595 | [php-socks5](https://github.com/walkor/php-socks5) 596 | [php-http-proxy](https://github.com/walkor/php-http-proxy) 597 | 598 | ## Donate 599 | 600 | 601 | ## LICENSE 602 | 603 | Workerman is released under the [MIT license](https://github.com/walkor/workerman/blob/master/MIT-LICENSE.txt). 604 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/WebServer.php: -------------------------------------------------------------------------------- 1 | 10 | * @copyright walkor 11 | * @link http://www.workerman.net/ 12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 13 | */ 14 | namespace Workerman; 15 | 16 | use Workerman\Protocols\Http; 17 | use Workerman\Protocols\HttpCache; 18 | 19 | /** 20 | * WebServer. 21 | */ 22 | class WebServer extends Worker 23 | { 24 | /** 25 | * Virtual host to path mapping. 26 | * 27 | * @var array ['workerman.net'=>'/home', 'www.workerman.net'=>'home/www'] 28 | */ 29 | protected $serverRoot = array(); 30 | 31 | /** 32 | * Mime mapping. 33 | * 34 | * @var array 35 | */ 36 | protected static $mimeTypeMap = array(); 37 | 38 | 39 | /** 40 | * Used to save user OnWorkerStart callback settings. 41 | * 42 | * @var callback 43 | */ 44 | protected $_onWorkerStart = null; 45 | 46 | /** 47 | * Add virtual host. 48 | * 49 | * @param string $domain 50 | * @param string $config 51 | * @return void 52 | */ 53 | public function addRoot($domain, $config) 54 | { 55 | if (is_string($config)) { 56 | $config = array('root' => $config); 57 | } 58 | $this->serverRoot[$domain] = $config; 59 | } 60 | 61 | /** 62 | * Construct. 63 | * 64 | * @param string $socket_name 65 | * @param array $context_option 66 | */ 67 | public function __construct($socket_name, $context_option = array()) 68 | { 69 | list(, $address) = explode(':', $socket_name, 2); 70 | parent::__construct('http:' . $address, $context_option); 71 | $this->name = 'WebServer'; 72 | } 73 | 74 | /** 75 | * Run webserver instance. 76 | * 77 | * @see Workerman.Worker::run() 78 | */ 79 | public function run() 80 | { 81 | $this->_onWorkerStart = $this->onWorkerStart; 82 | $this->onWorkerStart = array($this, 'onWorkerStart'); 83 | $this->onMessage = array($this, 'onMessage'); 84 | parent::run(); 85 | } 86 | 87 | /** 88 | * Emit when process start. 89 | * 90 | * @throws \Exception 91 | */ 92 | public function onWorkerStart() 93 | { 94 | if (empty($this->serverRoot)) { 95 | Worker::safeEcho(new \Exception('server root not set, please use WebServer::addRoot($domain, $root_path) to set server root path')); 96 | exit(250); 97 | } 98 | 99 | // Init mimeMap. 100 | $this->initMimeTypeMap(); 101 | 102 | // Try to emit onWorkerStart callback. 103 | if ($this->_onWorkerStart) { 104 | try { 105 | call_user_func($this->_onWorkerStart, $this); 106 | } catch (\Exception $e) { 107 | self::log($e); 108 | exit(250); 109 | } catch (\Error $e) { 110 | self::log($e); 111 | exit(250); 112 | } 113 | } 114 | } 115 | 116 | /** 117 | * Init mime map. 118 | * 119 | * @return void 120 | */ 121 | public function initMimeTypeMap() 122 | { 123 | $mime_file = Http::getMimeTypesFile(); 124 | if (!is_file($mime_file)) { 125 | $this->log("$mime_file mime.type file not fond"); 126 | return; 127 | } 128 | $items = file($mime_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 129 | if (!is_array($items)) { 130 | $this->log("get $mime_file mime.type content fail"); 131 | return; 132 | } 133 | foreach ($items as $content) { 134 | if (preg_match("/\s*(\S+)\s+(\S.+)/", $content, $match)) { 135 | $mime_type = $match[1]; 136 | $workerman_file_extension_var = $match[2]; 137 | $workerman_file_extension_array = explode(' ', substr($workerman_file_extension_var, 0, -1)); 138 | foreach ($workerman_file_extension_array as $workerman_file_extension) { 139 | self::$mimeTypeMap[$workerman_file_extension] = $mime_type; 140 | } 141 | } 142 | } 143 | } 144 | 145 | /** 146 | * Emit when http message coming. 147 | * 148 | * @param Connection\TcpConnection $connection 149 | * @return void 150 | */ 151 | public function onMessage($connection) 152 | { 153 | // REQUEST_URI. 154 | $workerman_url_info = parse_url($_SERVER['REQUEST_URI']); 155 | if (!$workerman_url_info) { 156 | Http::header('HTTP/1.1 400 Bad Request'); 157 | $connection->close('

400 Bad Request

'); 158 | return; 159 | } 160 | 161 | $workerman_path = isset($workerman_url_info['path']) ? $workerman_url_info['path'] : '/'; 162 | 163 | $workerman_path_info = pathinfo($workerman_path); 164 | $workerman_file_extension = isset($workerman_path_info['extension']) ? $workerman_path_info['extension'] : ''; 165 | if ($workerman_file_extension === '') { 166 | $workerman_path = ($len = strlen($workerman_path)) && $workerman_path[$len - 1] === '/' ? $workerman_path . 'index.php' : $workerman_path . '/index.php'; 167 | $workerman_file_extension = 'php'; 168 | } 169 | 170 | $workerman_siteConfig = isset($this->serverRoot[$_SERVER['SERVER_NAME']]) ? $this->serverRoot[$_SERVER['SERVER_NAME']] : current($this->serverRoot); 171 | $workerman_root_dir = $workerman_siteConfig['root']; 172 | $workerman_file = "$workerman_root_dir/$workerman_path"; 173 | if(isset($workerman_siteConfig['additionHeader'])){ 174 | Http::header($workerman_siteConfig['additionHeader']); 175 | } 176 | if ($workerman_file_extension === 'php' && !is_file($workerman_file)) { 177 | $workerman_file = "$workerman_root_dir/index.php"; 178 | if (!is_file($workerman_file)) { 179 | $workerman_file = "$workerman_root_dir/index.html"; 180 | $workerman_file_extension = 'html'; 181 | } 182 | } 183 | 184 | // File exsits. 185 | if (is_file($workerman_file)) { 186 | // Security check. 187 | if ((!($workerman_request_realpath = realpath($workerman_file)) || !($workerman_root_dir_realpath = realpath($workerman_root_dir))) || 0 !== strpos($workerman_request_realpath, 188 | $workerman_root_dir_realpath) 189 | ) { 190 | Http::header('HTTP/1.1 400 Bad Request'); 191 | $connection->close('

400 Bad Request

'); 192 | return; 193 | } 194 | 195 | $workerman_file = realpath($workerman_file); 196 | 197 | // Request php file. 198 | if ($workerman_file_extension === 'php') { 199 | $workerman_cwd = getcwd(); 200 | chdir($workerman_root_dir); 201 | ini_set('display_errors', 'off'); 202 | ob_start(); 203 | // Try to include php file. 204 | try { 205 | // $_SERVER. 206 | $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp(); 207 | $_SERVER['REMOTE_PORT'] = $connection->getRemotePort(); 208 | include $workerman_file; 209 | } catch (\Exception $e) { 210 | // Jump_exit? 211 | if ($e->getMessage() != 'jump_exit') { 212 | Worker::safeEcho($e); 213 | } 214 | } 215 | $content = ob_get_clean(); 216 | ini_set('display_errors', 'on'); 217 | if (strtolower($_SERVER['HTTP_CONNECTION']) === "keep-alive") { 218 | $connection->send($content); 219 | } else { 220 | $connection->close($content); 221 | } 222 | chdir($workerman_cwd); 223 | return; 224 | } 225 | 226 | // Send file to client. 227 | return self::sendFile($connection, $workerman_file); 228 | } else { 229 | // 404 230 | Http::header("HTTP/1.1 404 Not Found"); 231 | if(isset($workerman_siteConfig['custom404']) && file_exists($workerman_siteConfig['custom404'])){ 232 | $html404 = file_get_contents($workerman_siteConfig['custom404']); 233 | }else{ 234 | $html404 = '404 File not found

404 Not Found

'; 235 | } 236 | $connection->close($html404); 237 | return; 238 | } 239 | } 240 | 241 | public static function sendFile($connection, $file_path) 242 | { 243 | // Check 304. 244 | $info = stat($file_path); 245 | $modified_time = $info ? date('D, d M Y H:i:s', $info['mtime']) . ' ' . date_default_timezone_get() : ''; 246 | if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $info) { 247 | // Http 304. 248 | if ($modified_time === $_SERVER['HTTP_IF_MODIFIED_SINCE']) { 249 | // 304 250 | Http::header('HTTP/1.1 304 Not Modified'); 251 | // Send nothing but http headers.. 252 | $connection->close(''); 253 | return; 254 | } 255 | } 256 | 257 | // Http header. 258 | if ($modified_time) { 259 | $modified_time = "Last-Modified: $modified_time\r\n"; 260 | } 261 | $file_size = filesize($file_path); 262 | $file_info = pathinfo($file_path); 263 | $extension = isset($file_info['extension']) ? $file_info['extension'] : ''; 264 | $file_name = isset($file_info['filename']) ? $file_info['filename'] : ''; 265 | $header = "HTTP/1.1 200 OK\r\n"; 266 | if (isset(self::$mimeTypeMap[$extension])) { 267 | $header .= "Content-Type: " . self::$mimeTypeMap[$extension] . "\r\n"; 268 | } else { 269 | $header .= "Content-Type: application/octet-stream\r\n"; 270 | $header .= "Content-Disposition: attachment; filename=\"$file_name\"\r\n"; 271 | } 272 | $header .= "Connection: keep-alive\r\n"; 273 | $header .= $modified_time; 274 | $header .= "Content-Length: $file_size\r\n\r\n"; 275 | $trunk_limit_size = 1024*1024; 276 | if ($file_size < $trunk_limit_size) { 277 | return $connection->send($header.file_get_contents($file_path), true); 278 | } 279 | $connection->send($header, true); 280 | 281 | // Read file content from disk piece by piece and send to client. 282 | $connection->fileHandler = fopen($file_path, 'r'); 283 | $do_write = function()use($connection) 284 | { 285 | // Send buffer not full. 286 | while(empty($connection->bufferFull)) 287 | { 288 | // Read from disk. 289 | $buffer = fread($connection->fileHandler, 8192); 290 | // Read eof. 291 | if($buffer === '' || $buffer === false) 292 | { 293 | return; 294 | } 295 | $connection->send($buffer, true); 296 | } 297 | }; 298 | // Send buffer full. 299 | $connection->onBufferFull = function($connection) 300 | { 301 | $connection->bufferFull = true; 302 | }; 303 | // Send buffer drain. 304 | $connection->onBufferDrain = function($connection)use($do_write) 305 | { 306 | $connection->bufferFull = false; 307 | $do_write(); 308 | }; 309 | $do_write(); 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /vendor/workerman/workerman/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workerman/workerman", 3 | "type": "library", 4 | "keywords": [ 5 | "event-loop", 6 | "asynchronous" 7 | ], 8 | "homepage": "http://www.workerman.net", 9 | "license": "MIT", 10 | "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.", 11 | "authors": [ 12 | { 13 | "name": "walkor", 14 | "email": "walkor@workerman.net", 15 | "homepage": "http://www.workerman.net", 16 | "role": "Developer" 17 | } 18 | ], 19 | "support": { 20 | "email": "walkor@workerman.net", 21 | "issues": "https://github.com/walkor/workerman/issues", 22 | "forum": "http://wenda.workerman.net/", 23 | "wiki": "http://doc3.workerman.net/index.html", 24 | "source": "https://github.com/walkor/workerman" 25 | }, 26 | "require": { 27 | "php": ">=5.3" 28 | }, 29 | "suggest": { 30 | "ext-event": "For better performance. " 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "Workerman\\": "./" 35 | } 36 | }, 37 | "minimum-stability": "dev" 38 | } 39 | --------------------------------------------------------------------------------