├── .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 |
--------------------------------------------------------------------------------