├── .gitignore ├── App.php ├── Libs ├── Maccess.php ├── Mdb.php ├── Mmysqli.php ├── Mredis.php └── StatisticClient.php ├── README.md └── composer.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | demo.php 3 | composer.json 4 | composer.lock 5 | vendor 6 | -------------------------------------------------------------------------------- /App.php: -------------------------------------------------------------------------------- 1 | map[] = array($url,$callback,1); 60 | } 61 | 62 | public function AddFunc($url,callable $callback){ 63 | if ( $url != "/" ){ 64 | $url = strtolower(trim($url,"/")); 65 | } 66 | if ( is_callable($callback) ){ 67 | if ( $callback instanceof \Closure ){ 68 | $callback = \Closure::bind($callback, $this, get_class()); 69 | } 70 | }else{ 71 | throw new \Exception('can not HandleFunc'); 72 | } 73 | $this->map[] = array($url,$callback,2); 74 | } 75 | 76 | private function show_404($connection){ 77 | if ( $this->on404 ){ 78 | $callback = \Closure::bind($this->on404, $this, get_class()); 79 | call_user_func($callback); 80 | }else{ 81 | Http::header("HTTP/1.1 404 Not Found"); 82 | $html = ' 83 | 404 Not Found 84 | 85 |

404 Not Found

86 |
Workerman
87 | 88 | '; 89 | $connection->send($html); 90 | } 91 | } 92 | 93 | private function auto_close($conn){ 94 | if ( strtolower($_SERVER["SERVER_PROTOCOL"]) == "http/1.1" ){ 95 | if ( isset($_SERVER["HTTP_CONNECTION"]) ){ 96 | if ( strtolower($_SERVER["HTTP_CONNECTION"]) == "close" ){ 97 | if ( $this->conn_close ){ 98 | $conn->close(); 99 | } 100 | } 101 | } 102 | }else{ 103 | if ( $_SERVER["HTTP_CONNECTION"] == "keep-alive" ){ 104 | 105 | }else{ 106 | if ( $this->conn_close ){ 107 | $conn->close(); 108 | } 109 | } 110 | } 111 | $this->access_log[7] = round(microtime_float() - $this->access_log[7],4); 112 | echo implode(" - ",$this->access_log)."\n"; 113 | } 114 | 115 | public function onClientMessage($connection,$data){ 116 | $this->access_log[0] = $_SERVER["REMOTE_ADDR"]; 117 | $this->access_log[1] = date("Y-m-d H:i:s"); 118 | $this->access_log[2] = $_SERVER['REQUEST_METHOD']; 119 | $this->access_log[3] = $_SERVER['REQUEST_URI']; 120 | $this->access_log[4] = $_SERVER['SERVER_PROTOCOL']; 121 | $this->access_log[5] = "NULL"; 122 | $this->access_log[6] = 200; 123 | $this->access_log[7] = microtime_float(); 124 | if ( empty($this->map) ){ 125 | $str = <<<'EOD' 126 |
基于Workerman实现的自带http server的web开发框架.没有添加路由,请添加路由! 127 |
$app->HandleFunc("/",function($conn,$data) use($app){
128 |     $conn->send("默认页");
129 | });
130 |
131 | EOD; 132 | $connection->send($str); 133 | return; 134 | } 135 | if ( $this->statistic_server ){ 136 | require_once __DIR__ . '/Libs/StatisticClient.php'; 137 | $statistic_address = $this->statistic_server; 138 | } 139 | $this->conn = $connection; 140 | $url= $_SERVER["REQUEST_URI"]; 141 | $pos = stripos($url,"?"); 142 | if ($pos != false) { 143 | $url = substr($url,0,$pos); 144 | } 145 | if ( $url != "/"){ 146 | $url = strtolower(trim($url,"/")); 147 | } 148 | $url_arr = explode("/",$url); 149 | $class = empty($url_arr[0]) ? "_default" : $url_arr[0]; 150 | $method = empty($url_arr[1]) ? "_default" : $url_arr[1]; 151 | if ( $this->statistic_server ){ 152 | StatisticClient::tick($class, $method); 153 | } 154 | $success = false; 155 | foreach($this->map as $route){ 156 | if ( $route[2] == 1){//正常路由 157 | if ( $route[0] == $url ){ 158 | $callback[] = $route[1]; 159 | $success = true; 160 | } 161 | }else if ( $route[2] == 2 ){//中间件 162 | if ( $route[0] == "/" ){ 163 | $callback[] = $route[1]; 164 | }else if ( stripos($url,$route[0]) === 0 ){ 165 | $callback[] = $route[1]; 166 | } 167 | } 168 | } 169 | if ( isset($callback) && $success ){ 170 | try { 171 | foreach($callback as $cl){ 172 | if ( call_user_func($cl) === true){ 173 | break; 174 | } 175 | } 176 | if ( $this->statistic_server ){ 177 | StatisticClient::report($class, $method, 1, 0, '', $statistic_address); 178 | } 179 | }catch (\Exception $e) { 180 | // Jump_exit? 181 | if ($e->getMessage() != 'jump_exit') { 182 | $this->access_log[5] = $e; 183 | } 184 | $code = $e->getCode() ? $e->getCode() : 500; 185 | if ( $this->statistic_server ){ 186 | StatisticClient::report($class, $method, $success, $code, $e, $statistic_address); 187 | } 188 | $this->access_log[6] = 500; 189 | } 190 | }else{ 191 | $this->show_404($connection); 192 | $code = 404; 193 | $msg = "class $class not found"; 194 | if ( $this->statistic_server ){ 195 | StatisticClient::report($class, $method, $success, $code, $msg, $statistic_address); 196 | } 197 | } 198 | $this->auto_close($connection); 199 | 200 | // 已经处理请求数 201 | static $request_count = 0; 202 | // 如果请求数达到1000 203 | if( ++$request_count >= $this->max_request && $this->max_request > 0 ){ 204 | Worker::stopAll(); 205 | } 206 | 207 | 208 | } 209 | 210 | 211 | 212 | // 添加默认参数 json_encode的JSON_UNESCAPED_UNICODE参数 使之支持中文 不转义 213 | public function ServerJson($data,$option=JSON_UNESCAPED_UNICODE){ 214 | Http::header("Content-type: application/json"); 215 | //传入0 不做处理 216 | if ($option==0){ 217 | return $this->conn->send(json_encode($data)); 218 | }else{ 219 | 220 | if ($option==JSON_UNESCAPED_UNICODE) { 221 | // json_encode的JSON_UNESCAPED_UNICODE参数 PHP版本必须是5.4以上 222 | if (version_compare(PHP_VERSION,'5.4.0','>=')) { 223 | return $this->conn->send(json_encode($data,$option)); 224 | }else{ 225 | // PHP版本小于5.3的支持 未经测试 226 | // 实现方法一 如下 实现方法二 可以使用urldecode函数 227 | // $str = json_encode($data); 228 | // $str = preg_replace_callback("#\\\u([0-9a-f]{4})#i",function($matchs){ 229 | // iconv('UCS-2BE', 'UTF-8', pack('H4', $matchs[1])); 230 | // },$str); 231 | // return $this->conn->send($str); 232 | return $this->conn->send(json_encode($data)); 233 | } 234 | } 235 | 236 | // 如果传入了别的参数 237 | return $this->conn->send(json_encode($data),$option); 238 | } 239 | } 240 | 241 | 242 | public function ServerHtml($data){ 243 | $this->conn->send($data); 244 | } 245 | 246 | public function Header($str){ 247 | Http::header($str); 248 | } 249 | 250 | public function Setcookie($name,$value = '',$maxage = 0,$path = '',$domain = '',$secure = false,$HTTPOnly = false){ 251 | Http::setcookie($name,$value,$maxage,$path,$domain,$secure,$HTTPOnly); 252 | } 253 | 254 | public function run() 255 | { 256 | $this->reusePort = true; 257 | $this->onWorkerStart = $this->onAppStart; 258 | $this->onMessage = array($this, 'onClientMessage'); 259 | parent::run(); 260 | } 261 | 262 | } 263 | 264 | function autoload_dir($dir_arr){ 265 | extract($GLOBALS); 266 | foreach($dir_arr as $dir ){ 267 | foreach(glob($dir.'*.php') as $start_file) 268 | { 269 | require_once $start_file; 270 | } 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /Libs/Maccess.php: -------------------------------------------------------------------------------- 1 | report_mode = MYSQLI_REPORT_STRICT|MYSQLI_REPORT_ERROR; 6 | 7 | class Mmysqli extends \mysqli{ 8 | 9 | private $config = array(); 10 | 11 | public function __construct($config=array()){ 12 | $this->config = $config; 13 | $this->connect_db(); 14 | } 15 | 16 | private function connect_db(){ 17 | $host = isset($this->config["host"]) ? $this->config["host"] : "127.0.0.1"; 18 | $user = isset($this->config["user"]) ? $this->config["user"] : "root"; 19 | $password = isset($this->config["password"]) ? $this->config["password"] : "123456"; 20 | $db = isset($this->config["db"]) ? $this->config["db"] : "test"; 21 | $port = isset($this->config["port"]) ? $this->config["port"] : 3306; 22 | $charset = isset($this->config["charset"]) ? $this->config["charset"] : "utf8"; 23 | try { 24 | parent::__construct($host,$user,$password,$db,$port); 25 | if ( $this->connect_error ) { 26 | echo ("connect error " . $this->connect_errno ."\r\n"); 27 | return false; 28 | } 29 | if ( !$this->set_charset($charset) ) { 30 | echo ("Error loading character set $charset".$this->error."\r\n"); 31 | return false; 32 | } 33 | }catch (\Exception $e) { 34 | echo ($e); 35 | } catch (\Error $e) { 36 | echo ($e); 37 | } 38 | return true; 39 | 40 | } 41 | 42 | public function reconnect(){ 43 | if ( !$this->ping() ){ 44 | $this->close(); 45 | return $this->connect_db(); 46 | } 47 | return true; 48 | } 49 | 50 | public function query($query,$param=''){ 51 | try { 52 | return new Mmysqli_stmt($this, $query,$param); 53 | } catch (\Exception $e) { 54 | echo "query-exception-".$e->getCode()."--".$e->getMessage()."\r\n"; 55 | if ( in_array($e->getCode(),array(2006,2014))) { 56 | $this->close(); 57 | $this->connect_db(); 58 | return new Mmysqli_stmt($this, $query,$param); 59 | } 60 | } 61 | return false; 62 | } 63 | 64 | public function get($table,$start='',$limit=''){ 65 | if ( strlen($limit) && strlen($start) ){ 66 | $sql = "select * from $table limit $start,$limit"; 67 | }else{ 68 | $sql = "select * from $table"; 69 | } 70 | return $this->query($sql); 71 | } 72 | 73 | public function get_where($table,$where = array(),$start='',$limit=''){ 74 | $sql = "select * from $table "; 75 | if ( $where ){ 76 | $num = count($where)-1; 77 | $i = 0; 78 | foreach($where as $k=>$v){ 79 | $type = gettype($v); 80 | if ( $type == "string" ){ 81 | $v = "'".$this->real_escape_string($v)."'"; 82 | }else{ 83 | $v = $this->real_escape_string($v); 84 | } 85 | if ( $i == 0 && $num == 0 ) { 86 | $sql .= " where $k = $v"; 87 | }else if ( $i == 0 ){ 88 | $sql .= " where $k = $v and "; 89 | }else if ( $i == $num ){ 90 | $sql .= " where $k = $v "; 91 | }else{ 92 | $sql .= " $k = $v and"; 93 | } 94 | $i++; 95 | } 96 | } 97 | if (strlen($limit) && strlen($start) ){ 98 | $sql .= " limit $start,$limit"; 99 | } 100 | return $this->query($sql); 101 | } 102 | 103 | public function insert($table,$data){ 104 | $keys = array_keys($data); 105 | $keys_str = implode(",",$keys); 106 | $sql = "insert into $table($keys_str) values("; 107 | $num = count($data)-1; 108 | $i = 0; 109 | foreach($data as $k=>$v){ 110 | $type = gettype($v); 111 | if ( $type == "string" ){ 112 | $v = "'".$this->real_escape_string($v)."'"; 113 | }else{ 114 | $v = $this->real_escape_string($v); 115 | } 116 | if ( $i == 0 && $num == 0 ) { 117 | $sql .= "$v"; 118 | }else if ( $i == 0 ){ 119 | $sql .= "$v,"; 120 | }else if ( $i == $num ){ 121 | $sql .= "$v"; 122 | }else{ 123 | $sql .= "$v,"; 124 | } 125 | $i++; 126 | } 127 | $sql .= ")"; 128 | $stmt = $this->query($sql); 129 | return $stmt->insert_id; 130 | } 131 | 132 | public function update($table,$data,$where){ 133 | $keys = array_keys($data); 134 | $keys_str = implode(",",$keys); 135 | $sql = "update $table set "; 136 | $num = count($data)-1; 137 | $i = 0; 138 | foreach($data as $k=>$v){ 139 | $type = gettype($v); 140 | if ( $type == "string" ){ 141 | $v = "'".$this->real_escape_string($v)."'"; 142 | }else{ 143 | $v = $this->real_escape_string($v); 144 | } 145 | if ( $i == 0 && $num == 0 ) { 146 | $sql .= " $k = $v"; 147 | }else if ( $i == 0 ){ 148 | $sql .= "$k = $v,"; 149 | }else if ( $i == $num ){ 150 | $sql .= " $k = $v"; 151 | }else{ 152 | $sql .= "$k = $v,"; 153 | } 154 | $i++; 155 | } 156 | $sql .= " where "; 157 | $num = count($where)-1; 158 | $i = 0; 159 | foreach($where as $k=>$v){ 160 | $type = gettype($v); 161 | if ( $type == "string" ){ 162 | $v = "'".$this->real_escape_string($v)."'"; 163 | }else{ 164 | $v = $this->real_escape_string($v); 165 | } 166 | if ( $i == 0 && $num == 0 ) { 167 | $sql .= " $k = $v"; 168 | }else if ( $i == 0 ){ 169 | $sql .= "$k = $v and "; 170 | }else if ( $i == $num ){ 171 | $sql .= " $k = $v"; 172 | }else{ 173 | $sql .= "$k = $v and "; 174 | } 175 | $i++; 176 | } 177 | $stmt = $this->query($sql); 178 | return $stmt->affected_rows; 179 | } 180 | 181 | public function delete($table,$where){ 182 | $sql = "delete from $table "; 183 | $sql .= " where "; 184 | $num = count($where)-1; 185 | $i = 0; 186 | foreach($where as $k=>$v){ 187 | $type = gettype($v); 188 | if ( $type == "string" ){ 189 | $v = "'".$this->real_escape_string($v)."'"; 190 | }else{ 191 | $v = $this->real_escape_string($v); 192 | } 193 | if ( $i == 0 && $num == 0 ) { 194 | $sql .= " $k = $v"; 195 | }else if ( $i == 0 ){ 196 | $sql .= "$k = $v and "; 197 | }else if ( $i == $num ){ 198 | $sql .= " $k = $v"; 199 | }else{ 200 | $sql .= "$k = $v and "; 201 | } 202 | $i++; 203 | } 204 | $stmt = $this->query($sql); 205 | return $stmt->affected_rows; 206 | } 207 | 208 | } 209 | 210 | 211 | class Mmysqli_stmt extends \MySQLi_STMT{ 212 | 213 | private $mbind_types = array(); 214 | private $mbind_params = array(); 215 | 216 | public function __construct($link,$query,$param) { 217 | $this->mbind_reset(); 218 | parent::__construct($link, $query); 219 | if ( $param ){ 220 | if ( is_array($param)){ 221 | foreach($param as $p){ 222 | $type = gettype($p); 223 | if ( $type == "string" ){ 224 | $this->mbind_param('s',$p); 225 | }else if ( $type == "integer" ) { 226 | $this->mbind_param('i',$p); 227 | }else if ( $type == "double" ){ 228 | $this->mbind_param('d',$p); 229 | }else { 230 | $this->mbind_param('s',$p); 231 | } 232 | } 233 | }else{ 234 | $type = gettype($param); 235 | if ( $type == "string" ){ 236 | $this->mbind_param('s',$param); 237 | }else if ( $type == "integer" ) { 238 | $this->mbind_param('i',$param); 239 | }else if ( $type == "double" ){ 240 | $this->mbind_param('d',$param); 241 | }else { 242 | $this->mbind_param('s',$param); 243 | } 244 | } 245 | } 246 | $this->execute(); 247 | } 248 | 249 | private function mbind_reset() { 250 | unset($this->mbind_params); 251 | unset($this->mbind_types); 252 | $this->mbind_params = array(); 253 | $this->mbind_types = array(); 254 | } 255 | 256 | private function mbind_param($type, $param) { 257 | @$this->mbind_types[0].= $type; 258 | $this->mbind_params[] = $param; 259 | } 260 | 261 | private function mbind_value($type, $param) { 262 | $this->mbind_types[0].= $type; 263 | $this->mbind_params[] = $param; 264 | } 265 | 266 | private function mbind_param_do() { 267 | $params = array_merge($this->mbind_types, $this->mbind_params); 268 | return call_user_func_array(array($this, 'bind_param'), $this->makeValuesReferenced($params)); 269 | } 270 | 271 | private function makeValuesReferenced($arr){ 272 | $refs = array(); 273 | foreach($arr as $key => $value) 274 | $refs[$key] = &$arr[$key]; 275 | return $refs; 276 | 277 | } 278 | 279 | public function execute() { 280 | if(count($this->mbind_params)){ 281 | $this->mbind_param_do(); 282 | } 283 | return parent::execute(); 284 | } 285 | 286 | public function result(){ 287 | $res = $this->get_result(); 288 | $result = array(); 289 | while ($obj = $res->fetch_object() ) { 290 | $result[]= $obj; 291 | } 292 | return $result; 293 | } 294 | 295 | public function result_array($resulttype = MYSQLI_ASSOC){ 296 | $res = $this->get_result(); 297 | return $res->fetch_all($resulttype) ; 298 | } 299 | 300 | public function row(){ 301 | $res = $this->get_result(); 302 | return $res->fetch_object(); 303 | } 304 | 305 | public function row_array($resulttype = MYSQLI_ASSOC){ 306 | $res = $this->get_result(); 307 | return $res->fetch_array($resulttype); 308 | } 309 | 310 | 311 | } 312 | -------------------------------------------------------------------------------- /Libs/Mredis.php: -------------------------------------------------------------------------------- 1 | connect($host,$port); 22 | $password = isset($config["password"]) ? $config["password"] : ""; 23 | if ( $password ){ 24 | self::$_instance[$key]->auth($password); 25 | } 26 | $db = isset($config["db"]) ? $config["db"] : 0; 27 | if ( !self::$_instance[$key]->select($db) ){ 28 | echo "redis can't connect\r\n"; 29 | } 30 | }catch (\Exception $e) { 31 | echo $e; 32 | } catch (\Error $e) { 33 | echo $e; 34 | } 35 | } 36 | return self::$_instance[$key]; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Libs/StatisticClient.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 | * @author workerman.net 17 | */ 18 | 19 | namespace WebWorker\Libs; 20 | 21 | class StatisticClient 22 | { 23 | /** 24 | * [module=>[interface=>time_start, interface=>time_start ...], module=>[interface=>time_start ..], ... ] 25 | * @var array 26 | */ 27 | protected static $timeMap = array(); 28 | 29 | /** 30 | * 模块接口上报消耗时间记时 31 | * @param string $module 32 | * @param string $interface 33 | * @return void 34 | */ 35 | public static function tick($module = '', $interface = '') 36 | { 37 | return self::$timeMap[$module][$interface] = microtime(true); 38 | } 39 | 40 | /** 41 | * 上报统计数据 42 | * @param string $module 43 | * @param string $interface 44 | * @param bool $success 45 | * @param int $code 46 | * @param string $msg 47 | * @param string $report_address 48 | * @return boolean 49 | */ 50 | public static function report($module, $interface, $success, $code, $msg, $report_address = '') 51 | { 52 | $report_address = $report_address ? $report_address : 'udp://127.0.0.1:55656'; 53 | if(isset(self::$timeMap[$module][$interface]) && self::$timeMap[$module][$interface] > 0) 54 | { 55 | $time_start = self::$timeMap[$module][$interface]; 56 | self::$timeMap[$module][$interface] = 0; 57 | } 58 | else if(isset(self::$timeMap['']['']) && self::$timeMap[''][''] > 0) 59 | { 60 | $time_start = self::$timeMap['']['']; 61 | self::$timeMap[''][''] = 0; 62 | } 63 | else 64 | { 65 | $time_start = microtime(true); 66 | } 67 | 68 | $cost_time = microtime(true) - $time_start; 69 | 70 | $bin_data = StatisticProtocol::encode($module, $interface, $cost_time, $success, $code, $msg); 71 | 72 | return self::sendData($report_address, $bin_data); 73 | } 74 | 75 | /** 76 | * 发送数据给统计系统 77 | * @param string $address 78 | * @param string $buffer 79 | * @return boolean 80 | */ 81 | public static function sendData($address, $buffer) 82 | { 83 | $socket = stream_socket_client($address); 84 | if(!$socket) 85 | { 86 | return false; 87 | } 88 | return stream_socket_sendto($socket, $buffer) == strlen($buffer); 89 | } 90 | 91 | } 92 | /** 93 | * 94 | * struct statisticPortocol 95 | * { 96 | * unsigned char module_name_len; 97 | * unsigned char interface_name_len; 98 | * float cost_time; 99 | * unsigned char success; 100 | * int code; 101 | * unsigned short msg_len; 102 | * unsigned int time; 103 | * char[module_name_len] module_name; 104 | * char[interface_name_len] interface_name; 105 | * char[msg_len] msg; 106 | * } 107 | * 108 | * @author workerman.net 109 | */ 110 | class StatisticProtocol 111 | { 112 | /** 113 | * 包头长度 114 | * @var integer 115 | */ 116 | const PACKAGE_FIXED_LENGTH = 17; 117 | /** 118 | * udp 包最大长度 119 | * @var integer 120 | */ 121 | const MAX_UDP_PACKGE_SIZE = 65507; 122 | /** 123 | * char类型能保存的最大数值 124 | * @var integer 125 | */ 126 | const MAX_CHAR_VALUE = 255; 127 | /** 128 | * usigned short 能保存的最大数值 129 | * @var integer 130 | */ 131 | const MAX_UNSIGNED_SHORT_VALUE = 65535; 132 | /** 133 | * 编码 134 | * @param string $module 135 | * @param string $interface 136 | * @param float $cost_time 137 | * @param int $success 138 | * @param int $code 139 | * @param string $msg 140 | * @return string 141 | */ 142 | public static function encode($module, $interface , $cost_time, $success, $code = 0,$msg = '') 143 | { 144 | // 防止模块名过长 145 | if(strlen($module) > self::MAX_CHAR_VALUE) 146 | { 147 | $module = substr($module, 0, self::MAX_CHAR_VALUE); 148 | } 149 | // 防止接口名过长 150 | if(strlen($interface) > self::MAX_CHAR_VALUE) 151 | { 152 | $interface = substr($interface, 0, self::MAX_CHAR_VALUE); 153 | } 154 | // 防止msg过长 155 | $module_name_length = strlen($module); 156 | $interface_name_length = strlen($interface); 157 | $avalible_size = self::MAX_UDP_PACKGE_SIZE - self::PACKAGE_FIXED_LENGTH - $module_name_length - $interface_name_length; 158 | if(strlen($msg) > $avalible_size) 159 | { 160 | $msg = substr($msg, 0, $avalible_size); 161 | } 162 | // 打包 163 | return pack('CCfCNnN', $module_name_length, $interface_name_length, $cost_time, $success ? 1 : 0, $code, strlen($msg), time()).$module.$interface.$msg; 164 | } 165 | 166 | /** 167 | * 解包 168 | * @param string $bin_data 169 | * @return array 170 | */ 171 | public static function decode($bin_data) 172 | { 173 | // 解包 174 | $data = unpack("Cmodule_name_len/Cinterface_name_len/fcost_time/Csuccess/Ncode/nmsg_len/Ntime", $bin_data); 175 | $module = substr($bin_data, self::PACKAGE_FIXED_LENGTH, $data['module_name_len']); 176 | $interface = substr($bin_data, self::PACKAGE_FIXED_LENGTH + $data['module_name_len'], $data['interface_name_len']); 177 | $msg = substr($bin_data, self::PACKAGE_FIXED_LENGTH + $data['module_name_len'] + $data['interface_name_len']); 178 | return array( 179 | 'module' => $module, 180 | 'interface' => $interface, 181 | 'cost_time' => $data['cost_time'], 182 | 'success' => $data['success'], 183 | 'time' => $data['time'], 184 | 'code' => $data['code'], 185 | 'msg' => $msg, 186 | ); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WebWorker 2 | ======== 3 | 4 | 基于Workerman实现的自带http server的web开发框架,用于开发高性能的api应用,例如app接口服务端等。 5 | 6 | 详细文档见 http://doc.webworker.xtgxiso.com/ 7 | 问答互动见 http://ask.webworker.xtgxiso.com/ . 8 | 9 | 特性 10 | ======== 11 | * 仅只支持php7 12 | * 天生继承workerman所拥有的特性 13 | * 只实现了简单路由功能的小巧框架,便于开发者使用和扩展.demo1中只是目录示例,开发者可自行定义自己的应用目录结构 14 | * 相比php-fpm或mod_php的方式性能有几十倍左右的提升 15 | * 自带简单的单例redis操作类和单例mysqli操作类(支持自动重连) 16 | * 可设置自动加载目录加载目录下的所有php文件(仅一级不支持递归) 17 | * 自定义404响应 18 | * 支持http协议1.1和1.0的长连接和短连接 19 | * 集成了workerman-statistics项目,可以监控服务情况 20 | * 支持中间件 21 | * 支持一次性订阅和发布 22 | 23 | 框架由来 24 | ======== 25 | 大家经常说php性能差,其实主要是在php-fpm或mod_php方式下的差,而php语言本身是不错的,尤其在未来加入JIT之后,性能会越来越好的。面对新兴的语言和开发方式,个人认为php应该抛弃php-fpm或mod_php的开发方式了,以主流的守护进程的方式来开发,这样的方式性能会比php-fpm或mod_php有几十倍左右的提升. 26 | 27 | 测试对比 28 | ======== 29 | https://github.com/xtgxiso/WebWorker-benchmark 30 | 31 | 项目示例 32 | ======== 33 | https://github.com/xtgxiso/WebWorker-example 34 | 35 | 36 | 安装 37 | ======== 38 | 39 | ``` 40 | composer require xtgxiso/webworker 41 | ``` 42 | 43 | 快速开始 44 | ====== 45 | demo.php 46 | ```php 47 | count = 1; 59 | 60 | $config = array(); 61 | $config["redis"]["host"] = "127.0.0.1"; 62 | $config["redis"]["port"] = 6379; 63 | $config["redis"]["password"] = "123456"; 64 | $config["redis"]["db"] = 1; 65 | $config["db"]["host"] = "127.0.0.1"; 66 | $config["db"]["user"] = "root"; 67 | $config["db"]["password"] = "123456"; 68 | $config["db"]["db"] = "test"; 69 | $config["db"]["port"] = 3306; 70 | $config["db"]["charset"] = "utf8"; 71 | $config["access"]["appid"] = "123456"; 72 | $config["access"]["appsecret"] = "abcdef"; 73 | 74 | 75 | $app->name = "demo"; 76 | 77 | //设置每个进程处理多少请求后重启(防止程序写的有问题导致内存泄露),默认为10000 78 | $app->max_request = 1000; 79 | 80 | //进程数 81 | $app->count = 4; 82 | 83 | //自动加载目录--会加载目录下的所有php文件 84 | $app->autoload = array(); 85 | 86 | //启动时执行的代码,这儿包含的文件支持reload 87 | $app->onAppStart = function($app) use($config){ 88 | WebWorker\autoload_dir($app->autoload); 89 | }; 90 | 91 | //应用级中间件--对/hello访问启用ip限制访问 92 | $app->AddFunc("/hello",function() { 93 | if ( $_SERVER['REMOTE_ADDR'] != '127.0.0.1' ) { 94 | $this->ServerHtml("禁止访问"); 95 | return true;//返回ture,中断执行后面的路由或中间件,直接返回给浏览器 96 | } 97 | }); 98 | 99 | //应用级中间件--对所有以api前缀开头的启用签名验证 100 | $app->AddFunc("/api",function() use($config) { 101 | $data = $_GET ? $_GET : $_POST; 102 | if ( !Maccess::verify_sign($data,$config["access"]) ){ 103 | $this->ServerHtml("禁止访问"); 104 | return true; 105 | } 106 | }); 107 | 108 | 109 | //注册路由api/test 110 | $app->HandleFunc("/api/test",function() { 111 | $this->ServerHtml("api test hello"); 112 | }); 113 | 114 | //注册路由hello 115 | $app->HandleFunc("/hello",function() { 116 | $this->ServerHtml("Hello World WorkerMan WebWorker!"); 117 | }); 118 | 119 | //注册路由json 120 | $app->HandleFunc("/json",function() { 121 | //以json格式响应 122 | $this->ServerJson(array("name"=>"WebWorker")); 123 | }); 124 | 125 | //注册路由/ 126 | $app->HandleFunc("/",function() { 127 | //自定义响应头 128 | $this->Header("server: xtgxiso"); 129 | //设置cookie 130 | $this->Setcookie("xtgxiso",time()); 131 | //以json格式响应 132 | $this->ServerJson(array("name"=>"WebWorker")); 133 | }); 134 | 135 | //注册路由input 136 | $app->HandleFunc("/input",function() { 137 | //获取body 138 | $body = $GLOBALS['HTTP_RAW_POST_DATA']; 139 | $this->ServerHtml($body); 140 | }); 141 | 142 | 143 | //redis示例 144 | $app->HandleFunc("/redis",function() use($app,$config){ 145 | $redis = Mredis::getInstance($config["redis"]); 146 | $app->ServerHtml($redis->get("xtgxiso")); 147 | }); 148 | 149 | //mysql示例 150 | $app->HandleFunc("/mysql",function() use($app,$config){ 151 | $this->db = Mdb::getInstance($config["db"]); 152 | $result = array(); 153 | $sql = "select * from test limit 1"; 154 | //取一行对象结果集 155 | $result['data1'] = $this->db->query($sql)->row(); 156 | //取一行数组结果集 157 | $result['data2'] = $this->db->query($sql)->row_array(); 158 | //取多行对象结果集 159 | $result['data3'] = $this->db->query($sql)->result(); 160 | //取多行数组结果集 161 | $result['data4'] = $this->db->query($sql)->result_array(); 162 | //取多行,自动转义参数 163 | $result['data5'] = $this->db->query("select * from test where id = ? or id =? ",array(1,2))->result_array(); 164 | //取表test中的一行数据 165 | $result['data6'] = $this->db->get("test",0,1)->row_array(); 166 | //取表中id=22的一行数据 167 | $result['data7'] = $this->db->get_where("test",array("id"=>22),0,1)->row_array(); 168 | //向表test插入数据 169 | $result['data8'] = $this->db->insert("test",array("name"=>time())); 170 | //更新表test中的id=1的数据 171 | $result['data9'] = $this->db->update("test",array("name"=>time()),array("id"=>1)); 172 | //删除表test中的id=2的数据 173 | $result['data10'] = $this->db->delete("test",array("id"=>2)); 174 | $this->ServerJson($result); 175 | 176 | }); 177 | 178 | //自定义404 179 | $app->on404 = function() { 180 | $this->ServerHtml("我的404"); 181 | }; 182 | 183 | //测试类 184 | class xtgxiso{ 185 | static function test() 186 | { 187 | global $app; 188 | $app->ServerHtml("xtgxiso"); 189 | } 190 | } 191 | //注册类 192 | $app->HandleFunc("/xtgxiso",array("xtgxiso","test")); 193 | 194 | //订阅,不考虑分布式,分布式的话连接不能存储在内存中 195 | $app->HandleFunc("/subscription",function() { 196 | $id = $_GET["id"]; 197 | $this->conn_list[$id] = $this->conn; 198 | $this->conn_close = false;//通过设置属性控制连接不关闭 199 | }); 200 | 201 | //发布,不考虑分布式,分布式的话连接不能存储在内存中 202 | $app->HandleFunc("/publish",function() { 203 | $id = $_GET["id"]; 204 | $conn = $this->conn_list[$id]; 205 | if ( $conn ){ 206 | $str = $_GET["content"]; 207 | $conn->send($str); 208 | } 209 | $this->ServerHtml(""); 210 | }); 211 | 212 | //访问日志 213 | Worker::$stdoutFile = './stdout.log'; 214 | 215 | // Run worker 216 | Worker::runAll(); 217 | ``` 218 | 219 | 220 | 技术交流QQ群 221 | ======== 222 | 517297682 223 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "xtgxiso/webworker", 3 | "type" : "library", 4 | "keywords": ["http", "asynchronous"], 5 | "homepage": "https://github.com/xtgxiso/WebWorker", 6 | "license" : "MIT", 7 | "description": "An http server PHP framework for easily building fast, scalable network applications.", 8 | "authors" : [ 9 | { 10 | "name" : "xtgxiso", 11 | "email" : "124960772@qq.com", 12 | "homepage" : "http://www.xtgxiso.com", 13 | "role": "Developer" 14 | } 15 | ], 16 | "support" : { 17 | "email" : "124960772@qq.com", 18 | "issues": "https://github.com/xtgxiso/WebWorker/issues", 19 | "wiki" : "http://doc.webworker.xtgxiso.com/", 20 | "source": "https://github.com/xtgxiso/WebWorker" 21 | }, 22 | "require": { 23 | "php": ">=7.0", 24 | "workerman/workerman" : "^3.3" 25 | }, 26 | "suggest": { 27 | "ext-event": "For better performance." 28 | }, 29 | "autoload": { 30 | "psr-4": {"WebWorker\\": "./"} 31 | }, 32 | "minimum-stability":"dev" 33 | } 34 | --------------------------------------------------------------------------------